From cb8f418eff923e8b37bd1a0baf4c838cc2f794e9 Mon Sep 17 00:00:00 2001 From: Liang Geng Date: Tue, 27 Jan 2026 13:55:17 -0500 Subject: [PATCH] Improve libgpuspaital --- Cargo.lock | 79 ++ c/sedona-libgpuspatial/Cargo.toml | 3 + c/sedona-libgpuspatial/build.rs | 18 + .../libgpuspatial/CMakeLists.txt | 12 +- .../libgpuspatial/CMakePresets.json | 2 +- .../cmake/thirdparty/get_geoarrow.cmake | 1 + .../cmake/thirdparty/get_nanoarrow.cmake | 4 + .../gpuspatial/geom/{box.cuh => box.hpp} | 28 +- ...collection.cuh => geometry_collection.hpp} | 18 +- .../{geometry_type.cuh => geometry_type.hpp} | 0 .../{line_segment.cuh => line_segment.hpp} | 8 +- .../geom/{line_string.cuh => line_string.hpp} | 6 +- ..._line_string.cuh => multi_line_string.hpp} | 6 +- .../geom/{multi_point.cuh => multi_point.hpp} | 6 +- .../{multi_polygon.cuh => multi_polygon.hpp} | 2 +- .../gpuspatial/geom/{point.cuh => point.hpp} | 25 +- .../geom/{polygon.cuh => polygon.hpp} | 10 +- ...g_counter.cuh => ray_crossing_counter.hpp} | 6 +- .../include/gpuspatial/gpuspatial_c.h | 178 +++- .../gpuspatial/index/geometry_grouper.hpp | 294 ------ .../include/gpuspatial/index/object_pool.hpp | 161 --- .../gpuspatial/index/rt_spatial_index.cuh | 119 +++ ...patial_joiner.hpp => rt_spatial_index.hpp} | 26 +- .../gpuspatial/index/spatial_index.hpp | 66 ++ .../gpuspatial/index/spatial_joiner.cuh | 184 ---- .../gpuspatial/index/streaming_joiner.hpp | 98 -- ...e_geometries.cuh => device_geometries.hpp} | 14 +- ...l_wkb_loader.h => parallel_wkb_loader.hpp} | 472 ++++++--- .../include/gpuspatial/mem/memory_manager.hpp | 84 ++ .../gpuspatial/refine/rt_spatial_refiner.cuh | 124 +++ .../gpuspatial/refine/rt_spatial_refiner.hpp | 52 + .../gpuspatial/refine/spatial_refiner.hpp | 44 + ...ion_matrix.cuh => intersection_matrix.hpp} | 0 .../relate/{predicate.cuh => predicate.hpp} | 0 .../relate/{relate.cuh => relate.hpp} | 85 +- .../{index => relate}/relate_engine.cuh | 87 +- .../launch_parameters.cuh} | 61 +- .../{index/detail => rt}/rt_engine.hpp | 7 +- .../utils/{array_view.h => array_view.hpp} | 2 +- .../utils/{cuda_utils.h => cuda_utils.hpp} | 2 +- .../{doubledouble.h => doubledouble.hpp} | 2 +- .../utils/{exception.h => exception.hpp} | 4 +- .../{floating_point.h => floating_point.hpp} | 2 +- .../include/gpuspatial/utils/gpu_timer.hpp | 2 +- .../utils/{helpers.h => helpers.cuh} | 2 +- .../utils/{launcher.h => launcher.hpp} | 4 +- .../include/gpuspatial/utils/markers.hpp | 145 +++ .../include/gpuspatial/utils/mem_utils.hpp | 2 +- .../utils/{morton_code.h => morton_code.hpp} | 2 +- .../{pinned_vector.h => pinned_vector.hpp} | 2 +- .../gpuspatial/utils/{queue.h => queue.hpp} | 5 +- .../utils/{queue_view.h => queue_view.hpp} | 4 +- .../utils/{stopwatch.h => stopwatch.hpp} | 0 .../utils/{thread_pool.h => thread_pool.hpp} | 0 .../utils/{type_traits.h => type_traits.hpp} | 0 .../libgpuspatial/src/gpuspatial_c.cc | 447 +++++--- .../libgpuspatial/src/memory_manager.cc | 128 +++ .../libgpuspatial/src/relate_engine.cu | 986 +++++++++--------- .../libgpuspatial/src/rt/rt_engine.cpp | 52 +- .../src/rt/shaders/box_query_backward.cu | 48 +- .../src/rt/shaders/box_query_forward.cu | 49 +- .../src/rt/shaders/config_shaders.cmake | 2 +- .../rt/shaders/multipolygon_point_query.cu | 71 +- .../src/rt/shaders/point_query.cu | 57 +- .../src/rt/shaders/polygon_point_query.cu | 76 +- .../shaders/{ray_params.h => ray_params.cuh} | 6 +- .../{shader_config.h => shader_config.hpp} | 0 .../libgpuspatial/src/rt_spatial_index.cu | 682 ++++++++++++ .../libgpuspatial/src/rt_spatial_refiner.cu | 548 ++++++++++ .../libgpuspatial/src/spatial_joiner.cu | 483 --------- .../libgpuspatial/test/CMakeLists.txt | 28 +- .../libgpuspatial/test/c_wrapper_test.cc | 275 ++++- .../libgpuspatial/test/data/cities/Makefile | 2 +- .../test/data/cities/generated_points.parquet | Bin 33179 -> 452407 bytes .../test/data/countries/Makefile | 2 +- .../data/countries/generated_points.parquet | Bin 33115 -> 452487 bytes .../libgpuspatial/test/data/gen_points.py | 2 +- .../libgpuspatial/test/index_test.cu | 300 ++++++ .../libgpuspatial/test/joiner_test.cu | 438 -------- .../libgpuspatial/test/loader_test.cu | 78 +- .../libgpuspatial/test/main.cc | 2 + .../libgpuspatial/test/refiner_test.cu | 738 +++++++++++++ .../libgpuspatial/test/related_test.cu | 55 +- .../libgpuspatial/test/test_common.hpp | 6 +- .../libgpuspatial/vcpkg.json | 1 + c/sedona-libgpuspatial/src/error.rs | 10 +- c/sedona-libgpuspatial/src/lib.rs | 658 +++++++++--- c/sedona-libgpuspatial/src/libgpuspatial.rs | 802 ++++++++------ 88 files changed, 6225 insertions(+), 3377 deletions(-) rename c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/geom/{box.cuh => box.hpp} (89%) rename c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/geom/{geometry_collection.cuh => geometry_collection.hpp} (95%) rename c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/geom/{geometry_type.cuh => geometry_type.hpp} (100%) rename c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/geom/{line_segment.cuh => line_segment.hpp} (96%) rename c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/geom/{line_string.cuh => line_string.hpp} (96%) rename c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/geom/{multi_line_string.cuh => multi_line_string.hpp} (96%) rename c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/geom/{multi_point.cuh => multi_point.hpp} (96%) rename c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/geom/{multi_polygon.cuh => multi_polygon.hpp} (99%) rename c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/geom/{point.cuh => point.hpp} (94%) rename c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/geom/{polygon.cuh => polygon.hpp} (98%) rename c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/geom/{ray_crossing_counter.cuh => ray_crossing_counter.hpp} (98%) delete mode 100644 c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/index/geometry_grouper.hpp delete mode 100644 c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/index/object_pool.hpp create mode 100644 c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/index/rt_spatial_index.cuh rename c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/index/{spatial_joiner.hpp => rt_spatial_index.hpp} (53%) create mode 100644 c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/index/spatial_index.hpp delete mode 100644 c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/index/spatial_joiner.cuh delete mode 100644 c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/index/streaming_joiner.hpp rename c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/loader/{device_geometries.cuh => device_geometries.hpp} (96%) rename c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/loader/{parallel_wkb_loader.h => parallel_wkb_loader.hpp} (72%) create mode 100644 c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/mem/memory_manager.hpp create mode 100644 c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/refine/rt_spatial_refiner.cuh create mode 100644 c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/refine/rt_spatial_refiner.hpp create mode 100644 c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/refine/spatial_refiner.hpp rename c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/relate/{intersection_matrix.cuh => intersection_matrix.hpp} (100%) rename c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/relate/{predicate.cuh => predicate.hpp} (100%) rename c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/relate/{relate.cuh => relate.hpp} (95%) rename c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/{index => relate}/relate_engine.cuh (66%) rename c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/{index/detail/launch_parameters.h => rt/launch_parameters.cuh} (67%) rename c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/{index/detail => rt}/rt_engine.hpp (98%) rename c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/{array_view.h => array_view.hpp} (98%) rename c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/{cuda_utils.h => cuda_utils.hpp} (97%) rename c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/{doubledouble.h => doubledouble.hpp} (99%) rename c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/{exception.h => exception.hpp} (95%) rename c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/{floating_point.h => floating_point.hpp} (99%) rename c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/{helpers.h => helpers.cuh} (98%) rename c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/{launcher.h => launcher.hpp} (94%) create mode 100644 c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/markers.hpp rename c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/{morton_code.h => morton_code.hpp} (98%) rename c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/{pinned_vector.h => pinned_vector.hpp} (99%) rename c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/{queue.h => queue.hpp} (95%) rename c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/{queue_view.h => queue_view.hpp} (96%) rename c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/{stopwatch.h => stopwatch.hpp} (100%) rename c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/{thread_pool.h => thread_pool.hpp} (100%) rename c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/{type_traits.h => type_traits.hpp} (100%) create mode 100644 c/sedona-libgpuspatial/libgpuspatial/src/memory_manager.cc rename c/sedona-libgpuspatial/libgpuspatial/src/rt/shaders/{ray_params.h => ray_params.cuh} (95%) rename c/sedona-libgpuspatial/libgpuspatial/src/rt/shaders/{shader_config.h => shader_config.hpp} (100%) create mode 100644 c/sedona-libgpuspatial/libgpuspatial/src/rt_spatial_index.cu create mode 100644 c/sedona-libgpuspatial/libgpuspatial/src/rt_spatial_refiner.cu delete mode 100644 c/sedona-libgpuspatial/libgpuspatial/src/spatial_joiner.cu create mode 100644 c/sedona-libgpuspatial/libgpuspatial/test/index_test.cu delete mode 100644 c/sedona-libgpuspatial/libgpuspatial/test/joiner_test.cu create mode 100644 c/sedona-libgpuspatial/libgpuspatial/test/refiner_test.cu diff --git a/Cargo.lock b/Cargo.lock index bc6799a91..2df3d4d32 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1533,6 +1533,16 @@ dependencies = [ "darling_macro 0.13.4", ] +[[package]] +name = "darling" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" +dependencies = [ + "darling_core 0.20.11", + "darling_macro 0.20.11", +] + [[package]] name = "darling" version = "0.23.0" @@ -1557,6 +1567,20 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "darling_core" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim 0.11.1", + "syn 2.0.114", +] + [[package]] name = "darling_core" version = "0.23.0" @@ -1581,6 +1605,17 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "darling_macro" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" +dependencies = [ + "darling_core 0.20.11", + "quote", + "syn 2.0.114", +] + [[package]] name = "darling_macro" version = "0.23.0" @@ -3874,6 +3909,29 @@ dependencies = [ "syn 2.0.114", ] +[[package]] +name = "nvml-wrapper" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c9bff0aa1d48904a1385ea2a8b97576fbdcbc9a3cfccd0d31fe978e1c4038c5" +dependencies = [ + "bitflags", + "libloading 0.8.9", + "nvml-wrapper-sys", + "static_assertions", + "thiserror 1.0.69", + "wrapcenum-derive", +] + +[[package]] +name = "nvml-wrapper-sys" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "698d45156f28781a4e79652b6ebe2eaa0589057d588d3aec1333f6466f13fcb5" +dependencies = [ + "libloading 0.8.9", +] + [[package]] name = "object" version = "0.32.2" @@ -5278,13 +5336,16 @@ dependencies = [ "arrow-schema", "bindgen", "cmake", + "geo", "log", + "nvml-wrapper", "sedona-expr", "sedona-geos", "sedona-schema", "sedona-testing", "thiserror 2.0.17", "which", + "wkt 0.14.0", ] [[package]] @@ -5759,6 +5820,12 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "strsim" version = "0.10.0" @@ -6742,6 +6809,18 @@ dependencies = [ "thiserror 1.0.69", ] +[[package]] +name = "wrapcenum-derive" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a76ff259533532054cfbaefb115c613203c73707017459206380f03b3b3f266e" +dependencies = [ + "darling 0.20.11", + "proc-macro2", + "quote", + "syn 2.0.114", +] + [[package]] name = "writeable" version = "0.6.2" diff --git a/c/sedona-libgpuspatial/Cargo.toml b/c/sedona-libgpuspatial/Cargo.toml index f271cd57a..efde2d986 100644 --- a/c/sedona-libgpuspatial/Cargo.toml +++ b/c/sedona-libgpuspatial/Cargo.toml @@ -40,8 +40,11 @@ which = "8.0" arrow-array = { workspace = true, features = ["ffi"] } arrow-schema = { workspace = true } thiserror = { workspace = true } +geo = { workspace = true } +wkt = { workspace = true } log = "0.4" sedona-schema = { path = "../../rust/sedona-schema" } +nvml-wrapper = "0.10.0" [dev-dependencies] sedona-expr = { path = "../../rust/sedona-expr" } diff --git a/c/sedona-libgpuspatial/build.rs b/c/sedona-libgpuspatial/build.rs index 6bf5f3f8b..db9f3a48f 100644 --- a/c/sedona-libgpuspatial/build.rs +++ b/c/sedona-libgpuspatial/build.rs @@ -119,6 +119,13 @@ fn main() { println!("cargo:warning=CMAKE_CUDA_ARCHITECTURES environment variable not set. Defaulting to '86;89'."); "86;89".to_string() }); + // Determine the build profile to match Cargo's debug/release mode + let profile_mode = if cfg!(debug_assertions) { + "Debug" + } else { + "Release" + }; + let dst = cmake::Config::new("./libgpuspatial") .define("CMAKE_CUDA_ARCHITECTURES", cuda_architectures) .define("CMAKE_POLICY_VERSION_MINIMUM", "3.5") // Allow older CMake versions @@ -157,6 +164,17 @@ fn main() { println!("cargo:rustc-link-lib=static=gpuspatial"); println!("cargo:rustc-link-lib=static=rmm"); println!("cargo:rustc-link-lib=static=rapids_logger"); + // Use the 'd' suffix for the debug build of spdlog (libspdlogd.a) + let spdlog_lib_name = if cfg!(debug_assertions) { + "spdlogd" + } else { + "spdlog" + }; + println!( + "cargo:warning=Linking spdlog in {} mode: lib{}.a", + profile_mode, spdlog_lib_name + ); + println!("cargo:rustc-link-lib=static={}", spdlog_lib_name); println!("cargo:rustc-link-lib=static=geoarrow"); println!("cargo:rustc-link-lib=static=nanoarrow"); println!("cargo:rustc-link-lib=stdc++"); diff --git a/c/sedona-libgpuspatial/libgpuspatial/CMakeLists.txt b/c/sedona-libgpuspatial/libgpuspatial/CMakeLists.txt index 773cf2061..eab272481 100644 --- a/c/sedona-libgpuspatial/libgpuspatial/CMakeLists.txt +++ b/c/sedona-libgpuspatial/libgpuspatial/CMakeLists.txt @@ -132,8 +132,13 @@ config_shaders(PTX_FILES) message("-- Config shader PTX files ${PTX_FILES}") -add_library(gpuspatial src/rt/rt_engine.cpp src/relate_engine.cu src/spatial_joiner.cu - ${PTX_FILES}) +add_library(gpuspatial + src/rt/rt_engine.cpp + src/memory_manager.cc + src/relate_engine.cu + src/rt_spatial_index.cu + src/rt_spatial_refiner.cu + ${PTX_FILES}) # Link libraries target_link_libraries(gpuspatial @@ -142,8 +147,7 @@ target_link_libraries(gpuspatial cuda rmm::rmm rapids_logger::rapids_logger - OptiX - PRIVATE zstd) + OptiX) # Set include directories target_include_directories(gpuspatial diff --git a/c/sedona-libgpuspatial/libgpuspatial/CMakePresets.json b/c/sedona-libgpuspatial/libgpuspatial/CMakePresets.json index 55248ea7f..0cb8a7fbb 100644 --- a/c/sedona-libgpuspatial/libgpuspatial/CMakePresets.json +++ b/c/sedona-libgpuspatial/libgpuspatial/CMakePresets.json @@ -31,7 +31,7 @@ "name": "default", "configurePreset": "default-with-tests", "environment": { - "GPUSPATIAL_TEST_DIR": "${sourceDir}/test_data" + "GPUSPATIAL_TEST_DIR": "${sourceDir}/test/data" } } ] diff --git a/c/sedona-libgpuspatial/libgpuspatial/cmake/thirdparty/get_geoarrow.cmake b/c/sedona-libgpuspatial/libgpuspatial/cmake/thirdparty/get_geoarrow.cmake index 1f4d53c22..a7314c151 100644 --- a/c/sedona-libgpuspatial/libgpuspatial/cmake/thirdparty/get_geoarrow.cmake +++ b/c/sedona-libgpuspatial/libgpuspatial/cmake/thirdparty/get_geoarrow.cmake @@ -47,6 +47,7 @@ function(find_and_configure_geoarrow) "BUILD_SHARED_LIBS OFF" ${_exclude_from_all}) set_target_properties(geoarrow PROPERTIES POSITION_INDEPENDENT_CODE ON) + target_compile_options(geoarrow PRIVATE -Wno-conversion) rapids_export_find_package_root(BUILD geoarrow "${geoarrow_BINARY_DIR}" diff --git a/c/sedona-libgpuspatial/libgpuspatial/cmake/thirdparty/get_nanoarrow.cmake b/c/sedona-libgpuspatial/libgpuspatial/cmake/thirdparty/get_nanoarrow.cmake index ecc3b4179..61932beb6 100644 --- a/c/sedona-libgpuspatial/libgpuspatial/cmake/thirdparty/get_nanoarrow.cmake +++ b/c/sedona-libgpuspatial/libgpuspatial/cmake/thirdparty/get_nanoarrow.cmake @@ -48,6 +48,10 @@ function(find_and_configure_nanoarrow) "NANOARROW_NAMESPACE gpuspatial" ${_exclude_from_all}) set_target_properties(nanoarrow PROPERTIES POSITION_INDEPENDENT_CODE ON) + if(TARGET nanoarrow_ipc) # Tests need this + target_compile_options(nanoarrow_ipc PRIVATE -Wno-conversion) + endif() + target_compile_options(nanoarrow PRIVATE -Wno-conversion) rapids_export_find_package_root(BUILD nanoarrow "${nanoarrow_BINARY_DIR}" diff --git a/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/geom/box.cuh b/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/geom/box.hpp similarity index 89% rename from c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/geom/box.cuh rename to c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/geom/box.hpp index 9fb33fa8e..971f3565d 100644 --- a/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/geom/box.cuh +++ b/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/geom/box.hpp @@ -16,9 +16,9 @@ // under the License. #pragma once -#include "gpuspatial/utils/array_view.h" -#include "gpuspatial/utils/cuda_utils.h" -#include "gpuspatial/utils/helpers.h" +#include "gpuspatial/utils/array_view.hpp" +#include "gpuspatial/utils/cuda_utils.hpp" +#include "gpuspatial/utils/helpers.cuh" #include @@ -86,22 +86,26 @@ class Box { } DEV_HOST_INLINE OptixAabb ToOptixAabb() const { - OptixAabb aabb; + OptixAabb aabb{0, 0, 0, 0, 0, 0}; - memset(&aabb, 0, sizeof(OptixAabb)); - if (sizeof(scalar_t) == sizeof(float)) { + if constexpr (sizeof(scalar_t) == sizeof(float)) { for (int dim = 0; dim < n_dim; dim++) { - reinterpret_cast(&aabb.minX)[dim] = min_.get_coordinate(dim); - reinterpret_cast(&aabb.maxX)[dim] = max_.get_coordinate(dim); + auto min_val = min_.get_coordinate(dim); + auto max_val = max_.get_coordinate(dim); + if (min_val == max_val) { + min_val = next_float_from_double(min_val, -1, 2); + max_val = next_float_from_double(max_val, 1, 2); + } + (&aabb.minX)[dim] = min_val; + (&aabb.maxX)[dim] = max_val; } } else { for (int dim = 0; dim < n_dim; dim++) { auto min_val = min_.get_coordinate(dim); auto max_val = max_.get_coordinate(dim); - reinterpret_cast(&aabb.minX)[dim] = - next_float_from_double(min_val, -1, 2); - reinterpret_cast(&aabb.maxX)[dim] = next_float_from_double(max_val, 1, 2); + (&aabb.minX)[dim] = next_float_from_double(min_val, -1, 2); + (&aabb.maxX)[dim] = next_float_from_double(max_val, 1, 2); } } return aabb; @@ -137,6 +141,8 @@ class Box { DEV_HOST_INLINE scalar_t get_min(int dim) const { return min_.get_coordinate(dim); } + DEV_HOST_INLINE bool valid() const { return !min_.empty() && !max_.empty(); } + DEV_HOST_INLINE const point_t& get_max() const { return max_; } DEV_HOST_INLINE scalar_t get_max(int dim) const { return max_.get_coordinate(dim); } diff --git a/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/geom/geometry_collection.cuh b/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/geom/geometry_collection.hpp similarity index 95% rename from c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/geom/geometry_collection.cuh rename to c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/geom/geometry_collection.hpp index 433317190..66c7dee45 100644 --- a/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/geom/geometry_collection.cuh +++ b/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/geom/geometry_collection.hpp @@ -15,15 +15,15 @@ // specific language governing permissions and limitations // under the License. #pragma once -#include "gpuspatial/geom/box.cuh" -#include "gpuspatial/geom/geometry_type.cuh" -#include "gpuspatial/geom/line_string.cuh" -#include "gpuspatial/geom/multi_line_string.cuh" -#include "gpuspatial/geom/multi_point.cuh" -#include "gpuspatial/geom/multi_polygon.cuh" -#include "gpuspatial/geom/point.cuh" -#include "gpuspatial/geom/polygon.cuh" -#include "gpuspatial/utils/array_view.h" +#include "gpuspatial/geom/box.hpp" +#include "gpuspatial/geom/geometry_type.hpp" +#include "gpuspatial/geom/line_string.hpp" +#include "gpuspatial/geom/multi_line_string.hpp" +#include "gpuspatial/geom/multi_point.hpp" +#include "gpuspatial/geom/multi_polygon.hpp" +#include "gpuspatial/geom/point.hpp" +#include "gpuspatial/geom/polygon.hpp" +#include "gpuspatial/utils/array_view.hpp" namespace gpuspatial { diff --git a/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/geom/geometry_type.cuh b/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/geom/geometry_type.hpp similarity index 100% rename from c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/geom/geometry_type.cuh rename to c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/geom/geometry_type.hpp diff --git a/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/geom/line_segment.cuh b/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/geom/line_segment.hpp similarity index 96% rename from c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/geom/line_segment.cuh rename to c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/geom/line_segment.hpp index 75f83f38e..a4eef0707 100644 --- a/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/geom/line_segment.cuh +++ b/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/geom/line_segment.hpp @@ -15,10 +15,10 @@ // specific language governing permissions and limitations // under the License. #pragma once -#include "gpuspatial/geom/box.cuh" -#include "gpuspatial/geom/point.cuh" -#include "gpuspatial/utils/cuda_utils.h" -#include "gpuspatial/utils/floating_point.h" +#include "gpuspatial/geom/box.hpp" +#include "gpuspatial/geom/point.hpp" +#include "gpuspatial/utils/cuda_utils.hpp" +#include "gpuspatial/utils/floating_point.hpp" namespace gpuspatial { template diff --git a/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/geom/line_string.cuh b/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/geom/line_string.hpp similarity index 96% rename from c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/geom/line_string.cuh rename to c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/geom/line_string.hpp index e0ddabe8e..00b57b0d9 100644 --- a/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/geom/line_string.cuh +++ b/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/geom/line_string.hpp @@ -15,9 +15,9 @@ // specific language governing permissions and limitations // under the License. #pragma once -#include "gpuspatial/geom/line_segment.cuh" -#include "gpuspatial/utils/array_view.h" -#include "gpuspatial/utils/cuda_utils.h" +#include "gpuspatial/geom/line_segment.hpp" +#include "gpuspatial/utils/array_view.hpp" +#include "gpuspatial/utils/cuda_utils.hpp" namespace gpuspatial { template diff --git a/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/geom/multi_line_string.cuh b/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/geom/multi_line_string.hpp similarity index 96% rename from c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/geom/multi_line_string.cuh rename to c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/geom/multi_line_string.hpp index b6aae39f8..c5d84f1b6 100644 --- a/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/geom/multi_line_string.cuh +++ b/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/geom/multi_line_string.hpp @@ -15,9 +15,9 @@ // specific language governing permissions and limitations // under the License. #pragma once -#include "gpuspatial/geom/line_string.cuh" -#include "gpuspatial/utils/array_view.h" -#include "gpuspatial/utils/cuda_utils.h" +#include "gpuspatial/geom/line_string.hpp" +#include "gpuspatial/utils/array_view.hpp" +#include "gpuspatial/utils/cuda_utils.hpp" namespace gpuspatial { template diff --git a/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/geom/multi_point.cuh b/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/geom/multi_point.hpp similarity index 96% rename from c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/geom/multi_point.cuh rename to c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/geom/multi_point.hpp index e01938e75..e6bc5a226 100644 --- a/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/geom/multi_point.cuh +++ b/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/geom/multi_point.hpp @@ -15,9 +15,9 @@ // specific language governing permissions and limitations // under the License. #pragma once -#include "gpuspatial/geom/box.cuh" -#include "gpuspatial/utils/array_view.h" -#include "gpuspatial/utils/cuda_utils.h" +#include "gpuspatial/geom/box.hpp" +#include "gpuspatial/utils/array_view.hpp" +#include "gpuspatial/utils/cuda_utils.hpp" namespace gpuspatial { diff --git a/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/geom/multi_polygon.cuh b/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/geom/multi_polygon.hpp similarity index 99% rename from c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/geom/multi_polygon.cuh rename to c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/geom/multi_polygon.hpp index b1a443aec..9179789c6 100644 --- a/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/geom/multi_polygon.cuh +++ b/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/geom/multi_polygon.hpp @@ -15,7 +15,7 @@ // specific language governing permissions and limitations // under the License. #pragma once -#include "gpuspatial/geom/polygon.cuh" +#include "gpuspatial/geom/polygon.hpp" namespace gpuspatial { template diff --git a/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/geom/point.cuh b/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/geom/point.hpp similarity index 94% rename from c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/geom/point.cuh rename to c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/geom/point.hpp index 500d9def5..006da8d4b 100644 --- a/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/geom/point.cuh +++ b/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/geom/point.hpp @@ -15,11 +15,11 @@ // specific language governing permissions and limitations // under the License. #pragma once -#include "gpuspatial/geom/box.cuh" -#include "gpuspatial/utils/array_view.h" -#include "gpuspatial/utils/cuda_utils.h" -#include "gpuspatial/utils/floating_point.h" -#include "gpuspatial/utils/type_traits.h" +#include "gpuspatial/geom/box.hpp" +#include "gpuspatial/utils/array_view.hpp" +#include "gpuspatial/utils/cuda_utils.hpp" +#include "gpuspatial/utils/floating_point.hpp" +#include "gpuspatial/utils/type_traits.hpp" namespace gpuspatial { enum class PointLocation { @@ -73,7 +73,14 @@ class Point { DEV_HOST_INLINE const scalar_t* get_data() const { return &data_.x; } - DEV_HOST_INLINE bool empty() const { return std::isnan(data_.x); } + DEV_HOST_INLINE bool empty() const { + for (int dim = 0; dim < n_dim; dim++) { + if (std::isnan(get_coordinate(dim))) { + return true; + } + } + return false; + } DEV_HOST_INLINE void set_empty() { for (int dim = 0; dim < n_dim; dim++) { @@ -102,11 +109,7 @@ class Point { * @brief Provides const access to the x-coordinate. * This method is only available if N_DIM >= 1. */ - DEV_HOST_INLINE const scalar_t& x() const { - if constexpr (N_DIM >= 1) { - return data_.x; - } - } + DEV_HOST_INLINE const scalar_t& x() const { return data_.x; } /** * @brief Provides access to the y-coordinate. diff --git a/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/geom/polygon.cuh b/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/geom/polygon.hpp similarity index 98% rename from c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/geom/polygon.cuh rename to c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/geom/polygon.hpp index 6ed66f168..e457a8fb2 100644 --- a/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/geom/polygon.cuh +++ b/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/geom/polygon.hpp @@ -16,11 +16,11 @@ // under the License. #pragma once -#include "gpuspatial/geom/box.cuh" -#include "gpuspatial/geom/line_string.cuh" -#include "gpuspatial/utils/array_view.h" -#include "gpuspatial/utils/cuda_utils.h" -#include "gpuspatial/utils/floating_point.h" +#include "gpuspatial/geom/box.hpp" +#include "gpuspatial/geom/line_string.hpp" +#include "gpuspatial/utils/array_view.hpp" +#include "gpuspatial/utils/cuda_utils.hpp" +#include "gpuspatial/utils/floating_point.hpp" #include #include diff --git a/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/geom/ray_crossing_counter.cuh b/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/geom/ray_crossing_counter.hpp similarity index 98% rename from c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/geom/ray_crossing_counter.cuh rename to c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/geom/ray_crossing_counter.hpp index 12963b845..b25a0ad9a 100644 --- a/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/geom/ray_crossing_counter.cuh +++ b/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/geom/ray_crossing_counter.hpp @@ -15,9 +15,9 @@ // specific language governing permissions and limitations // under the License. #pragma once -#include "gpuspatial/geom/point.cuh" -#include "gpuspatial/utils/cuda_utils.h" -#include "gpuspatial/utils/doubledouble.h" +#include "gpuspatial/geom/point.hpp" +#include "gpuspatial/utils/cuda_utils.hpp" +#include "gpuspatial/utils/doubledouble.hpp" namespace gpuspatial { diff --git a/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/gpuspatial_c.h b/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/gpuspatial_c.h index b31af58b0..994310fed 100644 --- a/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/gpuspatial_c.h +++ b/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/gpuspatial_c.h @@ -14,60 +14,162 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. +#include #include #ifdef __cplusplus extern "C" { #endif -struct GpuSpatialJoinerConfig { - uint32_t concurrency; +struct ArrowSchema; +struct ArrowArray; + +// Interfaces for ray-tracing engine (OptiX) +struct GpuSpatialRuntimeConfig { + /** Path to PTX files */ const char* ptx_root; + /** Device ID to use, 0 is the first GPU */ + int device_id; + /** Whether to use CUDA memory pool for allocations */ + bool use_cuda_memory_pool; + /** Ratio of initial memory pool size to total GPU memory, between 0 and 100 */ + int cuda_memory_pool_init_precent; }; -struct GpuSpatialJoinerContext { - const char* last_error; // Pointer to std::string to store last error message - void* private_data; // GPUSpatial context - void* build_indices; // Pointer to std::vector to store results - void* stream_indices; +/** Opaque runtime for GPU spatial operations + * Each process should only has one instance of GpuSpatialRuntimeexactly + * + */ +struct GpuSpatialRuntime { + /** Initialize the runtime (OptiX) with the given configuration + * @return 0 on success, non-zero on failure + */ + int (*init)(struct GpuSpatialRuntime* self, struct GpuSpatialRuntimeConfig* config); + void (*release)(struct GpuSpatialRuntime* self); + const char* (*get_last_error)(struct GpuSpatialRuntime* self); + void* private_data; }; -enum GpuSpatialPredicate { - GpuSpatialPredicateEquals = 0, - GpuSpatialPredicateDisjoint, - GpuSpatialPredicateTouches, - GpuSpatialPredicateContains, - GpuSpatialPredicateCovers, - GpuSpatialPredicateIntersects, - GpuSpatialPredicateWithin, - GpuSpatialPredicateCoveredBy +/** Create an instance of GpuSpatialRuntime */ +void GpuSpatialRuntimeCreate(struct GpuSpatialRuntime* runtime); + +struct GpuSpatialIndexConfig { + /** Pointer to an initialized GpuSpatialRuntime struct */ + struct GpuSpatialRuntime* runtime; + /** How many threads will concurrently call Probe method */ + uint32_t concurrency; +}; + +// An opaque context for concurrent probing +struct SedonaSpatialIndexContext { + void* private_data; +}; + +struct SedonaFloatIndex2D { + /** Clear the spatial index, removing all built data */ + int (*clear)(struct SedonaFloatIndex2D* self); + /** Create a new context for concurrent probing */ + void (*create_context)(struct SedonaSpatialIndexContext* context); + /** Destroy a previously created context */ + void (*destroy_context)(struct SedonaSpatialIndexContext* context); + /** Push rectangles for building the spatial index, each rectangle is represented by 4 + * floats: [min_x, min_y, max_x, max_y] Points can also be indexed by providing [x, y, + * x, y] but points and rectangles cannot be mixed + * + * @return 0 on success, non-zero on failure + */ + int (*push_build)(struct SedonaFloatIndex2D* self, const float* buf, uint32_t n_rects); + /** + * Finish building the spatial index after all rectangles have been pushed + * + * @return 0 on success, non-zero on failure + */ + int (*finish_building)(struct SedonaFloatIndex2D* self); + /** + * Probe the spatial index with the given rectangles, each rectangle is represented by 4 + * floats: [min_x, min_y, max_x, max_y] Points can also be probed by providing [x, y, x, + * y] but points and rectangles cannot be mixed in one Probe call. The results of the + * probe will be stored in the context. + * + * @return 0 on success, non-zero on failure + */ + int (*probe)(struct SedonaFloatIndex2D* self, struct SedonaSpatialIndexContext* context, + const float* buf, uint32_t n_rects); + /** Get the build indices buffer from the context + * + * @return A pointer to the buffer and its length + */ + void (*get_build_indices_buffer)(struct SedonaSpatialIndexContext* context, + uint32_t** build_indices, + uint32_t* build_indices_length); + /** Get the probe indices buffer from the context + * + * @return A pointer to the buffer and its length + */ + void (*get_probe_indices_buffer)(struct SedonaSpatialIndexContext* context, + uint32_t** probe_indices, + uint32_t* probe_indices_length); + const char* (*get_last_error)(struct SedonaFloatIndex2D* self); + const char* (*context_get_last_error)(struct SedonaSpatialIndexContext* context); + /** Release the spatial index and free all resources */ + void (*release)(struct SedonaFloatIndex2D* self); + void* private_data; }; -struct GpuSpatialJoiner { - int (*init)(struct GpuSpatialJoiner* self, struct GpuSpatialJoinerConfig* config); - void (*clear)(struct GpuSpatialJoiner* self); - void (*create_context)(struct GpuSpatialJoiner* self, - struct GpuSpatialJoinerContext* context); - void (*destroy_context)(struct GpuSpatialJoinerContext* context); - int (*push_build)(struct GpuSpatialJoiner* self, const struct ArrowSchema* schema, - const struct ArrowArray* array, int64_t offset, int64_t length); - int (*finish_building)(struct GpuSpatialJoiner* self); - int (*push_stream)(struct GpuSpatialJoiner* self, - struct GpuSpatialJoinerContext* context, - const struct ArrowSchema* schema, const struct ArrowArray* array, - int64_t offset, int64_t length, enum GpuSpatialPredicate predicate, - int32_t array_index_offset); - void (*get_build_indices_buffer)(struct GpuSpatialJoinerContext* context, - void** build_indices, uint32_t* build_indices_length); - void (*get_stream_indices_buffer)(struct GpuSpatialJoinerContext* context, - void** stream_indices, - uint32_t* stream_indices_length); - void (*release)(struct GpuSpatialJoiner* self); +int GpuSpatialIndexFloat2DCreate(struct SedonaFloatIndex2D* index, + const struct GpuSpatialIndexConfig* config); + +struct GpuSpatialRefinerConfig { + /** Pointer to an initialized GpuSpatialRuntime struct */ + struct GpuSpatialRuntime* runtime; + /** How many threads will concurrently call Probe method */ + uint32_t concurrency; + /** Whether to compress the BVH structures to save memory */ + bool compress_bvh; + /** Number of batches to pipeline for parsing and refinement; setting to 1 disables + * pipelining */ + uint32_t pipeline_batches; +}; + +enum SedonaSpatialRelationPredicate { + SedonaSpatialPredicateEquals = 0, + SedonaSpatialPredicateDisjoint, + SedonaSpatialPredicateTouches, + SedonaSpatialPredicateContains, + SedonaSpatialPredicateCovers, + SedonaSpatialPredicateIntersects, + SedonaSpatialPredicateWithin, + SedonaSpatialPredicateCoveredBy +}; + +struct SedonaSpatialRefiner { + int (*clear)(struct SedonaSpatialRefiner* self); + + int (*push_build)(struct SedonaSpatialRefiner* self, + const struct ArrowSchema* build_schema, + const struct ArrowArray* build_array); + + int (*finish_building)(struct SedonaSpatialRefiner* self); + + int (*refine_loaded)(struct SedonaSpatialRefiner* self, + const struct ArrowSchema* probe_schema, + const struct ArrowArray* probe_array, + enum SedonaSpatialRelationPredicate predicate, + uint32_t* build_indices, uint32_t* probe_indices, + uint32_t indices_size, uint32_t* new_indices_size); + + int (*refine)(struct SedonaSpatialRefiner* self, const struct ArrowSchema* schema1, + const struct ArrowArray* array1, const struct ArrowSchema* schema2, + const struct ArrowArray* array2, + enum SedonaSpatialRelationPredicate predicate, uint32_t* indices1, + uint32_t* indices2, uint32_t indices_size, uint32_t* new_indices_size); + const char* (*get_last_error)(struct SedonaSpatialRefiner* self); + void (*release)(struct SedonaSpatialRefiner* self); void* private_data; - const char* last_error; }; -void GpuSpatialJoinerCreate(struct GpuSpatialJoiner* index); +int GpuSpatialRefinerCreate(struct SedonaSpatialRefiner* refiner, + const struct GpuSpatialRefinerConfig* config); #ifdef __cplusplus } #endif diff --git a/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/index/geometry_grouper.hpp b/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/index/geometry_grouper.hpp deleted file mode 100644 index 5dab852d1..000000000 --- a/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/index/geometry_grouper.hpp +++ /dev/null @@ -1,294 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. -#pragma once -#include "gpuspatial/geom/box.cuh" -#include "gpuspatial/loader/device_geometries.cuh" -#include "gpuspatial/utils/launcher.h" -#include "gpuspatial/utils/morton_code.h" - -#include "rmm/cuda_stream_view.hpp" -#include "rmm/device_uvector.hpp" -#include "rmm/exec_policy.hpp" - -#include -#include -#include - -#include - -namespace gpuspatial { -template -class GeometryGrouper { - using box_t = Box; - static constexpr int n_dim = POINT_T::n_dim; - using scalar_t = typename POINT_T::scalar_t; - - public: - void Group(const rmm::cuda_stream_view& stream, - const DeviceGeometries& geometries, - uint32_t geoms_per_aabb) { - switch (geometries.get_geometry_type()) { - case GeometryType::kPoint: { - Group( - stream, - geometries.template GetGeometryArrayView>(), - geoms_per_aabb); - break; - } - case GeometryType::kMultiPoint: { - Group(stream, - geometries - .template GetGeometryArrayView>(), - geoms_per_aabb); - break; - } - case GeometryType::kLineString: { - Group(stream, - geometries - .template GetGeometryArrayView>(), - geoms_per_aabb); - break; - } - case GeometryType::kMultiLineString: { - Group(stream, - geometries.template GetGeometryArrayView< - MultiLineStringArrayView>(), - geoms_per_aabb); - break; - } - case GeometryType::kPolygon: { - Group(stream, - geometries - .template GetGeometryArrayView>(), - geoms_per_aabb); - break; - } - case GeometryType::kMultiPolygon: { - Group( - stream, - geometries - .template GetGeometryArrayView>(), - geoms_per_aabb); - break; - } - case GeometryType::kBox: { - Group(stream, - geometries.template GetGeometryArrayView>(), - geoms_per_aabb); - break; - } - default: - assert(false); - } - } - - template - void Group(const rmm::cuda_stream_view& stream, const GEOMETRY_ARRAY_T& geometries, - uint32_t geoms_per_aabb) { - rmm::device_uvector morton_codes(geometries.size(), stream); - POINT_T min_world_corner, max_world_corner; - - min_world_corner.set_max(); - max_world_corner.set_min(); - - for (int dim = 0; dim < n_dim; dim++) { - auto min_val = thrust::transform_reduce( - rmm::exec_policy_nosync(stream), thrust::make_counting_iterator(0), - thrust::make_counting_iterator(geometries.size()), - [=] __host__ __device__(INDEX_T i) { - const auto& geom = geometries[i]; - const auto& mbr = geom.get_mbr(); - - return mbr.get_min(dim); - }, - std::numeric_limits::max(), thrust::minimum()); - - auto max_val = thrust::transform_reduce( - rmm::exec_policy_nosync(stream), thrust::make_counting_iterator(0), - thrust::make_counting_iterator(geometries.size()), - [=] __host__ __device__(INDEX_T i) { - const auto& geom = geometries[i]; - const auto& mbr = geom.get_mbr(); - - return mbr.get_max(dim); - }, - std::numeric_limits::lowest(), thrust::maximum()); - min_world_corner.set_coordinate(dim, min_val); - max_world_corner.set_coordinate(dim, max_val); - } - - // compute morton codes and reorder indices - thrust::transform(rmm::exec_policy_nosync(stream), - thrust::make_counting_iterator(0), - thrust::make_counting_iterator(geometries.size()), - morton_codes.begin(), [=] __device__(INDEX_T i) { - const auto& geom = geometries[i]; - const auto& mbr = geom.get_mbr(); - auto p = mbr.centroid(); - POINT_T norm_p; - - for (int dim = 0; dim < n_dim; dim++) { - auto min_val = min_world_corner.get_coordinate(dim); - auto max_val = max_world_corner.get_coordinate(dim); - auto extent = min_val == max_val ? 1 : max_val - min_val; - auto norm_val = (p.get_coordinate(dim) - min_val) / extent; - norm_p.set_coordinate(dim, norm_val); - } - return detail::morton_code(norm_p.get_vec()); - }); - reordered_indices_ = - std::make_unique>(geometries.size(), stream); - thrust::sequence(rmm::exec_policy_nosync(stream), reordered_indices_->begin(), - reordered_indices_->end()); - thrust::sort_by_key(rmm::exec_policy_nosync(stream), morton_codes.begin(), - morton_codes.end(), reordered_indices_->begin()); - - auto n_aabbs = (geometries.size() + geoms_per_aabb - 1) / geoms_per_aabb; - aabbs_ = std::make_unique>(n_aabbs, stream); - OptixAabb empty_aabb; - - if (n_dim == 2) { - empty_aabb = OptixAabb{ - std::numeric_limits::max(), std::numeric_limits::max(), 0, - std::numeric_limits::lowest(), std::numeric_limits::lowest(), 0}; - } else if (n_dim == 3) { - empty_aabb = OptixAabb{ - std::numeric_limits::max(), std::numeric_limits::max(), - std::numeric_limits::max(), std::numeric_limits::lowest(), - std::numeric_limits::lowest(), std::numeric_limits::lowest()}; - } - - thrust::fill(rmm::exec_policy_nosync(stream), aabbs_->begin(), aabbs_->end(), - empty_aabb); - - auto* p_aabbs = aabbs_->data(); - - rmm::device_uvector n_geoms_per_aabb(n_aabbs, stream); - - auto* p_reordered_indices = reordered_indices_->data(); - auto* p_n_geoms_per_aabb = n_geoms_per_aabb.data(); - - // each warp takes an AABB and processes points_per_aabb points - LaunchKernel(stream, [=] __device__() mutable { - typedef cub::WarpReduce WarpReduce; - __shared__ typename WarpReduce::TempStorage temp_storage[MAX_BLOCK_SIZE / 32]; - auto warp_id = threadIdx.x / 32; - auto lane_id = threadIdx.x % 32; - auto global_warp_id = TID_1D / 32; - auto n_warps = TOTAL_THREADS_1D / 32; - - for (uint32_t aabb_id = global_warp_id; aabb_id < n_aabbs; aabb_id += n_warps) { - POINT_T min_corner, max_corner; - size_t idx_begin = aabb_id * geoms_per_aabb; - size_t idx_end = std::min((size_t)geometries.size(), idx_begin + geoms_per_aabb); - size_t idx_end_rup = (idx_end + 31) / 32; - idx_end_rup *= 32; // round up to the next multiple of 32 - - p_n_geoms_per_aabb[aabb_id] = idx_end - idx_begin; - - for (auto idx = idx_begin + lane_id; idx < idx_end_rup; idx += 32) { - Box> mbr; - - auto warp_begin = idx - lane_id; - auto warp_end = std::min(warp_begin + 32, idx_end); - auto n_valid = warp_end - warp_begin; - - if (idx < idx_end) { - auto geom_idx = p_reordered_indices[idx]; - mbr = geometries[geom_idx].get_mbr(); - } - - for (int dim = 0; dim < n_dim; dim++) { - auto min_val = - WarpReduce(temp_storage[warp_id]) - .Reduce(mbr.get_min(dim), thrust::minimum(), n_valid); - if (lane_id == 0) { - min_corner.set_coordinate(dim, min_val); - } - auto max_val = - WarpReduce(temp_storage[warp_id]) - .Reduce(mbr.get_max(dim), thrust::maximum(), n_valid); - if (lane_id == 0) { - max_corner.set_coordinate(dim, max_val); - } - } - } - - if (lane_id == 0) { - box_t ext_mbr(min_corner, max_corner); - p_aabbs[aabb_id] = ext_mbr.ToOptixAabb(); - } - } - }); - - prefix_sum_ = std::make_unique>(n_aabbs + 1, stream); - prefix_sum_->set_element_to_zero_async(0, stream); - thrust::inclusive_scan(rmm::exec_policy_nosync(stream), n_geoms_per_aabb.begin(), - n_geoms_per_aabb.end(), prefix_sum_->begin() + 1); -#ifndef NDEBUG - auto* p_prefix_sum = prefix_sum_->data(); - - thrust::for_each(rmm::exec_policy_nosync(stream), - thrust::counting_iterator(0), - thrust::counting_iterator(aabbs_->size()), - [=] __device__(size_t aabb_idx) { - auto begin = p_prefix_sum[aabb_idx]; - auto end = p_prefix_sum[aabb_idx + 1]; - const auto& aabb = p_aabbs[aabb_idx]; - - for (auto i = begin; i < end; i++) { - auto geom_idx = p_reordered_indices[i]; - auto mbr = geometries[geom_idx].get_mbr(); - assert(mbr.covered_by(aabb)); - } - }); -#endif - } - - ArrayView get_aabbs() const { - if (aabbs_ != nullptr) { - return ArrayView(aabbs_->data(), aabbs_->size()); - } - return {}; - } - - ArrayView get_prefix_sum() const { - if (prefix_sum_ != nullptr) { - return ArrayView(prefix_sum_->data(), prefix_sum_->size()); - } - return {}; - } - - ArrayView get_reordered_indices() const { - if (reordered_indices_ != nullptr) { - return ArrayView(reordered_indices_->data(), reordered_indices_->size()); - } - return {}; - } - - void Clear() { - aabbs_ = nullptr; - prefix_sum_ = nullptr; - reordered_indices_ = nullptr; - } - - private: - std::unique_ptr> aabbs_; - std::unique_ptr> prefix_sum_; - std::unique_ptr> reordered_indices_; -}; -} // namespace gpuspatial diff --git a/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/index/object_pool.hpp b/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/index/object_pool.hpp deleted file mode 100644 index d0ab3e1ff..000000000 --- a/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/index/object_pool.hpp +++ /dev/null @@ -1,161 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. -#pragma once - -#include -#include -#include - -namespace gpuspatial { -// Forward declaration of ObjectPool to be used in the custom deleter. -template -class ObjectPool; - -// A helper struct to allow std::make_shared to access the private constructor. -// It inherits from ObjectPool and is defined outside of it. -template -struct PoolEnabler : public ObjectPool { - PoolEnabler(size_t size) : ObjectPool(size) {} -}; - -// A custom deleter for std::shared_ptr. -// When the shared_ptr's reference count goes to zero, this deleter -// will be invoked, returning the object to the pool instead of deleting it. -template -class PoolDeleter { - public: - // Constructor takes a weak_ptr to the pool to avoid circular references. - PoolDeleter(std::weak_ptr> pool) : pool_(pool) {} - - // The function call operator is what std::shared_ptr invokes. - void operator()(T* ptr) const { - // Attempt to lock the weak_ptr to get a shared_ptr to the pool. - if (auto pool_sp = pool_.lock()) { - // If the pool still exists, return the object to it. - pool_sp->release(ptr); - } else { - // If the pool no longer exists, we must delete the pointer to avoid a memory leak. - delete ptr; - } - } - - private: - std::weak_ptr> pool_; -}; - -/** - * @brief A thread-safe object pool for reusable objects. - * - * @tparam T The type of object to pool. - */ -template -class ObjectPool : public std::enable_shared_from_this> { - friend struct PoolEnabler; - - // Constructor is private to force object creation through the static 'create' method. - // This ensures the ObjectPool is always managed by a std::shared_ptr. - ObjectPool(size_t initial_size = 0) { - for (size_t i = 0; i < initial_size; ++i) { - pool_.push_back(new T()); - } - } - - public: - /** - * @brief Factory method to create an instance of the ObjectPool. - * Guarantees that the pool is managed by a std::shared_ptr, which is required - * for the custom deleter mechanism to work correctly. - * - * @param initial_size The number of objects to pre-allocate. - * @return A std::shared_ptr to the new ObjectPool instance. - */ - static std::shared_ptr> create(size_t initial_size = 0) { - return std::make_shared>(initial_size); - } - - /** - * @brief Destructor. Cleans up any remaining objects in the pool. - */ - ~ObjectPool() { - std::lock_guard lock(mutex_); - for (T* item : pool_) { - delete item; - } - pool_.clear(); - } - - // Disable copy constructor and assignment operator - ObjectPool(const ObjectPool&) = delete; - ObjectPool& operator=(const ObjectPool&) = delete; - - /** - * @brief Acquires an object from the pool. - * - * If the pool is empty, a new object is created. The returned shared_ptr - * has a custom deleter that will return the object to the pool when it's - * no longer referenced. - * - * @return A std::shared_ptr to an object of type T. - */ - std::shared_ptr take() { - std::lock_guard lock(mutex_); - T* resource_ptr = nullptr; - if (!pool_.empty()) { - // Take an existing object from the pool - resource_ptr = pool_.back(); - pool_.pop_back(); - } else { - // Pool is empty, create a new object - resource_ptr = new T(); - } - - // Create a custom deleter that knows how to return the object to this pool. - // this->shared_from_this() is now safe because creation is forced through the - // 'create' method. - PoolDeleter deleter(this->shared_from_this()); - - // Return a shared_ptr with the custom deleter. - return std::shared_ptr(resource_ptr, deleter); - } - - /** - * @brief Returns an object to the pool. - * - * This method is intended to be called by the PoolDeleter, not directly by clients. - * - * @param object The raw pointer to the object to return to the pool. - */ - void release(T* object) { - std::lock_guard lock(mutex_); - pool_.push_back(object); - } - - /** - * @brief Gets the current number of available objects in the pool. - * @return The size of the pool. - */ - size_t size() { - std::lock_guard lock(mutex_); - return pool_.size(); - } - - private: - std::vector pool_; - std::mutex mutex_; -}; - -} // namespace gpuspatial diff --git a/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/index/rt_spatial_index.cuh b/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/index/rt_spatial_index.cuh new file mode 100644 index 000000000..baaeb77f6 --- /dev/null +++ b/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/index/rt_spatial_index.cuh @@ -0,0 +1,119 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +#pragma once + +#include "gpuspatial/index/rt_spatial_index.hpp" +#include "gpuspatial/index/spatial_index.hpp" +#include "gpuspatial/rt/rt_engine.hpp" +#include "gpuspatial/utils/gpu_timer.hpp" +#include "gpuspatial/utils/queue.hpp" + +#include "rmm/cuda_stream_pool.hpp" +#include "rmm/cuda_stream_view.hpp" +#include "rmm/device_uvector.hpp" +#define GPUSPATIAL_PROFILING +namespace gpuspatial { + +/** * @brief A spatial index implementation using NVIDIA OptiX ray tracing engine. + * + * This class provides spatial indexing capabilities for geometric data using + * the OptiX ray tracing engine. It supports building the index from either + * points or bounding boxes and allows for efficient spatial queries. + * + * @tparam SCALAR_T The scalar type used for coordinates (e.g., float, double). + * @tparam N_DIM The number of dimensions (e.g., 2 for 2D, 3 for 3D). + */ +template +class RTSpatialIndex : public SpatialIndex { + using point_t = typename SpatialIndex::point_t; + using box_t = typename SpatialIndex::box_t; + using scalar_t = typename point_t::scalar_t; + static constexpr int n_dim = point_t::n_dim; + + using index_t = uint32_t; // type of the index to represent geometries + struct SpatialIndexContext { + rmm::cuda_stream_view stream; + std::string shader_id; + rmm::device_buffer bvh_buffer{0, rmm::cuda_stream_default}; + OptixTraversableHandle handle; + std::vector h_launch_params_buffer; + rmm::device_buffer launch_params_buffer{0, rmm::cuda_stream_default}; + std::unique_ptr> counter; + // output + Queue build_indices; + rmm::device_uvector probe_indices{0, rmm::cuda_stream_default}; +#ifdef GPUSPATIAL_PROFILING + GPUTimer timer; + // counters + double alloc_ms = 0.0; + double bvh_build_ms = 0.0; + double rt_ms = 0.0; + double copy_res_ms = 0.0; +#endif + }; + + public: + RTSpatialIndex() = default; + + RTSpatialIndex(const RTSpatialIndexConfig& config); + + void Clear() override; + + void PushBuild(const box_t* rects, uint32_t n_rects) override; + + void FinishBuilding() override; + + void Probe(const box_t* rects, uint32_t n_rects, std::vector* build_indices, + std::vector* probe_indices) override; + + private: + RTSpatialIndexConfig config_; + std::unique_ptr stream_pool_; + bool indexing_points_; + // The rectangles being indexed or the MBRs of grouped points + rmm::device_uvector rects_{0, rmm::cuda_stream_default}; + // Data structures for indexing points + rmm::device_uvector point_ranges_{0, rmm::cuda_stream_default}; + rmm::device_uvector reordered_point_indices_{0, rmm::cuda_stream_default}; + rmm::device_uvector points_{0, rmm::cuda_stream_default}; + rmm::device_buffer bvh_buffer_{0, rmm::cuda_stream_default}; + OptixTraversableHandle handle_; + + void allocateResultBuffer(SpatialIndexContext& ctx, uint32_t capacity) const; + + void handleBuildPoint(SpatialIndexContext& ctx, ArrayView points, + bool counting) const; + + void handleBuildPoint(SpatialIndexContext& ctx, ArrayView rects, + bool counting) const; + + void handleBuildBox(SpatialIndexContext& ctx, ArrayView points, + bool counting) const; + + void handleBuildBox(SpatialIndexContext& ctx, ArrayView rects, + bool counting) const; + + void prepareLaunchParamsBoxQuery(SpatialIndexContext& ctx, ArrayView probe_rects, + bool forward, bool counting) const; + + void filter(SpatialIndexContext& ctx, uint32_t dim_x) const; + + size_t numGeometries() const { + return indexing_points_ ? points_.size() : rects_.size(); + } +}; +} // namespace gpuspatial diff --git a/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/index/spatial_joiner.hpp b/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/index/rt_spatial_index.hpp similarity index 53% rename from c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/index/spatial_joiner.hpp rename to c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/index/rt_spatial_index.hpp index 6c836dfa9..b34475edd 100644 --- a/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/index/spatial_joiner.hpp +++ b/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/index/rt_spatial_index.hpp @@ -16,13 +16,31 @@ // under the License. #pragma once -#include "gpuspatial/index/streaming_joiner.hpp" +#include "gpuspatial/index/spatial_index.hpp" +#include "gpuspatial/rt/rt_engine.hpp" #include +#include namespace gpuspatial { -std::unique_ptr CreateSpatialJoiner(); -void InitSpatialJoiner(StreamingJoiner* index, const char* ptx_root, - uint32_t concurrency); +struct RTSpatialIndexConfig { + std::shared_ptr rt_engine; + // Prefer fast build the BVH + bool prefer_fast_build = false; + // Compress the BVH to save memory + bool compact = true; + // How many threads are allowed to call PushProbe concurrently + uint32_t concurrency = 1; + // number of points to represent an AABB when doing point-point queries + uint32_t n_points_per_aabb = 8; + RTSpatialIndexConfig() : prefer_fast_build(false), compact(false) { + concurrency = std::thread::hardware_concurrency(); + } +}; + +template +std::unique_ptr> CreateRTSpatialIndex( + const RTSpatialIndexConfig& config); + } // namespace gpuspatial diff --git a/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/index/spatial_index.hpp b/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/index/spatial_index.hpp new file mode 100644 index 000000000..688d0a9b6 --- /dev/null +++ b/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/index/spatial_index.hpp @@ -0,0 +1,66 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +#pragma once +#include "gpuspatial/geom/box.hpp" +#include "gpuspatial/geom/point.hpp" + +#include +#include +#include + +namespace gpuspatial { +template +class SpatialIndex { + public: + using point_t = Point; + using box_t = Box; + + virtual ~SpatialIndex() = default; + + /** + * Provide an array of geometries to build the index. + * @param rects An array of rectangles to be indexed. + */ + virtual void PushBuild(const box_t* rects, uint32_t n_rects) = 0; + + /** + * Waiting the index to be built. + * This method should be called after all geometries have been pushed. + */ + virtual void FinishBuilding() = 0; + + /** + * Remove all geometries from the index, so the index can reused. + */ + virtual void Clear() = 0; + + /** + * Query the index with an array of rectangles and return the indices of + * the rectangles. This method is thread-safe. + * @param build_indices A vector to store the indices of the geometries in the index + * that have a spatial overlap with the geometries in the stream. + * @param stream_indices A vector to store the indices of the geometries in the stream + * that have a spatial overlap with the geometries in the index. + */ + virtual void Probe(const box_t* rects, uint32_t n_rects, + std::vector* build_indices, + std::vector* stream_indices) { + throw std::runtime_error("Not implemented"); + } +}; + +} // namespace gpuspatial diff --git a/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/index/spatial_joiner.cuh b/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/index/spatial_joiner.cuh deleted file mode 100644 index 1c93a54b2..000000000 --- a/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/index/spatial_joiner.cuh +++ /dev/null @@ -1,184 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. -#pragma once -#include "geoarrow/geoarrow_type.h" -#include "gpuspatial/geom/box.cuh" -#include "gpuspatial/geom/point.cuh" -#include "gpuspatial/index/detail/rt_engine.hpp" -#include "gpuspatial/index/geometry_grouper.hpp" -#include "gpuspatial/index/object_pool.hpp" -#include "gpuspatial/index/relate_engine.cuh" -#include "gpuspatial/index/streaming_joiner.hpp" -#include "gpuspatial/loader/device_geometries.cuh" -#include "gpuspatial/loader/parallel_wkb_loader.h" -#include "gpuspatial/utils/gpu_timer.hpp" -#include "gpuspatial/utils/queue.h" -#include "gpuspatial/utils/thread_pool.h" - -#include "rmm/cuda_stream_pool.hpp" -#include "rmm/cuda_stream_view.hpp" -#include "rmm/device_uvector.hpp" - -#include -#include - - -// #define GPUSPATIAL_PROFILING -namespace gpuspatial { - -class SpatialJoiner : public StreamingJoiner { - // TODO: Assuming every thing is 2D in double for now - using scalar_t = double; - static constexpr int n_dim = 2; - using index_t = uint32_t; // type of the index to represent geometries - // geometry types - using point_t = Point; - using multi_point_t = MultiPoint; - using line_string_t = LineString; - using multi_line_string_t = MultiLineString; - using polygon_t = Polygon; - using multi_polygon_t = MultiPolygon; - // geometry array types - using point_array_t = PointArrayView; - using multi_point_array_t = MultiPointArrayView; - using line_string_array_t = LineStringArrayView; - using multi_line_string_array_t = MultiLineStringArrayView; - using polygon_array_t = PolygonArrayView; - using multi_polygon_array_t = MultiPolygonArrayView; - - using dev_geometries_t = DeviceGeometries; - using box_t = Box>; - using loader_t = ParallelWkbLoader; - - public: - struct SpatialJoinerConfig : Config { - const char* ptx_root; - // Prefer fast build the BVH - bool prefer_fast_build = false; - // Compress the BVH to save memory - bool compact = true; - // Loader configurations - // How many threads to use for parsing WKBs - uint32_t parsing_threads = std::thread::hardware_concurrency(); - // How many threads are allowed to call PushStream concurrently - uint32_t concurrency = 1; - // number of points to represent an AABB when doing point-point queries - uint32_t n_points_per_aabb = 8; - // reserve a ratio of available memory for result sets - float result_buffer_memory_reserve_ratio = 0.2; - // the memory quota for relate engine compared to the available memory - float relate_engine_memory_quota = 0.8; - // this value determines RELATE_MAX_DEPTH - size_t stack_size_bytes = 3 * 1024; - SpatialJoinerConfig() : ptx_root(nullptr), prefer_fast_build(false), compact(false) { - concurrency = std::thread::hardware_concurrency(); - } - }; - - struct SpatialJoinerContext : Context { - rmm::cuda_stream_view cuda_stream; - std::string shader_id; - std::unique_ptr stream_loader; - dev_geometries_t stream_geometries; - std::unique_ptr bvh_buffer; - OptixTraversableHandle handle; - std::vector h_launch_params_buffer; - std::unique_ptr launch_params_buffer; - // output - Queue> results; - int32_t array_index_offset; -#ifdef GPUSPATIAL_PROFILING - GPUTimer timer; - // counters - double parse_ms = 0.0; - double alloc_ms = 0.0; - double filter_ms = 0.0; - double refine_ms = 0.0; - double copy_res_ms = 0.0; -#endif - }; - - SpatialJoiner() = default; - - ~SpatialJoiner() = default; - - void Init(const Config* config) override; - - void Clear() override; - - void PushBuild(const ArrowSchema* schema, const ArrowArray* array, int64_t offset, - int64_t length) override; - - void FinishBuilding() override; - - std::shared_ptr CreateContext() override { return ctx_pool_->take(); } - - void PushStream(Context* ctx, const ArrowSchema* schema, const ArrowArray* array, - int64_t offset, int64_t length, Predicate predicate, - std::vector* build_indices, - std::vector* stream_indices, - int32_t array_index_offset) override; - - // Internal method but has to be public for the CUDA kernel to access - void handleBuildPointStreamPoint(SpatialJoinerContext* ctx, Predicate predicate, - std::vector* build_indices, - std::vector* stream_indices); - - void handleBuildBoxStreamPoint(SpatialJoinerContext* ctx, Predicate predicate, - std::vector* build_indices, - std::vector* stream_indices); - - void handleBuildPointStreamBox(SpatialJoinerContext* ctx, Predicate predicate, - std::vector* build_indices, - std::vector* stream_indices); - - void handleBuildBoxStreamBox(SpatialJoinerContext* ctx, Predicate predicate, - std::vector* build_indices, - std::vector* stream_indices); - - void filter(SpatialJoinerContext* ctx, uint32_t dim_x, bool swap_id = false); - - void refine(SpatialJoinerContext* ctx, Predicate predicate, - std::vector* build_indices, - std::vector* stream_indices); - - private: - SpatialJoinerConfig config_; - std::unique_ptr stream_pool_; - std::shared_ptr thread_pool_; - details::RTEngine rt_engine_; - std::unique_ptr bvh_buffer_; - std::unique_ptr build_loader_; - - DeviceGeometries build_geometries_; - // For grouping points with space-filing curve - GeometryGrouper geometry_grouper_; - RelateEngine relate_engine_; - OptixTraversableHandle handle_; - - std::shared_ptr> ctx_pool_; - - OptixTraversableHandle buildBVH(const rmm::cuda_stream_view& stream, - const ArrayView& aabbs, - std::unique_ptr& buffer); - - void allocateResultBuffer(SpatialJoinerContext* ctx); - - void prepareLaunchParamsBoxQuery(SpatialJoinerContext* ctx, bool forward); -}; - -} // namespace gpuspatial diff --git a/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/index/streaming_joiner.hpp b/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/index/streaming_joiner.hpp deleted file mode 100644 index ccf8a3bfe..000000000 --- a/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/index/streaming_joiner.hpp +++ /dev/null @@ -1,98 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. -#pragma once -#include "gpuspatial/relate/predicate.cuh" - -#include "nanoarrow/nanoarrow.hpp" - -#include -#include -#include -namespace gpuspatial { - -class StreamingJoiner { - public: - struct Context { - virtual ~Context() = default; - }; - - struct Config { - virtual ~Config() = default; - }; - - virtual ~StreamingJoiner() = default; - - /** - * Initialize the index with the given configuration. This method should be called only - * once before using the index. - * @param config - */ - virtual void Init(const Config* config) = 0; - - /** - * Provide an array of geometries to build the index. - * @param array ArrowArray that contains the geometries in WKB format. - * @param offset starting index of the ArrowArray - * @param length length of the ArrowArray to read. - */ - virtual void PushBuild(const ArrowSchema* schema, const ArrowArray* array, - int64_t offset, int64_t length) = 0; - - /** - * Waiting the index to be built. - * This method should be called after all geometries have been pushed. - */ - virtual void FinishBuilding() = 0; - - /** - * Remove all geometries from the index, so the index can reused. - */ - virtual void Clear() = 0; - - /** - * Query the index with an array of geometries in WKB format and return the indices of - * the geometries in stream and the index that satisfy a given predicate. This method is - * thread-safe. - * @param context A context object that can be used to store intermediate results. - * @param array ArrowArray that contains the geometries in WKB format. - * @param offset starting index of the ArrowArray - * @param length length of the ArrowArray to read. - * @param predicate A predicate to filter the query results. - * @param build_indices A vector to store the indices of the geometries in the index - * that have a spatial overlap with the geometries in the stream. - * @param stream_indices A vector to store the indices of the geometries in the stream - * that have a spatial overlap with the geometries in the index. - * @param stream_index_offset An offset to be added to stream_indices - */ - virtual void PushStream(Context* context, const ArrowSchema* schema, - const ArrowArray* array, int64_t offset, int64_t length, - Predicate predicate, std::vector* build_indices, - std::vector* stream_indices, - int32_t stream_index_offset) { - throw std::runtime_error("Not implemented"); - } - - /** - * Create a context object for issuing queries against the index. - * @return A context object that is used to store intermediate results. - */ - virtual std::shared_ptr CreateContext() { - throw std::runtime_error("Not implemented"); - } -}; - -} // namespace gpuspatial diff --git a/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/loader/device_geometries.cuh b/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/loader/device_geometries.hpp similarity index 96% rename from c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/loader/device_geometries.cuh rename to c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/loader/device_geometries.hpp index 3c44ca324..2d59d0a89 100644 --- a/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/loader/device_geometries.cuh +++ b/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/loader/device_geometries.hpp @@ -15,13 +15,13 @@ // specific language governing permissions and limitations // under the License. #pragma once -#include "gpuspatial/geom/box.cuh" -#include "gpuspatial/geom/geometry_type.cuh" -#include "gpuspatial/geom/multi_line_string.cuh" -#include "gpuspatial/geom/multi_point.cuh" -#include "gpuspatial/geom/multi_polygon.cuh" -#include "gpuspatial/geom/polygon.cuh" -#include "gpuspatial/utils/array_view.h" +#include "gpuspatial/geom/box.hpp" +#include "gpuspatial/geom/geometry_type.hpp" +#include "gpuspatial/geom/multi_line_string.hpp" +#include "gpuspatial/geom/multi_point.hpp" +#include "gpuspatial/geom/multi_polygon.hpp" +#include "gpuspatial/geom/polygon.hpp" +#include "gpuspatial/utils/array_view.hpp" #include "rmm/device_uvector.hpp" diff --git a/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/loader/parallel_wkb_loader.h b/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/loader/parallel_wkb_loader.hpp similarity index 72% rename from c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/loader/parallel_wkb_loader.h rename to c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/loader/parallel_wkb_loader.hpp index cb2186ff3..ff9962bcf 100644 --- a/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/loader/parallel_wkb_loader.h +++ b/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/loader/parallel_wkb_loader.hpp @@ -15,78 +15,41 @@ // specific language governing permissions and limitations // under the License. #pragma once - -#include "gpuspatial/geom/geometry_type.cuh" -#include "gpuspatial/loader/device_geometries.cuh" +#include "gpuspatial/geom/geometry_type.hpp" +#include "gpuspatial/loader/device_geometries.hpp" +#include "gpuspatial/mem/memory_manager.hpp" #include "gpuspatial/utils/logger.hpp" +#include "gpuspatial/utils/markers.hpp" #include "gpuspatial/utils/mem_utils.hpp" -#include "gpuspatial/utils/stopwatch.h" -#include "gpuspatial/utils/thread_pool.h" +#include "gpuspatial/utils/stopwatch.hpp" +#include "gpuspatial/utils/thread_pool.hpp" + +#include "nanoarrow/nanoarrow.hpp" -#include "nanoarrow/nanoarrow.h" +#include "geoarrow/geoarrow.hpp" #include "rmm/cuda_stream_view.hpp" #include "rmm/device_uvector.hpp" #include "rmm/exec_policy.hpp" +#include #include +#include + +#include +#include +#include #include #include - -#include -#include +#include namespace gpuspatial { namespace detail { -inline long long get_free_physical_memory_linux() { - struct sysinfo info; - if (sysinfo(&info) == 0) { - // info.freeram is in bytes (or unit defined by info.mem_unit) - // Use info.freeram * info.mem_unit for total free bytes - return (long long)info.freeram * (long long)info.mem_unit; - } - return 0; // Error -} - -// Copied from GeoArrow, it is faster than using GeoArrowWKBReaderRead -struct WKBReaderPrivate { - const uint8_t* data; - int64_t size_bytes; - const uint8_t* data0; - int need_swapping; - GeoArrowGeometry geom; -}; - -static int WKBReaderReadEndian(struct WKBReaderPrivate* s, struct GeoArrowError* error) { - if (s->size_bytes > 0) { - s->need_swapping = s->data[0] != GEOARROW_NATIVE_ENDIAN; - s->data++; - s->size_bytes--; - return GEOARROW_OK; - } else { - GeoArrowErrorSet(error, "Expected endian byte but found end of buffer at byte %ld", - (long)(s->data - s->data0)); - return EINVAL; - } -} - -static int WKBReaderReadUInt32(struct WKBReaderPrivate* s, uint32_t* out, - struct GeoArrowError* error) { - if (s->size_bytes >= 4) { - memcpy(out, s->data, sizeof(uint32_t)); - s->data += sizeof(uint32_t); - s->size_bytes -= sizeof(uint32_t); - if (s->need_swapping) { - *out = __builtin_bswap32(*out); - } - return GEOARROW_OK; - } else { - GeoArrowErrorSet(error, "Expected uint32 but found end of buffer at byte %ld", - (long)(s->data - s->data0)); - return EINVAL; - } +inline bool is_little_endian() { + const uint16_t x = 0x0001; + return *reinterpret_cast(&x) != 0; } /** @@ -105,6 +68,7 @@ template struct HostParsedGeometries { constexpr static int n_dim = POINT_T::n_dim; using mbr_t = Box>; + GeometryType type; // A general type that can reprs // each feature should have only one type except GeometryCollection std::vector feature_types; // This number should be one except GeometryCollection, which should be unnested # of @@ -120,17 +84,18 @@ struct HostParsedGeometries { bool has_geometry_collection = false; bool create_mbr = false; - HostParsedGeometries(bool multi_, bool has_geometry_collection_, bool create_mbr_) { + HostParsedGeometries(GeometryType t) : type(t) { + multi = type == GeometryType::kMultiPoint || type == GeometryType::kMultiLineString || + type == GeometryType::kMultiPolygon; + has_geometry_collection = type == GeometryType::kGeometryCollection; + create_mbr = type != GeometryType::kPoint; // Multi and GeometryCollection are mutually exclusive - assert(!(multi_ && has_geometry_collection_)); - multi = multi_; - has_geometry_collection = has_geometry_collection_; - create_mbr = create_mbr_; + assert(!(multi && has_geometry_collection)); } void AddGeometry(const GeoArrowGeometryView* geom) { if (geom == nullptr) { - throw std::runtime_error("Null geometry not supported yet"); + addNullEntry(); return; } @@ -405,6 +370,49 @@ struct HostParsedGeometries { } return node + 1; } + + void addNullEntry() { + // 1. Maintain MBR alignment if this type has MBRs + if (create_mbr) { + mbr_t empty_mbr; + empty_mbr.set_empty(); + mbrs.push_back(empty_mbr); + } + + // 2. Push zero-placeholders to maintain offset alignment + if (has_geometry_collection) { + // Null collection => 0 sub-geometries + num_geoms.push_back(0); + } else { + switch (type) { + case GeometryType::kPoint: { + // Push NaN point to represent empty/null + POINT_T p; + p.set_empty(); + vertices.push_back(p); + break; + } + case GeometryType::kLineString: + num_points.push_back(0); + break; + case GeometryType::kPolygon: + num_rings.push_back(0); + break; + case GeometryType::kMultiPoint: + num_points.push_back(0); + break; + case GeometryType::kMultiLineString: + num_parts.push_back(0); + break; + case GeometryType::kMultiPolygon: + num_parts.push_back(0); + break; + default: + throw std::runtime_error( + "Null geometry encountered for unsupported geometry type"); + } + } + } }; template @@ -442,7 +450,8 @@ struct DeviceParsedGeometries { } void Append(rmm::cuda_stream_view stream, - const std::vector>& host_geoms) { + const std::vector>& host_geoms, + double& t_alloc_ms, double& t_copy_ms) { size_t sz_feature_types = 0; size_t sz_num_geoms = 0; size_t sz_num_parts = 0; @@ -482,6 +491,9 @@ struct DeviceParsedGeometries { prev_sz_mbrs * sizeof(mbr_t) / 1024 / 1024, sz_mbrs * sizeof(mbr_t) / 1024 / 1024); + Stopwatch sw; + + sw.start(); feature_types.resize(feature_types.size() + sz_feature_types, stream); num_geoms.resize(num_geoms.size() + sz_num_geoms, stream); num_parts.resize(num_parts.size() + sz_num_parts, stream); @@ -489,7 +501,11 @@ struct DeviceParsedGeometries { num_points.resize(num_points.size() + sz_num_points, stream); vertices.resize(vertices.size() + sz_vertices, stream); mbrs.resize(mbrs.size() + sz_mbrs, stream); - + stream.synchronize(); + sw.stop(); + t_alloc_ms += sw.ms(); + Instrument::Range r("H2D", gpuspatial::Color::Blue); + sw.start(); for (auto& geoms : host_geoms) { detail::async_copy_h2d(stream, geoms.feature_types.data(), feature_types.data() + prev_sz_feature_types, @@ -518,6 +534,9 @@ struct DeviceParsedGeometries { prev_sz_vertices += geoms.vertices.size(); prev_sz_mbrs += geoms.mbrs.size(); } + stream.synchronize(); + sw.stop(); + t_copy_ms += sw.ms(); } }; } // namespace detail @@ -531,9 +550,7 @@ class ParallelWkbLoader { public: struct Config { - // How many rows of WKBs to process in one chunk - // This value affects the peak memory usage and overheads - int chunk_size = 16 * 1024; + float memory_quota = 0.8f; // percentage of free memory to use }; ParallelWkbLoader() @@ -543,9 +560,8 @@ class ParallelWkbLoader { : thread_pool_(thread_pool) {} void Init(const Config& config = Config()) { - ArrowArrayViewInitFromType(&array_view_, NANOARROW_TYPE_BINARY); config_ = config; - geometry_type_ = GeometryType::kNull; + Clear(rmm::cuda_stream_default); } void Clear(rmm::cuda_stream_view stream) { @@ -553,72 +569,97 @@ class ParallelWkbLoader { geoms_.Clear(stream); } - void Parse(rmm::cuda_stream_view stream, const ArrowArray* array, int64_t offset, - int64_t length) { - using host_geometries_t = detail::HostParsedGeometries; + void Parse(rmm::cuda_stream_view stream, const ArrowSchema* schema, + const ArrowArray* array, int64_t offset, int64_t length) { + auto begin = thrust::make_counting_iterator(offset); + auto end = begin + length; + + Parse(stream, schema, array, begin, end); + } + + template + void Parse(rmm::cuda_stream_view stream, const ArrowSchema* schema, + const ArrowArray* array, OFFSET_IT begin, OFFSET_IT end) { ArrowError arrow_error; - if (ArrowArrayViewSetArray(&array_view_, array, &arrow_error) != NANOARROW_OK) { + + if (ArrowArrayViewInitFromSchema(array_view_.get(), schema, &arrow_error) != + NANOARROW_OK) { + throw std::runtime_error("ArrowArrayViewInitFromSchema error " + + std::string(arrow_error.message)); + } + using host_geometries_t = detail::HostParsedGeometries; + + size_t num_offsets = std::distance(begin, end); + if (num_offsets == 0) return; + + if (ArrowArrayViewSetArray(array_view_.get(), array, &arrow_error) != NANOARROW_OK) { throw std::runtime_error("ArrowArrayViewSetArray error " + std::string(arrow_error.message)); } + auto parallelism = thread_pool_->num_threads(); - auto est_bytes = estimateTotalBytes(array, offset, length); - auto free_memory = detail::get_free_physical_memory_linux(); + uint64_t est_bytes = estimateTotalBytes(begin, end); + + uint64_t free_memory = MemoryManager::get_available_host_memory(); + uint64_t memory_quota = free_memory * config_.memory_quota; uint32_t est_n_chunks = est_bytes / free_memory + 1; - uint32_t chunk_size = (length + est_n_chunks - 1) / est_n_chunks; + + // Use num_offsets instead of offsets.size() + uint32_t chunk_size = (num_offsets + est_n_chunks - 1) / est_n_chunks; + uint32_t n_chunks = (num_offsets + chunk_size - 1) / chunk_size; GPUSPATIAL_LOG_INFO( - "Parsing %ld rows, est arrow size %ld MB, free memory %lld, chunk size %u\n", - length, est_bytes / 1024 / 1024, free_memory / 1024 / 1024, chunk_size); + "Parsing %zu rows, est ArrowArray size %lu MB, Free Host Memory %lu MB, Memory quota %lu MB, Chunk Size %u, Total Chunks %u", + num_offsets, est_bytes / 1024 / 1024, free_memory / 1024 / 1024, + memory_quota / 1024 / 1024, chunk_size, n_chunks); - auto n_chunks = (length + chunk_size - 1) / chunk_size; Stopwatch sw; double t_fetch_type = 0, t_parse = 0, t_copy = 0; + double t_alloc = 0, t_h2d = 0; sw.start(); - updateGeometryType(offset, length); + // Assumption: updateGeometryType is updated to accept iterators (begin, end) + updateGeometryType(begin, end); sw.stop(); t_fetch_type = sw.ms(); - bool multi = geometry_type_ == GeometryType::kMultiPoint || - geometry_type_ == GeometryType::kMultiLineString || - geometry_type_ == GeometryType::kMultiPolygon; - bool has_geometry_collection = geometry_type_ == GeometryType::kGeometryCollection; - bool create_mbr = geometry_type_ != GeometryType::kPoint; - // reserve space geoms_.vertices.reserve(est_bytes / sizeof(POINT_T), stream); - if (create_mbr) geoms_.mbrs.reserve(array->length, stream); + if (geometry_type_ != GeometryType::kPoint) + geoms_.mbrs.reserve(array->length, stream); // Batch processing to reduce the peak memory usage - for (int64_t chunk = 0; chunk < n_chunks; chunk++) { + for (size_t chunk = 0; chunk < n_chunks; chunk++) { auto chunk_start = chunk * chunk_size; - auto chunk_end = std::min(length, (chunk + 1) * chunk_size); - auto work_size = chunk_end - chunk_start; + auto chunk_end = std::min(num_offsets, (chunk + 1) * chunk_size); + auto split_points = + assignBalancedWorks(begin + chunk_start, begin + chunk_end, parallelism); std::vector> pending_local_geoms; - auto thread_work_size = (work_size + parallelism - 1) / parallelism; - sw.start(); // Each thread will parse in parallel and store results sequentially for (int thread_idx = 0; thread_idx < parallelism; thread_idx++) { auto run = [&](int tid) { - // FIXME: SetDevice - auto thread_work_start = chunk_start + tid * thread_work_size; - auto thread_work_end = - std::min(chunk_end, thread_work_start + thread_work_size); - host_geometries_t local_geoms(multi, has_geometry_collection, create_mbr); + auto thread_work_start = split_points[tid]; + auto thread_work_end = split_points[tid + 1]; + host_geometries_t local_geoms(geometry_type_); GeoArrowWKBReader reader; GeoArrowError error; - GEOARROW_THROW_NOT_OK(nullptr, GeoArrowWKBReaderInit(&reader)); + GEOARROW_THROW_NOT_OK(&error, GeoArrowWKBReaderInit(&reader)); + + uint64_t chunk_bytes = + estimateTotalBytes(begin + thread_work_start, begin + thread_work_end); + local_geoms.vertices.reserve(chunk_bytes / sizeof(POINT_T)); for (uint32_t work_offset = thread_work_start; work_offset < thread_work_end; work_offset++) { - auto arrow_offset = work_offset + offset; + // Use iterator indexing (Requires RandomAccessIterator) + auto arrow_offset = begin[chunk_start + work_offset]; + // handle null value - if (ArrowArrayViewIsNull(&array_view_, arrow_offset)) { + if (ArrowArrayViewIsNull(array_view_.get(), arrow_offset)) { local_geoms.AddGeometry(nullptr); } else { - auto item = ArrowArrayViewGetBytesUnsafe(&array_view_, arrow_offset); + auto item = ArrowArrayViewGetBytesUnsafe(array_view_.get(), arrow_offset); GeoArrowGeometryView geom; GEOARROW_THROW_NOT_OK( @@ -629,6 +670,7 @@ class ParallelWkbLoader { } } + GeoArrowWKBReaderReset(&reader); return std::move(local_geoms); }; pending_local_geoms.push_back(std::move(thread_pool_->enqueue(run, thread_idx))); @@ -641,15 +683,14 @@ class ParallelWkbLoader { sw.stop(); t_parse += sw.ms(); sw.start(); - geoms_.Append(stream, local_geoms); + geoms_.Append(stream, local_geoms, t_alloc, t_h2d); stream.synchronize(); sw.stop(); t_copy += sw.ms(); } GPUSPATIAL_LOG_INFO( - "ParallelWkbLoader::Parse: fetched type in %.3f ms, parsed in %.3f ms, copied in " - "%.3f ms", - t_fetch_type, t_parse, t_copy); + "ParallelWkbLoader::Parse: fetched type in %.3f ms, parsed in %.3f ms, alloc %.3f ms, h2d copy %.3f ms", + t_fetch_type, t_parse, t_alloc, t_h2d); } DeviceGeometries Finish(rmm::cuda_stream_view stream) { @@ -746,8 +787,10 @@ class ParallelWkbLoader { std::move(ps_num_points); break; } + default: + throw std::runtime_error("Unsupported geometry type " + + GeometryTypeToString(geometry_type_) + " in Finish"); } - Clear(stream); stream.synchronize(); sw.stop(); GPUSPATIAL_LOG_INFO("Finish building DeviceGeometries in %.3f ms", sw.ms()); @@ -756,102 +799,99 @@ class ParallelWkbLoader { private: Config config_; - ArrowArrayView array_view_; + nanoarrow::UniqueArrayView array_view_; GeometryType geometry_type_; detail::DeviceParsedGeometries geoms_; std::shared_ptr thread_pool_; - void updateGeometryType(int64_t offset, int64_t length) { + template + void updateGeometryType(OFFSET_IT begin, OFFSET_IT end) { if (geometry_type_ == GeometryType::kGeometryCollection) { - // it's already the most generic type return; } - std::vector type_flags(8 /*WKB types*/, false); - std::vector workers; + size_t num_offsets = std::distance(begin, end); + if (num_offsets == 0) return; + auto parallelism = thread_pool_->num_threads(); - auto thread_work_size = (length + parallelism - 1) / parallelism; - std::vector> futures; + auto thread_work_size = (num_offsets + parallelism - 1) / parallelism; + + std::vector> futures; + futures.reserve(parallelism); + + // Detect Endianness once (outside the loop) + const bool host_is_little = detail::is_little_endian(); for (int thread_idx = 0; thread_idx < parallelism; thread_idx++) { - auto run = [&](int tid) { - auto thread_work_start = tid * thread_work_size; - auto thread_work_end = std::min(length, thread_work_start + thread_work_size); - GeoArrowWKBReader reader; - GeoArrowError error; - GEOARROW_THROW_NOT_OK(nullptr, GeoArrowWKBReaderInit(&reader)); + auto run = [=](int tid) -> uint32_t { + size_t thread_work_start = tid * thread_work_size; + size_t thread_work_end = + std::min(num_offsets, thread_work_start + thread_work_size); + + uint32_t local_seen_mask = 0; for (uint32_t work_offset = thread_work_start; work_offset < thread_work_end; work_offset++) { - auto arrow_offset = work_offset + offset; - // handle null value - if (ArrowArrayViewIsNull(&array_view_, arrow_offset)) { + auto arrow_offset = begin[work_offset]; + + if (ArrowArrayViewIsNull(array_view_.get(), arrow_offset)) { continue; } - auto item = ArrowArrayViewGetBytesUnsafe(&array_view_, arrow_offset); - auto* s = (struct detail::WKBReaderPrivate*)reader.private_data; - s->data = item.data.as_uint8; - s->data0 = s->data; - s->size_bytes = item.size_bytes; + auto item = ArrowArrayViewGetBytesUnsafe(array_view_.get(), arrow_offset); + + // Safety check: WKB minimal size is 5 bytes (1 byte order + 4 type) + if (item.size_bytes < 5) continue; - NANOARROW_THROW_NOT_OK(detail::WKBReaderReadEndian(s, &error)); + const uint8_t* data = item.data.as_uint8; + + // 1. Read Endianness Byte (0 = Big/XDR, 1 = Little/NDR) + uint8_t wkb_endian = data[0]; + + // 2. Read Type (Bytes 1-4) uint32_t geometry_type; - NANOARROW_THROW_NOT_OK(detail::WKBReaderReadUInt32(s, &geometry_type, &error)); + std::memcpy(&geometry_type, data + 1, sizeof(uint32_t)); + + // 3. Swap if mismatch + // If (WKB is Little) != (Host is Little), we must swap + if ((wkb_endian == 1) != host_is_little) { + geometry_type = __builtin_bswap32(geometry_type); + } + + // 4. Validate and Accumulate (Branchless Masking) if (geometry_type > 7) { - throw std::runtime_error( - "Extended WKB types are not currently supported, type = " + - std::to_string(geometry_type)); + // It's safer to throw exception outside the tight loop or set an error flag + // For now, we skip or you can throw. + throw std::runtime_error("Extended WKB types not supported: " + + std::to_string(geometry_type)); } - assert(geometry_type < type_flags.size()); - type_flags[geometry_type] = true; + + local_seen_mask |= (1 << geometry_type); } + return local_seen_mask; }; + futures.push_back(std::move(thread_pool_->enqueue(run, thread_idx))); } + + // Reduction + uint32_t global_mask = 0; for (auto& fu : futures) { - fu.get(); + global_mask |= fu.get(); } std::unordered_set types; - // include existing geometry type if (geometry_type_ != GeometryType::kNull) { types.insert(geometry_type_); } for (int i = 1; i <= 7; i++) { - if (type_flags[i]) { + if (global_mask & (1 << i)) { types.insert(static_cast(i)); } } - GeometryType final_type; - // Infer a generic type that can represent the current and previous types - switch (types.size()) { - case 0: - final_type = GeometryType::kNull; - break; - case 1: - final_type = *types.begin(); - break; - case 2: { - if (types.count(GeometryType::kPoint) && types.count(GeometryType::kMultiPoint)) { - final_type = GeometryType::kMultiPoint; - } else if (types.count(GeometryType::kLineString) && - types.count(GeometryType::kMultiLineString)) { - final_type = GeometryType::kMultiLineString; - } else if (types.count(GeometryType::kPolygon) && - types.count(GeometryType::kMultiPolygon)) { - final_type = GeometryType::kMultiPolygon; - } else { - final_type = GeometryType::kGeometryCollection; - } - break; - } - default: - final_type = GeometryType::kGeometryCollection; - } - geometry_type_ = final_type; + geometry_type_ = getUpcastedGeometryType(types); } template @@ -875,21 +915,107 @@ class ParallelWkbLoader { nums.shrink_to_fit(stream); } - size_t estimateTotalBytes(const ArrowArray* array, int64_t offset, int64_t length) { - ArrowError arrow_error; - if (ArrowArrayViewSetArray(&array_view_, array, &arrow_error) != NANOARROW_OK) { - throw std::runtime_error("ArrowArrayViewSetArray error " + - std::string(arrow_error.message)); - } + template + size_t estimateTotalBytes(OFFSET_IT begin, OFFSET_IT end) const { size_t total_bytes = 0; - for (int64_t i = 0; i < length; i++) { - if (!ArrowArrayViewIsNull(&array_view_, offset + i)) { - auto item = ArrowArrayViewGetBytesUnsafe(&array_view_, offset + i); + for (auto it = begin; it != end; ++it) { + auto offset = *it; + if (!ArrowArrayViewIsNull(array_view_.get(), offset)) { + auto item = ArrowArrayViewGetBytesUnsafe(array_view_.get(), offset); total_bytes += item.size_bytes - 1 // byte order - 2 * sizeof(uint32_t); // type + size } } return total_bytes; } + + template + std::vector assignBalancedWorks(OFFSET_IT begin, OFFSET_IT end, + uint32_t num_threads) const { + size_t total_bytes = 0; + std::vector bytes_per_row; + size_t num_rows = std::distance(begin, end); + + bytes_per_row.resize(num_rows, 0); + + // 1. Calculate bytes per row + for (auto it = begin; it != end; ++it) { + auto offset = *it; + if (!ArrowArrayViewIsNull(array_view_.get(), offset)) { + auto item = ArrowArrayViewGetBytesUnsafe(array_view_.get(), offset); + // Assuming item.size_bytes fits in uint32_t based on vector definition + bytes_per_row[it - begin] = static_cast(item.size_bytes); + } + } + + // 2. Calculate prefix sum + // We use size_t (or uint64_t) for the sum to prevent overflow + std::vector prefix_sum; + prefix_sum.reserve(num_rows + 1); + prefix_sum.push_back(0); + + for (uint32_t b : bytes_per_row) { + total_bytes += b; + prefix_sum.push_back(total_bytes); + } + + // 3. Calculate balanced split points + std::vector split_points; + split_points.reserve(num_threads + 1); + split_points.push_back(0); // The start index for the first thread + + // Avoid division by zero + if (num_threads > 0) { + double ideal_chunk_size = static_cast(total_bytes) / num_threads; + + for (uint32_t i = 1; i < num_threads; ++i) { + auto target_size = static_cast(i * ideal_chunk_size); + + // Find the first index where cumulative bytes >= target_size + auto it = std::lower_bound(prefix_sum.begin(), prefix_sum.end(), target_size); + + // Convert iterator to index (row number) + auto split_index = static_cast(std::distance(prefix_sum.begin(), it)); + split_points.push_back(split_index); + } + } + + // Ensure the last point is the total number of rows + // If num_threads was 0, this will be the second element (0, num_rows) + split_points.push_back(static_cast(num_rows)); + + return split_points; + } + + GeometryType getUpcastedGeometryType( + const std::unordered_set& types) const { + GeometryType final_type; + // Infer a generic type that can represent the current and previous types + switch (types.size()) { + case 0: + final_type = GeometryType::kNull; + break; + case 1: + final_type = *types.begin(); + break; + case 2: { + if (types.count(GeometryType::kPoint) && types.count(GeometryType::kMultiPoint)) { + final_type = GeometryType::kMultiPoint; + } else if (types.count(GeometryType::kLineString) && + types.count(GeometryType::kMultiLineString)) { + final_type = GeometryType::kMultiLineString; + } else if (types.count(GeometryType::kPolygon) && + types.count(GeometryType::kMultiPolygon)) { + final_type = GeometryType::kMultiPolygon; + } else { + final_type = GeometryType::kGeometryCollection; + } + break; + } + default: + final_type = GeometryType::kGeometryCollection; + } + return final_type; + } }; } // namespace gpuspatial diff --git a/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/mem/memory_manager.hpp b/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/mem/memory_manager.hpp new file mode 100644 index 000000000..7160fb6da --- /dev/null +++ b/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/mem/memory_manager.hpp @@ -0,0 +1,84 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +#pragma once +#include "rmm/mr/device/cuda_async_memory_resource.hpp" +#include "rmm/mr/device/device_memory_resource.hpp" +#include "rmm/mr/device/pool_memory_resource.hpp" +#include "rmm/mr/device/tracking_resource_adaptor.hpp" + +#include +namespace gpuspatial { +/** @brief An optional singleton memory manager to use asynchronous memory allocation and + * memory pool with RAPIDS's RMM memory resources. + * Once the memory manager is initialized, all GPU memory allocations will use the RMM's + * memory allocator. The user should call Shutdown() to cleanly release RMM resources + * before program exit. + */ +class MemoryManager { + public: + static MemoryManager& instance(); + + MemoryManager(const MemoryManager&) = delete; + MemoryManager& operator=(const MemoryManager&) = delete; + + /** + * @brief Initializes the memory resources. + * @param use_pool Whether to use RMM pool allocator + * @param init_pool_precent Initial pool size as percent of total GPU memory + */ + void Init(bool use_pool, int init_pool_precent = 50); + + /** + * @brief Estimates free memory available in bytes + * * If using a pool: Returns (Total GPU Mem - Tracked Bytes) * 0.95 safety factor. + * If direct: Returns actual CUDA free memory. + */ + size_t get_available_device_memory() const; + + /** + * @brief Estimates free host memory available in bytes + */ + static size_t get_available_host_memory(); + /** + * @brief Cleanly resets RMM resources. Automatically called on destruction. + */ + void Shutdown(); + + private: + MemoryManager() = default; + ~MemoryManager(); + + // --- Type Aliases --- + using CudaMR = rmm::mr::cuda_async_memory_resource; + using PoolMR = rmm::mr::pool_memory_resource; + + // We have two possible tracker types depending on configuration + using PoolTracker = rmm::mr::tracking_resource_adaptor; + using CudaTracker = rmm::mr::tracking_resource_adaptor; + + // --- State --- + bool is_initialized_ = false; + bool use_pool_ = false; + + std::unique_ptr cuda_mr_; + std::unique_ptr pool_mr_; + std::unique_ptr active_resource_; + + void* raw_tracker_ptr_ = nullptr; +}; +} // namespace gpuspatial diff --git a/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/refine/rt_spatial_refiner.cuh b/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/refine/rt_spatial_refiner.cuh new file mode 100644 index 000000000..4a9c3112c --- /dev/null +++ b/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/refine/rt_spatial_refiner.cuh @@ -0,0 +1,124 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +#pragma once +#include "gpuspatial/geom/box.hpp" +#include "gpuspatial/geom/point.hpp" +#include "gpuspatial/loader/device_geometries.hpp" +#include "gpuspatial/loader/parallel_wkb_loader.hpp" +#include "gpuspatial/refine/rt_spatial_refiner.hpp" +#include "gpuspatial/refine/spatial_refiner.hpp" +#include "gpuspatial/relate/relate_engine.cuh" +#include "gpuspatial/rt/rt_engine.hpp" +#include "gpuspatial/utils/gpu_timer.hpp" +#include "gpuspatial/utils/thread_pool.hpp" + +#include "geoarrow/geoarrow_type.h" +#include "nanoarrow/nanoarrow.h" + +#include "rmm/cuda_stream_pool.hpp" +#include "rmm/cuda_stream_view.hpp" + +#include + +#define GPUSPATIAL_PROFILING +namespace gpuspatial { + +class RTSpatialRefiner : public SpatialRefiner { + // TODO: Assuming every thing is 2D in double for now + using scalar_t = double; + static constexpr int n_dim = 2; + using index_t = uint32_t; // type of the index to represent geometries + // geometry types + using point_t = Point; + using multi_point_t = MultiPoint; + using line_string_t = LineString; + using multi_line_string_t = MultiLineString; + using polygon_t = Polygon; + using multi_polygon_t = MultiPolygon; + // geometry array types + using point_array_t = PointArrayView; + using multi_point_array_t = MultiPointArrayView; + using line_string_array_t = LineStringArrayView; + using multi_line_string_array_t = MultiLineStringArrayView; + using polygon_array_t = PolygonArrayView; + using multi_polygon_array_t = MultiPolygonArrayView; + + using dev_geometries_t = DeviceGeometries; + using box_t = Box>; + using loader_t = ParallelWkbLoader; + + static_assert(sizeof(Box>) == sizeof(box_t), + "Box> size mismatch!"); + + public: + struct IndicesMap { + // Sorted unique original indices + std::vector h_uniq_indices; + rmm::device_uvector d_uniq_indices{0, rmm::cuda_stream_default}; + // Mapping from original indices to consecutive zero-based indices + rmm::device_uvector d_reordered_indices{0, rmm::cuda_stream_default}; + }; + struct SpatialRefinerContext { + rmm::cuda_stream_view cuda_stream; +#ifdef GPUSPATIAL_PROFILING + GPUTimer timer; + // counters + double parse_ms = 0.0; + double alloc_ms = 0.0; + double refine_ms = 0.0; + double copy_res_ms = 0.0; +#endif + }; + + RTSpatialRefiner() = default; + + RTSpatialRefiner(const RTSpatialRefinerConfig& config); + + ~RTSpatialRefiner() = default; + + void Clear() override; + + void PushBuild(const ArrowSchema* build_schema, const ArrowArray* build_array) override; + + void FinishBuilding() override; + + uint32_t Refine(const ArrowSchema* probe_schema, const ArrowArray* probe_array, + Predicate predicate, uint32_t* build_indices, uint32_t* probe_indices, + uint32_t len) override; + + uint32_t Refine(const ArrowSchema* build_schema, const ArrowArray* build_array, + const ArrowSchema* probe_schema, const ArrowArray* probe_array, + Predicate predicate, uint32_t* build_indices, uint32_t* probe_indices, + uint32_t len) override; + + uint32_t RefinePipelined(const ArrowSchema* probe_schema, const ArrowArray* probe_array, + Predicate predicate, uint32_t* build_indices, + uint32_t* probe_indices, uint32_t len); + + private: + RTSpatialRefinerConfig config_; + std::unique_ptr stream_pool_; + std::shared_ptr thread_pool_; + std::unique_ptr> wkb_loader_; + dev_geometries_t build_geometries_; + + template + void buildIndicesMap(rmm::cuda_stream_view stream, INDEX_IT index_begin, + INDEX_IT index_end, IndicesMap& indices_map) const; +}; + +} // namespace gpuspatial diff --git a/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/refine/rt_spatial_refiner.hpp b/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/refine/rt_spatial_refiner.hpp new file mode 100644 index 000000000..6b6978799 --- /dev/null +++ b/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/refine/rt_spatial_refiner.hpp @@ -0,0 +1,52 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +#pragma once + +#include "gpuspatial/refine/spatial_refiner.hpp" +#include "gpuspatial/rt/rt_engine.hpp" + +#include + +namespace gpuspatial { + +struct RTSpatialRefinerConfig { + std::shared_ptr rt_engine; + // Prefer fast build the BVH + bool prefer_fast_build = false; + // Compress the BVH to save memory + bool compact = true; + // Loader configurations + // How many threads to use for parsing WKBs + uint32_t parsing_threads = std::thread::hardware_concurrency(); + // How many threads are allowed to call PushStream concurrently + uint32_t concurrency = 1; + // Overlapping parsing and refinement by pipelining multiple batches; 1 means no + // pipelining + uint32_t pipeline_batches = 1; + // the host memory quota for WKB parser compared to the available memory + float wkb_parser_memory_quota = 0.8; + // the device memory quota for relate engine compared to the available memory + float relate_engine_memory_quota = 0.8; + // this value determines RELATE_MAX_DEPTH + size_t stack_size_bytes = 3 * 1024; + bool sort_probe_indices = true; // Sedona's spatial-join may require ordered output +}; + +std::unique_ptr CreateRTSpatialRefiner( + const RTSpatialRefinerConfig& config); + +} // namespace gpuspatial diff --git a/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/refine/spatial_refiner.hpp b/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/refine/spatial_refiner.hpp new file mode 100644 index 000000000..3b979ed56 --- /dev/null +++ b/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/refine/spatial_refiner.hpp @@ -0,0 +1,44 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +#pragma once +#include "gpuspatial/relate/predicate.hpp" + +#include "nanoarrow/nanoarrow.h" + +namespace gpuspatial { +class SpatialRefiner { + public: + virtual ~SpatialRefiner() = default; + + virtual void Clear() = 0; + + virtual void PushBuild(const ArrowSchema* build_schema, + const ArrowArray* build_array) = 0; + + virtual void FinishBuilding() = 0; + + virtual uint32_t Refine(const ArrowSchema* probe_schema, const ArrowArray* probe_array, + Predicate predicate, uint32_t* build_indices, + uint32_t* probe_indices, uint32_t len) = 0; + + virtual uint32_t Refine(const ArrowSchema* build_schema, const ArrowArray* build_array, + const ArrowSchema* probe_schema, const ArrowArray* probe_array, + Predicate predicate, uint32_t* build_indices, + uint32_t* probe_indices, uint32_t len) = 0; +}; + +} // namespace gpuspatial diff --git a/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/relate/intersection_matrix.cuh b/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/relate/intersection_matrix.hpp similarity index 100% rename from c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/relate/intersection_matrix.cuh rename to c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/relate/intersection_matrix.hpp diff --git a/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/relate/predicate.cuh b/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/relate/predicate.hpp similarity index 100% rename from c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/relate/predicate.cuh rename to c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/relate/predicate.hpp diff --git a/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/relate/relate.cuh b/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/relate/relate.hpp similarity index 95% rename from c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/relate/relate.cuh rename to c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/relate/relate.hpp index 4b397453c..038ce7681 100644 --- a/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/relate/relate.cuh +++ b/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/relate/relate.hpp @@ -22,13 +22,13 @@ */ #pragma once -#include "gpuspatial/geom/line_string.cuh" -#include "gpuspatial/geom/multi_line_string.cuh" -#include "gpuspatial/geom/multi_point.cuh" -#include "gpuspatial/geom/multi_polygon.cuh" -#include "gpuspatial/geom/point.cuh" -#include "gpuspatial/geom/polygon.cuh" -#include "gpuspatial/relate/intersection_matrix.cuh" +#include "gpuspatial/geom/line_string.hpp" +#include "gpuspatial/geom/multi_line_string.hpp" +#include "gpuspatial/geom/multi_point.hpp" +#include "gpuspatial/geom/multi_polygon.hpp" +#include "gpuspatial/geom/point.hpp" +#include "gpuspatial/geom/polygon.hpp" +#include "gpuspatial/relate/intersection_matrix.hpp" // Ref: https://github.com/heterodb/pg-strom/blob/master/src/xpu_postgis.cu // A good visualize to cases // https://dev.luciad.com/portal/productDocumentation/LuciadFusion/docs/articles/guide/geometry/images/interior_exterior_boundary.png @@ -169,8 +169,10 @@ DEV_HOST int32_t relate(const POINT_T& P1, bool p1_is_head, const POINT_T& P2, if (p1_in_qq != PointLocation::kOutside && p2_in_qq != PointLocation::kOutside) { /* P1-P2 is fully contained by Q1-Q2 */ - if (p1_is_head) retval |= (IntersectionMatrix::BOUND_BOUND_0D | IM__LINE_HEAD_CONTAINED); - if (p2_is_tail) retval |= (IntersectionMatrix::BOUND_BOUND_0D | IM__LINE_TAIL_CONTAINED); + if (p1_is_head) + retval |= (IntersectionMatrix::BOUND_BOUND_0D | IM__LINE_HEAD_CONTAINED); + if (p2_is_tail) + retval |= (IntersectionMatrix::BOUND_BOUND_0D | IM__LINE_TAIL_CONTAINED); if (P1 == P2) { if (!p1_is_head && !p2_is_tail) retval |= IntersectionMatrix::INTER_BOUND_0D; @@ -457,8 +459,9 @@ DEV_HOST_INLINE int32_t relate(const LinearRing& ring, std::min(P1.x(), P2.x()) > mbr.get_max().x() || std::max(P1.y(), P2.y()) < mbr.get_min().y() || std::min(P1.y(), P2.y()) > mbr.get_max().y()) { - status = (IntersectionMatrix::INTER_EXTER_1D | IntersectionMatrix::BOUND_EXTER_0D | IntersectionMatrix::EXTER_INTER_2D | - IntersectionMatrix::EXTER_BOUND_1D | IntersectionMatrix::EXTER_EXTER_2D); + status = (IntersectionMatrix::INTER_EXTER_1D | IntersectionMatrix::BOUND_EXTER_0D | + IntersectionMatrix::EXTER_INTER_2D | IntersectionMatrix::EXTER_BOUND_1D | + IntersectionMatrix::EXTER_EXTER_2D); } else { status = relate(P1, false, P2, false, geom, 0, false); // char res[10]; @@ -497,25 +500,32 @@ DEV_HOST_INLINE int32_t relate(const LinearRing& ring, */ if ((rflags & IntersectionMatrix::INTER_BOUND_2D) == IntersectionMatrix::INTER_BOUND_1D) boundary = IntersectionMatrix::BOUND_BOUND_1D; - else if ((rflags & IntersectionMatrix::INTER_BOUND_2D) == IntersectionMatrix::INTER_BOUND_0D) + else if ((rflags & IntersectionMatrix::INTER_BOUND_2D) == + IntersectionMatrix::INTER_BOUND_0D) boundary = IntersectionMatrix::BOUND_BOUND_0D; - if ((rflags & IntersectionMatrix::INTER_INTER_2D) == 0 && (rflags & IntersectionMatrix::INTER_BOUND_2D) != 0 && + if ((rflags & IntersectionMatrix::INTER_INTER_2D) == 0 && + (rflags & IntersectionMatrix::INTER_BOUND_2D) != 0 && (rflags & IntersectionMatrix::INTER_EXTER_2D) == 0) { /* ring equals to the polygon */ - return (IntersectionMatrix::INTER_INTER_2D | IntersectionMatrix::BOUND_BOUND_1D | IntersectionMatrix::EXTER_EXTER_2D); - } else if ((rflags & IntersectionMatrix::INTER_INTER_2D) == 0 && (rflags & IntersectionMatrix::INTER_BOUND_2D) == 0 && + return (IntersectionMatrix::INTER_INTER_2D | IntersectionMatrix::BOUND_BOUND_1D | + IntersectionMatrix::EXTER_EXTER_2D); + } else if ((rflags & IntersectionMatrix::INTER_INTER_2D) == 0 && + (rflags & IntersectionMatrix::INTER_BOUND_2D) == 0 && (rflags & IntersectionMatrix::INTER_EXTER_2D) != 0) { if (poly_has_outside) { /* disjoint */ - return (IntersectionMatrix::INTER_EXTER_2D | IntersectionMatrix::BOUND_EXTER_1D | IntersectionMatrix::EXTER_INTER_2D | - IntersectionMatrix::EXTER_BOUND_1D | IntersectionMatrix::EXTER_EXTER_2D); + return (IntersectionMatrix::INTER_EXTER_2D | IntersectionMatrix::BOUND_EXTER_1D | + IntersectionMatrix::EXTER_INTER_2D | IntersectionMatrix::EXTER_BOUND_1D | + IntersectionMatrix::EXTER_EXTER_2D); } else { /* ring fully contains the polygons */ - return (IntersectionMatrix::INTER_INTER_2D | IntersectionMatrix::INTER_BOUND_1D | IntersectionMatrix::INTER_EXTER_2D | - IntersectionMatrix::BOUND_EXTER_1D | IntersectionMatrix::EXTER_EXTER_2D); + return (IntersectionMatrix::INTER_INTER_2D | IntersectionMatrix::INTER_BOUND_1D | + IntersectionMatrix::INTER_EXTER_2D | IntersectionMatrix::BOUND_EXTER_1D | + IntersectionMatrix::EXTER_EXTER_2D); } - } else if ((rflags & IntersectionMatrix::INTER_INTER_2D) != 0 && (rflags & IntersectionMatrix::INTER_BOUND_2D) != 0 + } else if ((rflags & IntersectionMatrix::INTER_INTER_2D) != 0 && + (rflags & IntersectionMatrix::INTER_BOUND_2D) != 0 // TODO: Need this? && (rflags & IntersectionMatrix::INTER_EXTER_2D) != 0 ) { /* ring has intersection to the polygon */ @@ -523,26 +533,36 @@ DEV_HOST_INLINE int32_t relate(const LinearRing& ring, if ((rflags & IntersectionMatrix::INTER_EXTER_2D) != 0) { boundary |= IntersectionMatrix::BOUND_EXTER_1D; } - return boundary | (IntersectionMatrix::INTER_INTER_2D | IntersectionMatrix::INTER_BOUND_1D | IntersectionMatrix::INTER_EXTER_2D | - IntersectionMatrix::BOUND_INTER_1D | IntersectionMatrix::EXTER_INTER_2D | IntersectionMatrix::EXTER_BOUND_1D | - IntersectionMatrix::EXTER_EXTER_2D); - } else if ((rflags & IntersectionMatrix::INTER_INTER_2D) == 0 && (rflags & IntersectionMatrix::INTER_BOUND_2D) != 0 && + return boundary | + (IntersectionMatrix::INTER_INTER_2D | IntersectionMatrix::INTER_BOUND_1D | + IntersectionMatrix::INTER_EXTER_2D | IntersectionMatrix::BOUND_INTER_1D | + IntersectionMatrix::EXTER_INTER_2D | IntersectionMatrix::EXTER_BOUND_1D | + IntersectionMatrix::EXTER_EXTER_2D); + } else if ((rflags & IntersectionMatrix::INTER_INTER_2D) == 0 && + (rflags & IntersectionMatrix::INTER_BOUND_2D) != 0 && (rflags & IntersectionMatrix::INTER_EXTER_2D) != 0) { if (poly_has_outside) { /* ring touched the polygon at a boundary, but no intersection */ assert(boundary != 0); - return boundary | (IntersectionMatrix::INTER_EXTER_2D | IntersectionMatrix::BOUND_EXTER_1D | IntersectionMatrix::EXTER_INTER_2D | - IntersectionMatrix::EXTER_BOUND_1D | IntersectionMatrix::EXTER_EXTER_2D); + return boundary | + (IntersectionMatrix::INTER_EXTER_2D | IntersectionMatrix::BOUND_EXTER_1D | + IntersectionMatrix::EXTER_INTER_2D | IntersectionMatrix::EXTER_BOUND_1D | + IntersectionMatrix::EXTER_EXTER_2D); } else { /* ring fully contains the polygon touched at boundaries */ assert(boundary != 0); - return boundary | (IntersectionMatrix::INTER_INTER_2D | IntersectionMatrix::INTER_BOUND_1D | IntersectionMatrix::INTER_EXTER_2D | - IntersectionMatrix::BOUND_EXTER_1D | IntersectionMatrix::EXTER_EXTER_2D); + return boundary | + (IntersectionMatrix::INTER_INTER_2D | IntersectionMatrix::INTER_BOUND_1D | + IntersectionMatrix::INTER_EXTER_2D | IntersectionMatrix::BOUND_EXTER_1D | + IntersectionMatrix::EXTER_EXTER_2D); } - } else if ((rflags & IntersectionMatrix::INTER_INTER_2D) != 0 && (rflags & IntersectionMatrix::INTER_EXTER_2D) == 0) { + } else if ((rflags & IntersectionMatrix::INTER_INTER_2D) != 0 && + (rflags & IntersectionMatrix::INTER_EXTER_2D) == 0) { /* ring is fully contained by the polygon; might be touched */ - return boundary | (IntersectionMatrix::INTER_INTER_2D | IntersectionMatrix::BOUND_INTER_1D | IntersectionMatrix::EXTER_INTER_2D | - IntersectionMatrix::EXTER_BOUND_1D | IntersectionMatrix::EXTER_EXTER_2D); + return boundary | + (IntersectionMatrix::INTER_INTER_2D | IntersectionMatrix::BOUND_INTER_1D | + IntersectionMatrix::EXTER_INTER_2D | IntersectionMatrix::EXTER_BOUND_1D | + IntersectionMatrix::EXTER_EXTER_2D); } // FIXME: printf("unknown intersection\n"); @@ -663,7 +683,8 @@ DEV_HOST_INLINE int32_t relate(const POINT_T& geom1, const MultiPolygon& geom2, ArrayView locations) { assert(geom2.num_polygons() == locations.size()); - if (geom2.empty()) return IntersectionMatrix::INTER_EXTER_0D | IntersectionMatrix::EXTER_EXTER_2D; + if (geom2.empty()) + return IntersectionMatrix::INTER_EXTER_0D | IntersectionMatrix::EXTER_EXTER_2D; int32_t retval = IntersectionMatrix::EXTER_EXTER_2D; bool matched = false; diff --git a/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/index/relate_engine.cuh b/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/relate/relate_engine.cuh similarity index 66% rename from c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/index/relate_engine.cuh rename to c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/relate/relate_engine.cuh index 5fb275078..c83538a75 100644 --- a/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/index/relate_engine.cuh +++ b/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/relate/relate_engine.cuh @@ -15,10 +15,9 @@ // specific language governing permissions and limitations // under the License. #pragma once -#include "gpuspatial/index/detail/rt_engine.hpp" -#include "gpuspatial/loader/device_geometries.cuh" -#include "gpuspatial/relate/predicate.cuh" -#include "gpuspatial/utils/queue.h" +#include "gpuspatial/loader/device_geometries.hpp" +#include "gpuspatial/relate/predicate.hpp" +#include "gpuspatial/rt/rt_engine.hpp" #include "rmm/cuda_stream_view.hpp" @@ -31,8 +30,9 @@ class RelateEngine { public: struct Config { bool bvh_fast_build = false; - bool bvh_fast_compact = true; + bool bvh_compact = true; float memory_quota = 0.8; + int segs_per_aabb = 32; }; RelateEngine() = default; @@ -40,80 +40,94 @@ class RelateEngine { RelateEngine(const DeviceGeometries* geoms1); RelateEngine(const DeviceGeometries* geoms1, - const details::RTEngine* rt_engine); + const RTEngine* rt_engine); void set_config(const Config& config) { config_ = config; } void Evaluate(const rmm::cuda_stream_view& stream, const DeviceGeometries& geoms2, Predicate predicate, - Queue>& ids); + rmm::device_uvector& ids1, rmm::device_uvector& ids2); template void Evaluate(const rmm::cuda_stream_view& stream, const GEOM2_ARRAY_VIEW_T& geom_array2, Predicate predicate, - Queue>& ids); + rmm::device_uvector& ids1, rmm::device_uvector& ids2); // This is a generic version that can accept any two geometry array views template void Evaluate(const rmm::cuda_stream_view& stream, const GEOM1_ARRAY_VIEW_T& geom_array1, const GEOM2_ARRAY_VIEW_T& geom_array2, Predicate predicate, - Queue>& ids); + rmm::device_uvector& ids1, rmm::device_uvector& ids2); // These are the specific overloads for RT-accelerated PIP queries void Evaluate(const rmm::cuda_stream_view& stream, const PointArrayView& geom_array1, const PolygonArrayView& geom_array2, - Predicate predicate, Queue>& ids); + Predicate predicate, rmm::device_uvector& ids1, + rmm::device_uvector& ids2); void Evaluate(const rmm::cuda_stream_view& stream, const MultiPointArrayView& geom_array1, const PolygonArrayView& geom_array2, - Predicate predicate, Queue>& ids); + Predicate predicate, rmm::device_uvector& ids1, + rmm::device_uvector& ids2); void Evaluate(const rmm::cuda_stream_view& stream, const PolygonArrayView& geom_array1, const PointArrayView& geom_array2, Predicate predicate, - Queue>& ids); + rmm::device_uvector& ids1, rmm::device_uvector& ids2); void Evaluate(const rmm::cuda_stream_view& stream, const PolygonArrayView& geom_array1, const MultiPointArrayView& geom_array2, - Predicate predicate, Queue>& ids); + Predicate predicate, rmm::device_uvector& ids1, + rmm::device_uvector& ids2); void Evaluate(const rmm::cuda_stream_view& stream, const PointArrayView& geom_array1, const MultiPolygonArrayView& geom_array2, - Predicate predicate, Queue>& ids); + Predicate predicate, rmm::device_uvector& ids1, + rmm::device_uvector& ids2); void Evaluate(const rmm::cuda_stream_view& stream, const MultiPointArrayView& geom_array1, const MultiPolygonArrayView& geom_array2, - Predicate predicate, Queue>& ids); + Predicate predicate, rmm::device_uvector& ids1, + rmm::device_uvector& ids2); void Evaluate(const rmm::cuda_stream_view& stream, const MultiPolygonArrayView& geom_array1, const PointArrayView& geom_array2, Predicate predicate, - Queue>& ids); + rmm::device_uvector& ids1, rmm::device_uvector& ids2); void Evaluate(const rmm::cuda_stream_view& stream, const MultiPolygonArrayView& geom_array1, const MultiPointArrayView& geom_array2, - Predicate predicate, Queue>& ids); + Predicate predicate, rmm::device_uvector& ids1, + rmm::device_uvector& ids2); void EvaluateImpl(const rmm::cuda_stream_view& stream, const PointArrayView& point_array, const MultiPointArrayView& multi_point_array, const PolygonArrayView& poly_array, - Predicate predicate, Queue>& ids, - bool inverse = false); + Predicate predicate, rmm::device_uvector& point_ids, + rmm::device_uvector& poly_ids, bool inverse = false); void EvaluateImpl(const rmm::cuda_stream_view& stream, const PointArrayView& point_array, const MultiPointArrayView& multi_point_array, const MultiPolygonArrayView& multi_poly_array, - Predicate predicate, Queue>& ids, - bool inverse); + Predicate predicate, rmm::device_uvector& ids1, + rmm::device_uvector& ids2, bool inverse); + + size_t EstimateBVHSize(const rmm::cuda_stream_view& stream, + const PolygonArrayView& polys, + ArrayView poly_ids, int segs_per_aabb); + + size_t EstimateBVHSize(const rmm::cuda_stream_view& stream, + const MultiPolygonArrayView& multi_polys, + ArrayView multi_poly_ids, int segs_per_aabb); /** * Build BVH for a subset of polygons @@ -122,34 +136,27 @@ class RelateEngine { * @param polygon_ids * @param buffer */ - OptixTraversableHandle BuildBVH(const rmm::cuda_stream_view& stream, - const PolygonArrayView& polygons, - ArrayView polygon_ids, - rmm::device_uvector& seg_begins, - rmm::device_buffer& buffer, - rmm::device_uvector& aabb_poly_ids, - rmm::device_uvector& aabb_ring_ids); + OptixTraversableHandle BuildBVH( + const rmm::cuda_stream_view& stream, + const PolygonArrayView& polygons, ArrayView polygon_ids, + int segs_per_aabb, rmm::device_buffer& buffer, + rmm::device_uvector& aabb_poly_ids, + rmm::device_uvector& aabb_ring_ids, + rmm::device_uvector>& aabb_vertex_offsets); OptixTraversableHandle BuildBVH( const rmm::cuda_stream_view& stream, const MultiPolygonArrayView& multi_polys, - ArrayView multi_poly_ids, rmm::device_uvector& seg_begins, - rmm::device_uvector& part_begins, rmm::device_buffer& buffer, + ArrayView multi_poly_ids, int segs_per_aabb, rmm::device_buffer& buffer, rmm::device_uvector& aabb_multi_poly_ids, rmm::device_uvector& aabb_part_ids, - rmm::device_uvector& aabb_ring_ids); - - size_t EstimateBVHSize(const rmm::cuda_stream_view& stream, - const PolygonArrayView& polys, - ArrayView poly_ids); - - size_t EstimateBVHSize(const rmm::cuda_stream_view& stream, - const MultiPolygonArrayView& multi_polys, - ArrayView multi_poly_ids); + rmm::device_uvector& aabb_ring_ids, + rmm::device_uvector>& aabb_vertex_offsets, + rmm::device_uvector& part_begins); private: Config config_; const DeviceGeometries* geoms1_; - const details::RTEngine* rt_engine_; + const RTEngine* rt_engine_; }; } // namespace gpuspatial diff --git a/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/index/detail/launch_parameters.h b/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/rt/launch_parameters.cuh similarity index 67% rename from c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/index/detail/launch_parameters.h rename to c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/rt/launch_parameters.cuh index 555d2504c..a263fbcf2 100644 --- a/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/index/detail/launch_parameters.h +++ b/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/rt/launch_parameters.cuh @@ -16,13 +16,13 @@ // under the License. #pragma once -#include "gpuspatial/geom/box.cuh" -#include "gpuspatial/geom/multi_point.cuh" -#include "gpuspatial/geom/multi_polygon.cuh" -#include "gpuspatial/geom/point.cuh" -#include "gpuspatial/geom/polygon.cuh" -#include "gpuspatial/utils/array_view.h" -#include "gpuspatial/utils/queue_view.h" +#include "gpuspatial/geom/box.hpp" +#include "gpuspatial/geom/multi_point.hpp" +#include "gpuspatial/geom/multi_polygon.hpp" +#include "gpuspatial/geom/point.hpp" +#include "gpuspatial/geom/polygon.hpp" +#include "gpuspatial/utils/array_view.hpp" +#include "gpuspatial/utils/queue_view.hpp" #include @@ -31,29 +31,29 @@ namespace detail { template struct LaunchParamsPointQuery { - using box_t = Box>; - // Data structures of geometries1 - bool grouped; - ArrayView prefix_sum; // Only used when grouped - ArrayView reordered_indices; // Only used when grouped - ArrayView mbrs1; // MBR of each feature in geometries1 + using box_t = Box; + // Input + ArrayView rects; + ArrayView points; OptixTraversableHandle handle; - // Data structures of geometries2 - ArrayView points2; - // Output: Geom1 ID, Geom2 ID - QueueView> ids; + uint32_t* count; + // Output + QueueView rect_ids; + ArrayView point_ids; }; template struct LaunchParamsBoxQuery { - using box_t = Box>; + using box_t = Box; // Input - ArrayView mbrs1; - ArrayView mbrs2; + ArrayView rects1; + ArrayView rects2; // can be either geometries 1 or 2 OptixTraversableHandle handle; - // Output: Geom2 ID, Geom2 ID - QueueView> ids; + uint32_t* count; + // Output + QueueView rect1_ids; + ArrayView rect2_ids; }; /** @@ -67,12 +67,15 @@ struct LaunchParamsPolygonPointQuery { MultiPointArrayView multi_points; PointArrayView points; PolygonArrayView polygons; - ArrayView polygon_ids; // sorted - ArrayView> ids; + ArrayView uniq_polygon_ids; // sorted + index_t* query_point_ids; + index_t* query_polygon_ids; + size_t query_size; ArrayView seg_begins; ArrayView IMs; // intersection matrices OptixTraversableHandle handle; ArrayView aabb_poly_ids, aabb_ring_ids; + ArrayView> aabb_vertex_offsets; }; /** @@ -87,14 +90,16 @@ struct LaunchParamsPointMultiPolygonQuery { // Either MultiPointArrayView or PointArrayView will be used MultiPointArrayView multi_points; PointArrayView points; - ArrayView multi_polygon_ids; // sorted - ArrayView> ids; - ArrayView seg_begins; - ArrayView uniq_part_begins; + ArrayView uniq_multi_polygon_ids; // sorted + index_t* query_point_ids; + index_t* query_multi_polygon_ids; + size_t query_size; + ArrayView uniq_part_begins; // used to calculate z-index for parts // each query point has n elements of part_min_y and part_locations, n is # of parts ArrayView IMs; // intersection matrices OptixTraversableHandle handle; ArrayView aabb_multi_poly_ids, aabb_part_ids, aabb_ring_ids; + ArrayView> aabb_vertex_offsets; }; } // namespace detail diff --git a/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/index/detail/rt_engine.hpp b/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/rt/rt_engine.hpp similarity index 98% rename from c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/index/detail/rt_engine.hpp rename to c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/rt/rt_engine.hpp index d571feaa7..3b3019e46 100644 --- a/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/index/detail/rt_engine.hpp +++ b/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/rt/rt_engine.hpp @@ -16,7 +16,7 @@ // under the License. #pragma once -#include "gpuspatial/utils/array_view.h" +#include "gpuspatial/utils/array_view.hpp" #include "rmm/cuda_stream.hpp" #include "rmm/device_uvector.hpp" @@ -33,7 +33,6 @@ #define GPUSPATIAL_OPTIX_LAUNCH_PARAMS_NAME "params" namespace gpuspatial { -namespace details { /*! SBT record for a raygen program */ struct __align__(OPTIX_SBT_RECORD_ALIGNMENT) RaygenRecord { @@ -160,6 +159,9 @@ RTConfig get_default_rt_config(const std::string& ptx_root); class RTEngine { public: + RTEngine(const RTEngine&) = delete; + RTEngine& operator=(const RTEngine&) = delete; + RTEngine(); ~RTEngine(); @@ -201,5 +203,4 @@ class RTEngine { bool initialized_; }; -} // namespace details } // namespace gpuspatial diff --git a/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/array_view.h b/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/array_view.hpp similarity index 98% rename from c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/array_view.h rename to c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/array_view.hpp index f1d5fb487..da9339ae7 100644 --- a/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/array_view.h +++ b/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/array_view.hpp @@ -15,7 +15,7 @@ // specific language governing permissions and limitations // under the License. #pragma once -#include "gpuspatial/utils/cuda_utils.h" +#include "gpuspatial/utils/cuda_utils.hpp" #include namespace gpuspatial { diff --git a/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/cuda_utils.h b/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/cuda_utils.hpp similarity index 97% rename from c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/cuda_utils.h rename to c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/cuda_utils.hpp index 2f6941704..4cca08fd0 100644 --- a/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/cuda_utils.h +++ b/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/cuda_utils.hpp @@ -28,7 +28,7 @@ #else #define DEV_HOST -#define DEV_HOST_INLINE +#define DEV_HOST_INLINE inline #define DEV_INLINE #define CONST_STATIC_INIT(...) = __VA_ARGS__ diff --git a/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/doubledouble.h b/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/doubledouble.hpp similarity index 99% rename from c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/doubledouble.h rename to c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/doubledouble.hpp index 91c5adce8..9bf3c9267 100644 --- a/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/doubledouble.h +++ b/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/doubledouble.hpp @@ -68,7 +68,7 @@ #pragma once -#include "gpuspatial/utils/cuda_utils.h" +#include "gpuspatial/utils/cuda_utils.hpp" #include #include diff --git a/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/exception.h b/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/exception.hpp similarity index 95% rename from c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/exception.h rename to c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/exception.hpp index a35005ebe..ab6f174e7 100644 --- a/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/exception.h +++ b/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/exception.hpp @@ -53,7 +53,7 @@ inline void optixCheck(OptixResult res, const char* call, const char* file, std::stringstream ss; ss << "OptiX API call (" << call << ") failed with error " << optixGetErrorName(res) << " (" << file << ":" << line << ")"; - GPUSPATIAL_LOG_ERROR("Optix API error: {}", ss.str()); + GPUSPATIAL_LOG_ERROR("Optix API error: %s", ss.str()); throw GPUException(res, ss.str().c_str()); } } @@ -64,7 +64,7 @@ inline void cudaCheck(cudaError_t error, const char* call, const char* file, std::stringstream ss; ss << "CUDA API call (" << call << ") failed with error " << cudaGetErrorString(error) << " (" << file << ":" << line << ")"; - GPUSPATIAL_LOG_ERROR("CUDA API error: {}", ss.str()); + GPUSPATIAL_LOG_ERROR("CUDA API error: %s", ss.str()); throw GPUException(ss.str().c_str()); } } diff --git a/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/floating_point.h b/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/floating_point.hpp similarity index 99% rename from c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/floating_point.h rename to c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/floating_point.hpp index 9014a552b..6512fe40c 100644 --- a/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/floating_point.h +++ b/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/floating_point.hpp @@ -15,7 +15,7 @@ */ #pragma once -#include "gpuspatial/utils/cuda_utils.h" +#include "gpuspatial/utils/cuda_utils.hpp" #include #include diff --git a/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/gpu_timer.hpp b/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/gpu_timer.hpp index 33c8d47bc..1cec9359f 100644 --- a/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/gpu_timer.hpp +++ b/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/gpu_timer.hpp @@ -15,7 +15,7 @@ // specific language governing permissions and limitations // under the License. #pragma once -#include "gpuspatial/utils/exception.h" +#include "gpuspatial/utils/exception.hpp" #include namespace gpuspatial { diff --git a/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/helpers.h b/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/helpers.cuh similarity index 98% rename from c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/helpers.h rename to c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/helpers.cuh index 5fc1d54ff..99c02b38c 100644 --- a/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/helpers.h +++ b/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/helpers.cuh @@ -15,7 +15,7 @@ // specific language governing permissions and limitations // under the License. #pragma once -#include "gpuspatial/utils/cuda_utils.h" +#include "gpuspatial/utils/cuda_utils.hpp" #include #include diff --git a/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/launcher.h b/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/launcher.hpp similarity index 94% rename from c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/launcher.h rename to c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/launcher.hpp index 09c2c8aed..31c0b6a7d 100644 --- a/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/launcher.h +++ b/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/launcher.hpp @@ -15,8 +15,8 @@ // specific language governing permissions and limitations // under the License. #pragma once -#include "gpuspatial/utils/cuda_utils.h" -#include "gpuspatial/utils/exception.h" +#include "gpuspatial/utils/cuda_utils.hpp" +#include "gpuspatial/utils/exception.hpp" #include "rmm/cuda_stream_view.hpp" diff --git a/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/markers.hpp b/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/markers.hpp new file mode 100644 index 000000000..6cc62edb5 --- /dev/null +++ b/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/markers.hpp @@ -0,0 +1,145 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +#pragma once + +#include +#define DISABLE_NVTX_MARKERS + +#ifndef DISABLE_NVTX_MARKERS +#include +#endif + +namespace gpuspatial { + +struct Category { + static constexpr uint32_t KernelWorkitems = 1; + static constexpr uint32_t IntervalWorkitems = 2; +}; + +// Colors in ARGB format (Alpha, Red, Green, Blue) +struct Color { + static constexpr uint32_t Red = 0xFF880000; + static constexpr uint32_t Green = 0xFF008800; + static constexpr uint32_t Blue = 0xFF000088; + static constexpr uint32_t Yellow = 0xFFFFFF00; + static constexpr uint32_t Default = 0; +}; + +#ifndef DISABLE_NVTX_MARKERS + +struct Instrument { + // --------------------------------------------------------------------------- + // Helper: Create attributes correctly using constructors + // --------------------------------------------------------------------------- + static nvtx3::event_attributes create_attr(const char* msg, uint32_t color_val, + uint32_t category_val) { + // 1. Basic Message + nvtx3::event_attributes attr{msg}; + + // 2. Apply Color (if not default) + if (color_val != Color::Default) { + // Use nvtx3::rgb wrapping the uint32_t directly usually works, + // but if it fails, we assign to the internal color_type directly via the generic + // color wrapper + attr = nvtx3::event_attributes{msg, nvtx3::color{color_val}}; + } + + // 3. Apply Category (if valid) + // Note: We cannot "append" to an existing immutable object. + // We must construct with all arguments at once. + + if (color_val != Color::Default && category_val != 0) { + return nvtx3::event_attributes{msg, nvtx3::color{color_val}, + nvtx3::category{category_val}}; + } else if (color_val != Color::Default) { + return nvtx3::event_attributes{msg, nvtx3::color{color_val}}; + } else if (category_val != 0) { + return nvtx3::event_attributes{msg, nvtx3::category{category_val}}; + } + + return attr; + } + + // --------------------------------------------------------------------------- + // Instant Markers + // --------------------------------------------------------------------------- + static void Mark(const char* message, uint32_t color = Color::Default, + uint32_t category = 0) { + nvtx3::mark(create_attr(message, color, category)); + } + + static void MarkInt(int64_t value, const char* message, uint32_t color = Color::Default, + uint32_t category = 0) { + // Construct with payload immediately + // Note: If you need color+category+payload, the constructor list gets long. + // This covers the most common case: Message + Payload + if (color == Color::Default && category == 0) { + nvtx3::event_attributes attr{message, nvtx3::payload{value}}; + nvtx3::mark(attr); + } else { + // Fallback: manually construct complex attribute + // Most NVTX3 versions support {msg, color, payload, category} in any order + nvtx3::event_attributes attr{message, nvtx3::color{color}, + nvtx3::category{category}, nvtx3::payload{value}}; + nvtx3::mark(attr); + } + } + + static void MarkWorkitems(uint64_t items, const char* message = "Workitems") { + nvtx3::event_attributes attr{message, nvtx3::payload{items}, + nvtx3::category{Category::KernelWorkitems}}; + nvtx3::mark(attr); + } + + // --------------------------------------------------------------------------- + // Scoped Ranges (RAII) + // --------------------------------------------------------------------------- + struct Range { + nvtx3::scoped_range range; + + // Standard Range + explicit Range(const char* message, uint32_t color = Color::Default, + uint32_t category = 0) + : range(Instrument::create_attr(message, color, category)) {} + + // Payload Range (for workitems/intervals) + explicit Range(const char* message, uint64_t payload, + uint32_t category = Category::IntervalWorkitems) + : range(nvtx3::event_attributes{message, nvtx3::payload{payload}, + nvtx3::category{category}}) {} + }; +}; + +#else + +// ----------------------------------------------------------------------------- +// No-Op Implementation +// ----------------------------------------------------------------------------- +struct Instrument { + static inline void Mark(const char*, uint32_t = 0, uint32_t = 0) {} + static inline void MarkInt(int64_t, const char*, uint32_t = 0, uint32_t = 0) {} + static inline void MarkWorkitems(uint64_t, const char*) {} + + struct Range { + explicit Range(const char*, uint32_t = 0, uint32_t = 0) {} + explicit Range(const char*, uint64_t, uint32_t = 0) {} + }; +}; + +#endif // DISABLE_NVTX_MARKERS + +} // namespace gpuspatial diff --git a/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/mem_utils.hpp b/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/mem_utils.hpp index 1b36c934f..779387676 100644 --- a/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/mem_utils.hpp +++ b/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/mem_utils.hpp @@ -15,7 +15,7 @@ // specific language governing permissions and limitations // under the License. #pragma once -#include "gpuspatial/utils/exception.h" +#include "gpuspatial/utils/exception.hpp" #include "rmm/cuda_stream_view.hpp" diff --git a/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/morton_code.h b/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/morton_code.hpp similarity index 98% rename from c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/morton_code.h rename to c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/morton_code.hpp index ded74f02b..0867ed007 100644 --- a/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/morton_code.h +++ b/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/morton_code.hpp @@ -19,7 +19,7 @@ */ #pragma once -#include "gpuspatial/utils/cuda_utils.h" +#include "gpuspatial/utils/cuda_utils.hpp" #include #include diff --git a/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/pinned_vector.h b/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/pinned_vector.hpp similarity index 99% rename from c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/pinned_vector.h rename to c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/pinned_vector.hpp index 73ac54d01..2c21ea5e4 100644 --- a/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/pinned_vector.h +++ b/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/pinned_vector.hpp @@ -15,7 +15,7 @@ // specific language governing permissions and limitations // under the License. #pragma once -#include "gpuspatial/utils/exception.h" +#include "gpuspatial/utils/exception.hpp" #include // For CUDA memory management functions diff --git a/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/queue.h b/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/queue.hpp similarity index 95% rename from c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/queue.h rename to c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/queue.hpp index 29beac229..c1921dca3 100644 --- a/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/queue.h +++ b/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/queue.hpp @@ -15,8 +15,8 @@ // specific language governing permissions and limitations // under the License. #pragma once -#include "gpuspatial/utils/array_view.h" -#include "gpuspatial/utils/queue_view.h" +#include "gpuspatial/utils/array_view.hpp" +#include "gpuspatial/utils/queue_view.hpp" #include "rmm/cuda_stream_view.hpp" #include "rmm/device_scalar.hpp" @@ -41,6 +41,7 @@ class Queue { if (counter_ == nullptr) { counter_ = std::make_unique>(stream); } + Clear(stream); } void Clear(const rmm::cuda_stream_view& stream) { diff --git a/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/queue_view.h b/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/queue_view.hpp similarity index 96% rename from c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/queue_view.h rename to c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/queue_view.hpp index e4b10ef9d..f907bff57 100644 --- a/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/queue_view.h +++ b/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/queue_view.hpp @@ -16,8 +16,8 @@ // under the License. #pragma once -#include "gpuspatial/utils/array_view.h" -#include "gpuspatial/utils/cuda_utils.h" +#include "gpuspatial/utils/array_view.hpp" +#include "gpuspatial/utils/cuda_utils.hpp" #include diff --git a/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/stopwatch.h b/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/stopwatch.hpp similarity index 100% rename from c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/stopwatch.h rename to c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/stopwatch.hpp diff --git a/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/thread_pool.h b/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/thread_pool.hpp similarity index 100% rename from c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/thread_pool.h rename to c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/thread_pool.hpp diff --git a/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/type_traits.h b/c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/type_traits.hpp similarity index 100% rename from c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/type_traits.h rename to c/sedona-libgpuspatial/libgpuspatial/include/gpuspatial/utils/type_traits.hpp diff --git a/c/sedona-libgpuspatial/libgpuspatial/src/gpuspatial_c.cc b/c/sedona-libgpuspatial/libgpuspatial/src/gpuspatial_c.cc index 58ef354ab..e8494f3e2 100644 --- a/c/sedona-libgpuspatial/libgpuspatial/src/gpuspatial_c.cc +++ b/c/sedona-libgpuspatial/libgpuspatial/src/gpuspatial_c.cc @@ -14,157 +14,342 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. + #include "gpuspatial/gpuspatial_c.h" -#include "gpuspatial/index/spatial_joiner.hpp" +#include "gpuspatial/index/rt_spatial_index.hpp" +#include "gpuspatial/index/spatial_index.hpp" +#include "gpuspatial/mem/memory_manager.hpp" +#include "gpuspatial/refine/rt_spatial_refiner.hpp" +#include "gpuspatial/rt/rt_engine.hpp" +#include "gpuspatial/utils/exception.hpp" #include +#include +#include #include -#define GPUSPATIAL_ERROR_MSG_BUFFER_SIZE (1024) -struct GpuSpatialJoinerExporter { - static void Export(std::unique_ptr& idx, - struct GpuSpatialJoiner* out) { - out->private_data = idx.release(); - out->init = &CInit; +// ----------------------------------------------------------------------------- +// INTERNAL HELPERS +// ----------------------------------------------------------------------------- +// This is what the private_data points to for the public C interfaces +template +struct GpuSpatialWrapper { + T payload; + std::string last_error; // Pointer to std::string to store last error message +}; + +// The unified error handling wrapper +// Func: The lambda containing the logic +template +int SafeExecute(GpuSpatialWrapper* wrapper, Func&& func) { + try { + func(); + wrapper->last_error.clear(); + return 0; + } catch (const std::exception& e) { + wrapper->last_error = std::string(e.what()); + return EINVAL; + } catch (...) { + wrapper->last_error = "Unknown internal error"; + return EINVAL; + } +} + +// ----------------------------------------------------------------------------- +// IMPLEMENTATION +// ----------------------------------------------------------------------------- + +struct GpuSpatialRuntimeExporter { + struct Payload { + std::shared_ptr rt_engine; + int device_id; + }; + + using private_data_t = GpuSpatialWrapper; + static void Export(struct GpuSpatialRuntime* out) { + private_data_t* private_data = + new private_data_t{Payload{std::make_shared()}, ""}; + out->init = CInit; + out->release = CRelease; + out->get_last_error = CGetLastError; + out->private_data = private_data; + } + + static int CInit(GpuSpatialRuntime* self, GpuSpatialRuntimeConfig* config) { + return SafeExecute(static_cast(self->private_data), [&] { + std::string ptx_root(config->ptx_root); + auto rt_config = gpuspatial::get_default_rt_config(ptx_root); + + GPUSPATIAL_LOG_INFO("Initializing GpuSpatialRuntime on device %d, PTX root %s", + config->device_id, config->ptx_root); + + CUDA_CHECK(cudaSetDevice(config->device_id)); + + gpuspatial::MemoryManager::instance().Init(config->use_cuda_memory_pool, + config->cuda_memory_pool_init_precent); + + static_cast(self->private_data) + ->payload.rt_engine->Init(rt_config); + }); + } + + static void CRelease(GpuSpatialRuntime* self) { + gpuspatial::MemoryManager::instance().Shutdown(); + delete static_cast(self->private_data); + self->private_data = nullptr; + } + + static const char* CGetLastError(GpuSpatialRuntime* self) { + auto* private_data = static_cast(self->private_data); + return private_data->last_error.c_str(); + } +}; + +void GpuSpatialRuntimeCreate(struct GpuSpatialRuntime* runtime) { + GpuSpatialRuntimeExporter::Export(runtime); +} + +using runtime_data_t = GpuSpatialRuntimeExporter::private_data_t; + +struct GpuSpatialIndexFloat2DExporter { + using scalar_t = float; + static constexpr int n_dim = 2; + using self_t = SedonaFloatIndex2D; + using spatial_index_t = gpuspatial::SpatialIndex; + + struct Payload { + std::unique_ptr index; + runtime_data_t* rdata; + }; + + struct ResultBuffer { + std::vector build_indices; + std::vector probe_indices; + ResultBuffer() = default; + + ResultBuffer(const ResultBuffer&) = delete; + ResultBuffer& operator=(const ResultBuffer&) = delete; + + ResultBuffer(ResultBuffer&&) = default; + ResultBuffer& operator=(ResultBuffer&&) = default; + }; + + using private_data_t = GpuSpatialWrapper; + using context_t = GpuSpatialWrapper; + + static void Export(const struct GpuSpatialIndexConfig* config, + struct SedonaFloatIndex2D* out) { + auto* rdata = static_cast(config->runtime->private_data); + + gpuspatial::RTSpatialIndexConfig index_config; + + index_config.rt_engine = rdata->payload.rt_engine; + index_config.concurrency = config->concurrency; + + // Create SpatialIndex may involve GPU operations, set device here + CUDA_CHECK(cudaSetDevice(rdata->payload.device_id)); + + auto uniq_index = gpuspatial::CreateRTSpatialIndex(index_config); + out->clear = &CClear; - out->push_build = &CPushBuild; - out->finish_building = &CFinishBuilding; out->create_context = &CCreateContext; out->destroy_context = &CDestroyContext; - out->push_stream = &CPushStream; + out->push_build = &CPushBuild; + out->finish_building = &CFinishBuilding; + out->probe = &CProbe; out->get_build_indices_buffer = &CGetBuildIndicesBuffer; - out->get_stream_indices_buffer = &CGetStreamIndicesBuffer; + out->get_probe_indices_buffer = &CGetProbeIndicesBuffer; + out->get_last_error = &CGetLastError; + out->context_get_last_error = &CContextGetLastError; out->release = &CRelease; - out->last_error = new char[GPUSPATIAL_ERROR_MSG_BUFFER_SIZE]; - } - - static int CInit(struct GpuSpatialJoiner* self, struct GpuSpatialJoinerConfig* config) { - int err = 0; - auto* joiner = static_cast(self->private_data); - try { - gpuspatial::InitSpatialJoiner(joiner, config->ptx_root, config->concurrency); - } catch (const std::exception& e) { - int len = - std::min(strlen(e.what()), (size_t)(GPUSPATIAL_ERROR_MSG_BUFFER_SIZE - 1)); - auto* last_error = const_cast(self->last_error); - strncpy(last_error, e.what(), len); - last_error[len] = '\0'; - err = EINVAL; - } - return err; - } - - static void CCreateContext(struct GpuSpatialJoiner* self, - struct GpuSpatialJoinerContext* context) { - auto* joiner = static_cast(self->private_data); - context->private_data = new std::shared_ptr(joiner->CreateContext()); - context->last_error = new char[GPUSPATIAL_ERROR_MSG_BUFFER_SIZE]; - context->build_indices = new std::vector(); - context->stream_indices = new std::vector(); - } - - static void CDestroyContext(struct GpuSpatialJoinerContext* context) { - delete (std::shared_ptr*)context->private_data; - delete[] context->last_error; - delete (std::vector*)context->build_indices; - delete (std::vector*)context->stream_indices; + out->private_data = new private_data_t{Payload{std::move(uniq_index), rdata}, ""}; + } + + static void CCreateContext(struct SedonaSpatialIndexContext* context) { + context->private_data = new context_t(); + } + + static void CDestroyContext(struct SedonaSpatialIndexContext* context) { + delete static_cast(context->private_data); context->private_data = nullptr; - context->last_error = nullptr; - context->build_indices = nullptr; - context->stream_indices = nullptr; - } - - static void CClear(struct GpuSpatialJoiner* self) { - auto* joiner = static_cast(self->private_data); - joiner->Clear(); - } - - static int CPushBuild(struct GpuSpatialJoiner* self, const struct ArrowSchema* schema, - const struct ArrowArray* array, int64_t offset, int64_t length) { - auto* joiner = static_cast(self->private_data); - int err = 0; - try { - joiner->PushBuild(schema, array, offset, length); - } catch (const std::exception& e) { - int len = - std::min(strlen(e.what()), (size_t)(GPUSPATIAL_ERROR_MSG_BUFFER_SIZE - 1)); - auto* last_error = const_cast(self->last_error); - strncpy(last_error, e.what(), len); - last_error[len] = '\0'; - err = EINVAL; - } - return err; - } - - static int CFinishBuilding(struct GpuSpatialJoiner* self) { - auto* joiner = static_cast(self->private_data); - int err = 0; - try { - joiner->FinishBuilding(); - } catch (const std::exception& e) { - int len = - std::min(strlen(e.what()), (size_t)(GPUSPATIAL_ERROR_MSG_BUFFER_SIZE - 1)); - auto* last_error = const_cast(self->last_error); - strncpy(last_error, e.what(), len); - last_error[len] = '\0'; - err = EINVAL; - } - return err; - } - - static int CPushStream(struct GpuSpatialJoiner* self, - struct GpuSpatialJoinerContext* context, - const struct ArrowSchema* schema, const struct ArrowArray* array, - int64_t offset, int64_t length, - enum GpuSpatialPredicate predicate, int32_t array_index_offset) { - auto* joiner = static_cast(self->private_data); - auto* private_data = - (std::shared_ptr*)context->private_data; - int err = 0; - try { - joiner->PushStream(private_data->get(), schema, array, offset, length, - static_cast(predicate), - static_cast*>(context->build_indices), - static_cast*>(context->stream_indices), - array_index_offset); - } catch (const std::exception& e) { - int len = - std::min(strlen(e.what()), (size_t)(GPUSPATIAL_ERROR_MSG_BUFFER_SIZE - 1)); - strncpy((char*)context->last_error, e.what(), len); - ((char*)context->last_error)[len] = '\0'; - err = EINVAL; - } - return err; - } - - static void CGetBuildIndicesBuffer(struct GpuSpatialJoinerContext* context, - void** build_indices, + } + + static int CClear(self_t* self) { + return SafeExecute(static_cast(self->private_data), + [=] { use_index(self).Clear(); }); + } + + static int CPushBuild(self_t* self, const float* buf, uint32_t n_rects) { + return SafeExecute(static_cast(self->private_data), [&] { + auto* rects = reinterpret_cast(buf); + use_index(self).PushBuild(rects, n_rects); + }); + } + + static int CFinishBuilding(self_t* self) { + return SafeExecute(static_cast(self->private_data), + [&] { use_index(self).FinishBuilding(); }); + } + + static int CProbe(self_t* self, SedonaSpatialIndexContext* context, const float* buf, + uint32_t n_rects) { + return SafeExecute(static_cast(context->private_data), [&] { + auto* rects = reinterpret_cast(buf); + auto& buff = static_cast(context->private_data)->payload; + use_index(self).Probe(rects, n_rects, &buff.build_indices, &buff.probe_indices); + }); + } + + static void CGetBuildIndicesBuffer(struct SedonaSpatialIndexContext* context, + uint32_t** build_indices, uint32_t* build_indices_length) { - auto* vec = static_cast*>(context->build_indices); + auto* ctx = static_cast(context->private_data); + *build_indices = ctx->payload.build_indices.data(); + *build_indices_length = ctx->payload.build_indices.size(); + } - *build_indices = vec->data(); - *build_indices_length = vec->size(); + static void CGetProbeIndicesBuffer(struct SedonaSpatialIndexContext* context, + uint32_t** probe_indices, + uint32_t* probe_indices_length) { + auto* ctx = static_cast(context->private_data); + *probe_indices = ctx->payload.probe_indices.data(); + *probe_indices_length = ctx->payload.probe_indices.size(); } - static void CGetStreamIndicesBuffer(struct GpuSpatialJoinerContext* context, - void** stream_indices, - uint32_t* stream_indices_length) { - auto* vec = static_cast*>(context->stream_indices); + static const char* CGetLastError(self_t* self) { + auto* private_data = static_cast(self->private_data); + return private_data->last_error.c_str(); + } - *stream_indices = vec->data(); - *stream_indices_length = vec->size(); + static const char* CContextGetLastError(SedonaSpatialIndexContext* self) { + auto* private_data = static_cast(self->private_data); + return private_data->last_error.c_str(); } - static void CRelease(struct GpuSpatialJoiner* self) { - delete[] self->last_error; - auto* joiner = static_cast(self->private_data); - delete joiner; + static void CRelease(self_t* self) { + delete static_cast(self->private_data); self->private_data = nullptr; - self->last_error = nullptr; + } + + static spatial_index_t& use_index(self_t* self) { + auto* private_data = static_cast(self->private_data); + auto* r_data = private_data->payload.rdata; + + CUDA_CHECK(cudaSetDevice(r_data->payload.device_id)); + return *(private_data->payload.index); } }; -void GpuSpatialJoinerCreate(struct GpuSpatialJoiner* joiner) { - auto idx = gpuspatial::CreateSpatialJoiner(); - GpuSpatialJoinerExporter::Export(idx, joiner); +int GpuSpatialIndexFloat2DCreate(struct SedonaFloatIndex2D* index, + const struct GpuSpatialIndexConfig* config) { + try { + GpuSpatialIndexFloat2DExporter::Export(config, index); + } catch (std::exception& e) { + GPUSPATIAL_LOG_ERROR("Failed to create GpuSpatialIndexFloat2D: %s", e.what()); + return EINVAL; + } + return 0; +} + +struct GpuSpatialRefinerExporter { + struct Payload { + std::unique_ptr refiner; + runtime_data_t* rdata; + }; + using private_data_t = GpuSpatialWrapper; + + static void Export(const GpuSpatialRefinerConfig* config, + struct SedonaSpatialRefiner* out) { + auto* rdata = static_cast(config->runtime->private_data); + + gpuspatial::RTSpatialRefinerConfig refiner_config; + + refiner_config.rt_engine = rdata->payload.rt_engine; + refiner_config.concurrency = config->concurrency; + refiner_config.compact = config->compress_bvh; + refiner_config.pipeline_batches = config->pipeline_batches; + + // Create Refinner may involve GPU operations, set device here + CUDA_CHECK(cudaSetDevice(rdata->payload.device_id)); + + auto refiner = gpuspatial::CreateRTSpatialRefiner(refiner_config); + + out->clear = &CClear; + out->push_build = &CPushBuild; + out->finish_building = &CFinishBuilding; + out->refine_loaded = &CRefineLoaded; + out->refine = &CRefine; + out->get_last_error = &CGetLastError; + out->release = &CRelease; + out->private_data = new private_data_t{Payload{std::move(refiner), rdata}, ""}; + } + + static int CClear(SedonaSpatialRefiner* self) { + return SafeExecute(static_cast(self->private_data), + [&] { use_refiner(self).Clear(); }); + } + + static int CPushBuild(SedonaSpatialRefiner* self, const ArrowSchema* build_schema, + const ArrowArray* build_array) { + return SafeExecute(static_cast(self->private_data), + [&] { use_refiner(self).PushBuild(build_schema, build_array); }); + } + + static int CFinishBuilding(SedonaSpatialRefiner* self) { + return SafeExecute(static_cast(self->private_data), + [&] { use_refiner(self).FinishBuilding(); }); + } + + static int CRefineLoaded(SedonaSpatialRefiner* self, const ArrowSchema* probe_schema, + const ArrowArray* probe_array, + SedonaSpatialRelationPredicate predicate, + uint32_t* build_indices, uint32_t* probe_indices, + uint32_t indices_size, uint32_t* new_indices_size) { + return SafeExecute(static_cast(self->private_data), [&] { + *new_indices_size = use_refiner(self).Refine( + probe_schema, probe_array, static_cast(predicate), + build_indices, probe_indices, indices_size); + }); + } + + static int CRefine(SedonaSpatialRefiner* self, const ArrowSchema* schema1, + const ArrowArray* array1, const ArrowSchema* schema2, + const ArrowArray* array2, SedonaSpatialRelationPredicate predicate, + uint32_t* indices1, uint32_t* indices2, uint32_t indices_size, + uint32_t* new_indices_size) { + return SafeExecute(static_cast(self->private_data), [&] { + *new_indices_size = use_refiner(self).Refine( + schema1, array1, schema2, array2, static_cast(predicate), + indices1, indices2, indices_size); + }); + } + + static const char* CGetLastError(SedonaSpatialRefiner* self) { + auto* private_data = static_cast(self->private_data); + return private_data->last_error.c_str(); + } + + static void CRelease(SedonaSpatialRefiner* self) { + delete static_cast(self->private_data); + self->private_data = nullptr; + } + + static gpuspatial::SpatialRefiner& use_refiner(SedonaSpatialRefiner* self) { + auto* private_data = static_cast(self->private_data); + auto* r_data = private_data->payload.rdata; + + CUDA_CHECK(cudaSetDevice(r_data->payload.device_id)); + return *(private_data->payload.refiner); + } +}; + +int GpuSpatialRefinerCreate(SedonaSpatialRefiner* refiner, + const GpuSpatialRefinerConfig* config) { + try { + GpuSpatialRefinerExporter::Export(config, refiner); + } catch (std::exception& e) { + GPUSPATIAL_LOG_ERROR("Failed to create GpuSpatialRefiner: %s", e.what()); + return EINVAL; + } + return 0; } diff --git a/c/sedona-libgpuspatial/libgpuspatial/src/memory_manager.cc b/c/sedona-libgpuspatial/libgpuspatial/src/memory_manager.cc new file mode 100644 index 000000000..fdf66e700 --- /dev/null +++ b/c/sedona-libgpuspatial/libgpuspatial/src/memory_manager.cc @@ -0,0 +1,128 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +#include "gpuspatial/mem/memory_manager.hpp" +#include "gpuspatial/utils/logger.hpp" + +#if defined(_WIN32) +#include +#elif defined(__linux__) +#include +#else // POSIX (BSD, Solaris, etc.) +#include +#endif +namespace gpuspatial { +namespace detail { +inline long long get_free_physical_memory() { +#if defined(_WIN32) + // --- Windows --- + MEMORYSTATUSEX status; + status.dwLength = sizeof(status); + if (GlobalMemoryStatusEx(&status)) { + return (long long)status.ullAvailPhys; + } + return 0; + +#elif defined(__linux__) + // --- Linux (sysinfo) --- + struct sysinfo info; + if (sysinfo(&info) == 0) { + return (long long)info.freeram * (long long)info.mem_unit; + } + return 0; + +#else + // --- Generic POSIX --- + // _SC_AVPHYS_PAGES: The number of physical memory pages not currently in use. + long pages = sysconf(_SC_AVPHYS_PAGES); + long page_size = sysconf(_SC_PAGESIZE); + + if (pages > 0 && page_size > 0) { + return (long long)pages * (long long)page_size; + } + return 0; +#endif +} +} // namespace detail + +MemoryManager& MemoryManager::instance() { + static MemoryManager instance; + return instance; +} + +MemoryManager::~MemoryManager() { Shutdown(); } + +void MemoryManager::Shutdown() { + if (is_initialized_) { + rmm::mr::set_current_device_resource(nullptr); + active_resource_.reset(); + pool_mr_.reset(); + cuda_mr_.reset(); + raw_tracker_ptr_ = nullptr; + is_initialized_ = false; + } +} + +void MemoryManager::Init(bool use_pool, int init_pool_precent) { + if (is_initialized_) { + GPUSPATIAL_LOG_WARN( + "MemoryManager is already initialized. Skipping re-initialization."); + return; + } + + cuda_mr_ = std::make_unique(); + use_pool_ = use_pool; + + if (use_pool_) { + auto safe_precent = std::max(0, std::min(init_pool_precent, 100)); + auto pool_bytes = rmm::percent_of_free_device_memory(safe_precent); + + GPUSPATIAL_LOG_INFO("Creating RMM pool memory resource with size %zu MB", + pool_bytes / 1024 / 1024); + + pool_mr_ = std::make_unique(cuda_mr_.get(), pool_bytes); + active_resource_ = std::make_unique(pool_mr_.get()); + } else { + active_resource_ = std::make_unique(cuda_mr_.get()); + } + + raw_tracker_ptr_ = active_resource_.get(); + + rmm::mr::set_current_device_resource(active_resource_.get()); + is_initialized_ = true; +} + +size_t MemoryManager::get_available_device_memory() const { + auto avail_bytes = rmm::available_device_memory().first; + if (!is_initialized_ || !use_pool_) { + return avail_bytes; + } + + // --- POOL STRATEGY --- + auto* tracker = static_cast(raw_tracker_ptr_); + size_t used = tracker->get_allocated_bytes(); + + // Safety Buffer: 5% of TOTAL capacity (not just pool capacity) + size_t safe_limit = static_cast(avail_bytes * 0.95); + + return (used < safe_limit) ? (safe_limit - used) : 0; +} + +size_t MemoryManager::get_available_host_memory() { + return detail::get_free_physical_memory(); +} +} // namespace gpuspatial diff --git a/c/sedona-libgpuspatial/libgpuspatial/src/relate_engine.cu b/c/sedona-libgpuspatial/libgpuspatial/src/relate_engine.cu index da978012c..db081da22 100644 --- a/c/sedona-libgpuspatial/libgpuspatial/src/relate_engine.cu +++ b/c/sedona-libgpuspatial/libgpuspatial/src/relate_engine.cu @@ -14,19 +14,21 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -#include "gpuspatial/index/detail/launch_parameters.h" -#include "gpuspatial/index/geometry_grouper.hpp" -#include "gpuspatial/index/relate_engine.cuh" -#include "gpuspatial/relate/predicate.cuh" -#include "gpuspatial/relate/relate.cuh" -#include "gpuspatial/utils/array_view.h" -#include "gpuspatial/utils/helpers.h" -#include "gpuspatial/utils/launcher.h" +#include "gpuspatial/mem/memory_manager.hpp" +#include "gpuspatial/relate/predicate.hpp" +#include "gpuspatial/relate/relate.hpp" +#include "gpuspatial/relate/relate_engine.cuh" +#include "gpuspatial/rt/launch_parameters.cuh" +#include "gpuspatial/utils/array_view.hpp" +#include "gpuspatial/utils/helpers.cuh" +#include "gpuspatial/utils/launcher.hpp" #include "gpuspatial/utils/logger.hpp" -#include "gpuspatial/utils/queue.h" #include "rt/shaders/shader_id.hpp" +#include +#include #include "rmm/cuda_stream_view.hpp" +#include "rmm/device_scalar.hpp" #include "rmm/exec_policy.hpp" #include @@ -93,6 +95,92 @@ DEV_HOST_INLINE bool EvaluatePredicate(Predicate p, int32_t im) { } return false; } + +template +uint32_t ComputeNumAabbs(const rmm::cuda_stream_view& stream, + const PolygonArrayView& polygons, + ArrayView polygon_ids, int segs_per_aabb) { + auto n_polygons = polygon_ids.size(); + + rmm::device_uvector n_aabbs(n_polygons, stream); + auto* p_n_aabbs = n_aabbs.data(); + + LaunchKernel(stream, [=] __device__() { + using WarpReduce = cub::WarpReduce; + __shared__ WarpReduce::TempStorage temp_storage[MAX_BLOCK_SIZE / 32]; + auto lane = threadIdx.x % 32; + auto warp_id = threadIdx.x / 32; + auto global_warp_id = TID_1D / 32; + auto n_warps = TOTAL_THREADS_1D / 32; + + for (auto i = global_warp_id; i < n_polygons; i += n_warps) { + auto id = polygon_ids[i]; + const auto& polygon = polygons[id]; + uint32_t total_segs = 0; + + for (auto ring = lane; ring < polygon.num_rings(); ring += 32) { + total_segs += + (polygon.get_ring(ring).num_segments() + segs_per_aabb - 1) / segs_per_aabb; + } + total_segs = WarpReduce(temp_storage[warp_id]).Sum(total_segs); + if (lane == 0) { + p_n_aabbs[i] = total_segs; + } + } + }); + return thrust::reduce(rmm::exec_policy_nosync(stream), n_aabbs.begin(), n_aabbs.end()); +} + +template +uint32_t ComputeNumAabbs(const rmm::cuda_stream_view& stream, + const MultiPolygonArrayView& multi_polygons, + ArrayView multi_polygon_ids, int segs_per_aabb) { + auto n_multi_polygons = multi_polygon_ids.size(); + rmm::device_uvector n_aabbs(n_multi_polygons, stream); + auto* p_n_aabbs = n_aabbs.data(); + + LaunchKernel(stream, [=] __device__() { + using WarpReduce = cub::WarpReduce; + __shared__ WarpReduce::TempStorage temp_storage[MAX_BLOCK_SIZE / 32]; + auto lane = threadIdx.x % 32; + auto warp_id = threadIdx.x / 32; + auto global_warp_id = TID_1D / 32; + auto n_warps = TOTAL_THREADS_1D / 32; + + for (auto i = global_warp_id; i < n_multi_polygons; i += n_warps) { + auto id = multi_polygon_ids[i]; + const auto& multi_polygon = multi_polygons[id]; + + uint32_t multipoly_aabb_count = 0; + + for (int part_idx = 0; part_idx < multi_polygon.num_polygons(); part_idx++) { + auto polygon = multi_polygon.get_polygon(part_idx); + + // Local accumulator for this thread + uint32_t thread_aabb_count = 0; + + for (auto ring = lane; ring < polygon.num_rings(); ring += 32) { + auto n_segs = polygon.get_ring(ring).num_segments(); + + thread_aabb_count += (n_segs + segs_per_aabb - 1) / segs_per_aabb; + } + + // Reduce across the warp to get total AABBs for this polygon (part) + uint32_t part_total = WarpReduce(temp_storage[warp_id]).Sum(thread_aabb_count); + + // Add this part's total to the multi-polygon accumulator + if (lane == 0) { + multipoly_aabb_count += part_total; + } + } + + if (lane == 0) { + p_n_aabbs[i] = multipoly_aabb_count; + } + } + }); + return thrust::reduce(rmm::exec_policy_nosync(stream), n_aabbs.begin(), n_aabbs.end()); +} } // namespace detail template @@ -102,48 +190,49 @@ RelateEngine::RelateEngine( template RelateEngine::RelateEngine( - const DeviceGeometries* geoms1, const details::RTEngine* rt_engine) + const DeviceGeometries* geoms1, const RTEngine* rt_engine) : geoms1_(geoms1), rt_engine_(rt_engine) {} template void RelateEngine::Evaluate( const rmm::cuda_stream_view& stream, const DeviceGeometries& geoms2, - Predicate predicate, Queue>& ids) { + Predicate predicate, rmm::device_uvector& ids1, + rmm::device_uvector& ids2) { switch (geoms2.get_geometry_type()) { case GeometryType::kPoint: { using geom2_array_view_t = PointArrayView; Evaluate(stream, geoms2.template GetGeometryArrayView(), - predicate, ids); + predicate, ids1, ids2); break; } case GeometryType::kMultiPoint: { using geom2_array_view_t = MultiPointArrayView; Evaluate(stream, geoms2.template GetGeometryArrayView(), - predicate, ids); + predicate, ids1, ids2); break; } case GeometryType::kLineString: { using geom2_array_view_t = LineStringArrayView; Evaluate(stream, geoms2.template GetGeometryArrayView(), - predicate, ids); + predicate, ids1, ids2); break; } case GeometryType::kMultiLineString: { using geom2_array_view_t = MultiLineStringArrayView; Evaluate(stream, geoms2.template GetGeometryArrayView(), - predicate, ids); + predicate, ids1, ids2); break; } case GeometryType::kPolygon: { using geom2_array_view_t = PolygonArrayView; Evaluate(stream, geoms2.template GetGeometryArrayView(), - predicate, ids); + predicate, ids1, ids2); break; } case GeometryType::kMultiPolygon: { using geom2_array_view_t = MultiPolygonArrayView; Evaluate(stream, geoms2.template GetGeometryArrayView(), - predicate, ids); + predicate, ids1, ids2); break; } default: @@ -153,44 +242,46 @@ void RelateEngine::Evaluate( template template -void RelateEngine::Evaluate( - const rmm::cuda_stream_view& stream, const GEOM2_ARRAY_VIEW_T& geom_array2, - Predicate predicate, Queue>& ids) { +void RelateEngine::Evaluate(const rmm::cuda_stream_view& stream, + const GEOM2_ARRAY_VIEW_T& geom_array2, + Predicate predicate, + rmm::device_uvector& ids1, + rmm::device_uvector& ids2) { switch (geoms1_->get_geometry_type()) { case GeometryType::kPoint: { using geom1_array_view_t = PointArrayView; Evaluate(stream, geoms1_->template GetGeometryArrayView(), - geom_array2, predicate, ids); + geom_array2, predicate, ids1, ids2); break; } case GeometryType::kMultiPoint: { using geom1_array_view_t = MultiPointArrayView; Evaluate(stream, geoms1_->template GetGeometryArrayView(), - geom_array2, predicate, ids); + geom_array2, predicate, ids1, ids2); break; } case GeometryType::kLineString: { using geom1_array_view_t = LineStringArrayView; Evaluate(stream, geoms1_->template GetGeometryArrayView(), - geom_array2, predicate, ids); + geom_array2, predicate, ids1, ids2); break; } case GeometryType::kMultiLineString: { using geom1_array_view_t = MultiLineStringArrayView; Evaluate(stream, geoms1_->template GetGeometryArrayView(), - geom_array2, predicate, ids); + geom_array2, predicate, ids1, ids2); break; } case GeometryType::kPolygon: { using geom1_array_view_t = PolygonArrayView; Evaluate(stream, geoms1_->template GetGeometryArrayView(), - geom_array2, predicate, ids); + geom_array2, predicate, ids1, ids2); break; } case GeometryType::kMultiPolygon: { using geom1_array_view_t = MultiPolygonArrayView; Evaluate(stream, geoms1_->template GetGeometryArrayView(), - geom_array2, predicate, ids); + geom_array2, predicate, ids1, ids2); break; } default: @@ -200,11 +291,14 @@ void RelateEngine::Evaluate( template template -void RelateEngine::Evaluate( - const rmm::cuda_stream_view& stream, const GEOM1_ARRAY_VIEW_T& geom_array1, - const GEOM2_ARRAY_VIEW_T& geom_array2, Predicate predicate, - Queue>& ids) { - size_t ids_size = ids.size(stream); +void RelateEngine::Evaluate(const rmm::cuda_stream_view& stream, + const GEOM1_ARRAY_VIEW_T& geom_array1, + const GEOM2_ARRAY_VIEW_T& geom_array2, + Predicate predicate, + rmm::device_uvector& ids1, + rmm::device_uvector& ids2) { + assert(ids1.size() == ids2.size()); + size_t ids_size = ids1.size(); GPUSPATIAL_LOG_INFO( "Refine with generic kernel, geom1 %zu, geom2 %zu, predicate %s, result size %zu", geom_array1.size(), geom_array2.size(), PredicateToString(predicate), ids_size); @@ -219,20 +313,24 @@ void RelateEngine::Evaluate( GPUSPATIAL_LOG_WARN( "Evaluate Polygon-Polygon relate with the GPU, which is not well-tested and the performance may be poor."); } - auto end = thrust::remove_if( - rmm::exec_policy_nosync(stream), ids.data(), ids.data() + ids_size, - [=] __device__(const thrust::pair& pair) { - auto geom1_id = pair.first; - auto geom2_id = pair.second; - const auto& geom1 = geom_array1[geom1_id]; - const auto& geom2 = geom_array2[geom2_id]; - - auto IM = relate(geom1, geom2); - return !detail::EvaluatePredicate(predicate, IM); - }); - size_t new_size = thrust::distance(ids.data(), end); - GPUSPATIAL_LOG_INFO("Refined, result size %zu", new_size); - ids.set_size(stream, new_size); + auto zip_begin = + thrust::make_zip_iterator(thrust::make_tuple(ids1.begin(), ids2.begin())); + auto zip_end = thrust::make_zip_iterator(thrust::make_tuple(ids1.end(), ids2.end())); + + auto end = + thrust::remove_if(rmm::exec_policy_nosync(stream), zip_begin, zip_end, + [=] __device__(const thrust::tuple& tuple) { + auto geom1_id = thrust::get<0>(tuple); + auto geom2_id = thrust::get<1>(tuple); + const auto& geom1 = geom_array1[geom1_id]; + const auto& geom2 = geom_array2[geom2_id]; + + auto IM = relate(geom1, geom2); + return !detail::EvaluatePredicate(predicate, IM); + }); + size_t new_size = thrust::distance(zip_begin, end); + ids1.resize(new_size, stream); + ids2.resize(new_size, stream); } template @@ -240,9 +338,9 @@ void RelateEngine::Evaluate( const rmm::cuda_stream_view& stream, const PointArrayView& geom_array1, const PolygonArrayView& geom_array2, Predicate predicate, - Queue>& ids) { + rmm::device_uvector& ids1, rmm::device_uvector& ids2) { EvaluateImpl(stream, geom_array1, MultiPointArrayView(), geom_array2, - predicate, ids, false /*inverse IM*/); + predicate, ids1, ids2, false /*inverse IM*/); } template @@ -250,9 +348,9 @@ void RelateEngine::Evaluate( const rmm::cuda_stream_view& stream, const MultiPointArrayView& geom_array1, const PolygonArrayView& geom_array2, Predicate predicate, - Queue>& ids) { + rmm::device_uvector& ids1, rmm::device_uvector& ids2) { EvaluateImpl(stream, PointArrayView(), geom_array1, geom_array2, - predicate, ids, false /*inverse IM*/); + predicate, ids1, ids2, false /*inverse IM*/); } template @@ -260,19 +358,9 @@ void RelateEngine::Evaluate( const rmm::cuda_stream_view& stream, const PolygonArrayView& geom_array1, const PointArrayView& geom_array2, Predicate predicate, - Queue>& ids) { - thrust::for_each(rmm::exec_policy_nosync(stream), ids.data(), - ids.data() + ids.size(stream), - [] __device__(thrust::pair & pair) { - thrust::swap(pair.first, pair.second); - }); + rmm::device_uvector& ids1, rmm::device_uvector& ids2) { EvaluateImpl(stream, geom_array2, MultiPointArrayView(), geom_array1, - predicate, ids, true /*inverse IM*/); - thrust::for_each(rmm::exec_policy_nosync(stream), ids.data(), - ids.data() + ids.size(stream), - [] __device__(thrust::pair & pair) { - thrust::swap(pair.first, pair.second); - }); + predicate, ids2, ids1, true /*inverse IM*/); } template @@ -280,19 +368,9 @@ void RelateEngine::Evaluate( const rmm::cuda_stream_view& stream, const PolygonArrayView& geom_array1, const MultiPointArrayView& geom_array2, Predicate predicate, - Queue>& ids) { - thrust::for_each(rmm::exec_policy_nosync(stream), ids.data(), - ids.data() + ids.size(stream), - [] __device__(thrust::pair & pair) { - thrust::swap(pair.first, pair.second); - }); + rmm::device_uvector& ids1, rmm::device_uvector& ids2) { EvaluateImpl(stream, PointArrayView(), geom_array2, geom_array1, - predicate, ids, true /*inverse IM*/); - thrust::for_each(rmm::exec_policy_nosync(stream), ids.data(), - ids.data() + ids.size(stream), - [] __device__(thrust::pair & pair) { - thrust::swap(pair.first, pair.second); - }); + predicate, ids2, ids1, true /*inverse IM*/); } template @@ -300,9 +378,9 @@ void RelateEngine::Evaluate( const rmm::cuda_stream_view& stream, const PointArrayView& geom_array1, const MultiPolygonArrayView& geom_array2, Predicate predicate, - Queue>& ids) { + rmm::device_uvector& ids1, rmm::device_uvector& ids2) { EvaluateImpl(stream, geom_array1, MultiPointArrayView(), geom_array2, - predicate, ids, false /*inverse IM*/); + predicate, ids1, ids2, false /*inverse IM*/); } template @@ -310,9 +388,9 @@ void RelateEngine::Evaluate( const rmm::cuda_stream_view& stream, const MultiPointArrayView& geom_array1, const MultiPolygonArrayView& geom_array2, Predicate predicate, - Queue>& ids) { + rmm::device_uvector& ids1, rmm::device_uvector& ids2) { EvaluateImpl(stream, PointArrayView(), geom_array1, geom_array2, - predicate, ids, false /*inverse IM*/); + predicate, ids1, ids2, false /*inverse IM*/); } template @@ -320,19 +398,9 @@ void RelateEngine::Evaluate( const rmm::cuda_stream_view& stream, const MultiPolygonArrayView& geom_array1, const PointArrayView& geom_array2, Predicate predicate, - Queue>& ids) { - thrust::for_each(rmm::exec_policy_nosync(stream), ids.data(), - ids.data() + ids.size(stream), - [] __device__(thrust::pair & pair) { - thrust::swap(pair.first, pair.second); - }); + rmm::device_uvector& ids1, rmm::device_uvector& ids2) { EvaluateImpl(stream, geom_array2, MultiPointArrayView(), geom_array1, - predicate, ids, true /*inverse IM*/); - thrust::for_each(rmm::exec_policy_nosync(stream), ids.data(), - ids.data() + ids.size(stream), - [] __device__(thrust::pair & pair) { - thrust::swap(pair.first, pair.second); - }); + predicate, ids2, ids1, true /*inverse IM*/); } template @@ -340,19 +408,9 @@ void RelateEngine::Evaluate( const rmm::cuda_stream_view& stream, const MultiPolygonArrayView& geom_array1, const MultiPointArrayView& geom_array2, Predicate predicate, - Queue>& ids) { - thrust::for_each(rmm::exec_policy_nosync(stream), ids.data(), - ids.data() + ids.size(stream), - [] __device__(thrust::pair & pair) { - thrust::swap(pair.first, pair.second); - }); + rmm::device_uvector& ids1, rmm::device_uvector& ids2) { EvaluateImpl(stream, PointArrayView(), geom_array2, geom_array1, - predicate, ids, true /*inverse IM*/); - thrust::for_each(rmm::exec_policy_nosync(stream), ids.data(), - ids.data() + ids.size(stream), - [] __device__(thrust::pair & pair) { - thrust::swap(pair.first, pair.second); - }); + predicate, ids2, ids1, true /*inverse IM*/); } template @@ -361,10 +419,15 @@ void RelateEngine::EvaluateImpl( const PointArrayView& point_array, const MultiPointArrayView& multi_point_array, const PolygonArrayView& poly_array, Predicate predicate, - Queue>& ids, bool inverse) { + rmm::device_uvector& point_ids, rmm::device_uvector& poly_ids, + bool inverse) { + // Casting short rays from each point to do precise point-in-polygon test + // Reference: "Geng L, Lee R, Zhang X. Rayjoin: Fast and precise spatial join. + // InProceedings of the 38th ACM International Conference on Supercomputing 2024" using params_t = detail::LaunchParamsPolygonPointQuery; - - size_t ids_size = ids.size(stream); + assert(point_array.empty() || multi_point_array.empty()); + assert(point_ids.size() == poly_ids.size()); + size_t ids_size = point_ids.size(); GPUSPATIAL_LOG_INFO( "Refine with ray-tracing, (multi-)point %zu, polygon %zu, predicate %s, result size %zu, inverse %d", !point_array.empty() ? point_array.size() : multi_point_array.size(), @@ -373,79 +436,88 @@ void RelateEngine::EvaluateImpl( if (ids_size == 0) { return; } - // pair.first is point id; pair.second is polygon id - // Sort by multi polygon id - thrust::sort(rmm::exec_policy_nosync(stream), ids.data(), ids.data() + ids_size, - [] __device__(const thrust::pair& pair1, - const thrust::pair& pair2) { - return pair1.second < pair2.second; + + auto zip_begin = + thrust::make_zip_iterator(thrust::make_tuple(point_ids.begin(), poly_ids.begin())); + auto zip_end = + thrust::make_zip_iterator(thrust::make_tuple(point_ids.end(), poly_ids.end())); + auto invalid_tuple = thrust::make_tuple(std::numeric_limits::max(), + std::numeric_limits::max()); + + // Sort by polygon id + thrust::sort(rmm::exec_policy_nosync(stream), zip_begin, zip_end, + [] __device__(const thrust::tuple& tu1, + const thrust::tuple& tu2) { + return thrust::get<1>(tu1) < thrust::get<1>(tu2); }); - rmm::device_uvector poly_ids(ids_size, stream); + rmm::device_uvector uniq_poly_ids(ids_size, stream); - thrust::transform(rmm::exec_policy_nosync(stream), ids.data(), ids.data() + ids_size, - poly_ids.data(), - [] __device__(const thrust::pair& pair) { - return pair.second; - }); - auto poly_ids_end = - thrust::unique(rmm::exec_policy_nosync(stream), poly_ids.begin(), poly_ids.end()); - poly_ids.resize(thrust::distance(poly_ids.begin(), poly_ids_end), stream); - poly_ids.shrink_to_fit(stream); + thrust::copy(rmm::exec_policy_nosync(stream), poly_ids.begin(), poly_ids.end(), + uniq_poly_ids.begin()); - auto bvh_bytes = EstimateBVHSize(stream, poly_array, ArrayView(poly_ids)); - size_t avail_bytes = rmm::available_device_memory().first * config_.memory_quota; + // Collect uniq polygon ids to estimate total BVH memory usage + auto uniq_poly_ids_end = thrust::unique(rmm::exec_policy_nosync(stream), + uniq_poly_ids.begin(), uniq_poly_ids.end()); + uniq_poly_ids.resize(thrust::distance(uniq_poly_ids.begin(), uniq_poly_ids_end), + stream); + uniq_poly_ids.shrink_to_fit(stream); + + auto bvh_bytes = EstimateBVHSize(stream, poly_array, ArrayView(uniq_poly_ids), + config_.segs_per_aabb); + size_t avail_bytes = + MemoryManager::instance().get_available_device_memory() * config_.memory_quota; auto n_batches = bvh_bytes / avail_bytes + 1; auto batch_size = (ids_size + n_batches - 1) / n_batches; - auto invalid_pair = thrust::make_pair(std::numeric_limits::max(), - std::numeric_limits::max()); GPUSPATIAL_LOG_INFO( "Unique polygons %zu, memory quota %zu MB, estimated BVH size %zu MB", - poly_ids.size(), avail_bytes / (1024 * 1024), bvh_bytes / (1024 * 1024)); + uniq_poly_ids.size(), avail_bytes / (1024 * 1024), bvh_bytes / (1024 * 1024)); for (int batch = 0; batch < n_batches; batch++) { auto ids_begin = batch * batch_size; auto ids_end = std::min(ids_begin + batch_size, ids_size); auto ids_size_batch = ids_end - ids_begin; - poly_ids.resize(ids_size_batch, stream); - thrust::transform(rmm::exec_policy_nosync(stream), ids.data() + ids_begin, - ids.data() + ids_end, poly_ids.data(), - [] __device__(const thrust::pair& pair) { - return pair.second; - }); + // Extract unique polygon IDs in this batch + uniq_poly_ids.resize(ids_size_batch, stream); + thrust::copy(rmm::exec_policy_nosync(stream), poly_ids.begin() + ids_begin, + poly_ids.begin() + ids_end, uniq_poly_ids.begin()); - // ids is sorted - poly_ids_end = - thrust::unique(rmm::exec_policy_nosync(stream), poly_ids.begin(), poly_ids.end()); + // poly ids are sorted + uniq_poly_ids_end = thrust::unique(rmm::exec_policy_nosync(stream), + uniq_poly_ids.begin(), uniq_poly_ids.end()); - poly_ids.resize(thrust::distance(poly_ids.begin(), poly_ids_end), stream); - poly_ids.shrink_to_fit(stream); + uniq_poly_ids.resize(thrust::distance(uniq_poly_ids.begin(), uniq_poly_ids_end), + stream); + uniq_poly_ids.shrink_to_fit(stream); rmm::device_uvector IMs(ids_size_batch, stream); - rmm::device_uvector seg_begins(0, stream); rmm::device_uvector locations(ids_size_batch, stream); rmm::device_buffer bvh_buffer(0, stream); rmm::device_uvector aabb_poly_ids(0, stream), aabb_ring_ids(0, stream); + rmm::device_uvector> aabb_vertex_offsets(0, stream); // aabb id -> vertex begin[polygon] + ith point in this polygon - auto handle = BuildBVH(stream, poly_array, ArrayView(poly_ids), seg_begins, - bvh_buffer, aabb_poly_ids, aabb_ring_ids); + auto handle = BuildBVH(stream, poly_array, ArrayView(uniq_poly_ids), + config_.segs_per_aabb, bvh_buffer, aabb_poly_ids, + aabb_ring_ids, aabb_vertex_offsets); params_t params; params.points = point_array; params.multi_points = multi_point_array; params.polygons = poly_array; - params.polygon_ids = ArrayView(poly_ids); - params.ids = ArrayView>(ids.data() + ids_begin, - ids_size_batch); - params.seg_begins = ArrayView(seg_begins); + params.uniq_polygon_ids = ArrayView(uniq_poly_ids); + params.query_point_ids = point_ids.data() + ids_begin; + params.query_polygon_ids = poly_ids.data() + ids_begin; + params.query_size = ids_size_batch; params.IMs = ArrayView(IMs); params.handle = handle; params.aabb_poly_ids = ArrayView(aabb_poly_ids); params.aabb_ring_ids = ArrayView(aabb_ring_ids); + params.aabb_vertex_offsets = + ArrayView>(aabb_vertex_offsets); rmm::device_buffer params_buffer(sizeof(params_t), stream); @@ -457,34 +529,32 @@ void RelateEngine::EvaluateImpl( dim3{static_cast(ids_size_batch), 1, 1}, ArrayView((char*)params_buffer.data(), params_buffer.size())); - auto* p_IMs = IMs.data(); - auto* p_ids = ids.data(); - - thrust::transform(rmm::exec_policy_nosync(stream), - thrust::make_counting_iterator(0), - thrust::make_counting_iterator(ids_size_batch), - ids.data() + ids_begin, [=] __device__(uint32_t i) { - const auto& pair = p_ids[ids_begin + i]; - - auto IM = p_IMs[i]; - if (inverse) { - IM = IntersectionMatrix::Transpose(IM); - } - if (detail::EvaluatePredicate(predicate, IM)) { - return pair; - } else { - return invalid_pair; - } - }); + thrust::transform( + rmm::exec_policy_nosync(stream), + thrust::make_zip_iterator(thrust::make_tuple( + point_ids.begin() + ids_begin, poly_ids.begin() + ids_begin, IMs.begin())), + thrust::make_zip_iterator(thrust::make_tuple( + point_ids.begin() + ids_end, poly_ids.begin() + ids_end, IMs.end())), + thrust::make_zip_iterator(thrust::make_tuple(point_ids.begin() + ids_begin, + poly_ids.begin() + ids_begin)), + [=] __device__(const thrust::tuple& t) { + auto res = thrust::make_tuple(thrust::get<0>(t), thrust::get<1>(t)); + auto IM = thrust::get<2>(t); + + if (inverse) { + IM = IntersectionMatrix::Transpose(IM); + } + + return detail::EvaluatePredicate(predicate, IM) ? res : invalid_tuple; + }); } - auto end = thrust::remove_if( - rmm::exec_policy_nosync(stream), ids.data(), ids.data() + ids_size, - [=] __device__(const thrust::pair& pair) { - return pair == invalid_pair; - }); - size_t new_size = thrust::distance(ids.data(), end); - GPUSPATIAL_LOG_INFO("Refined, result size %zu", new_size); - ids.set_size(stream, new_size); + auto end = thrust::remove_if(rmm::exec_policy_nosync(stream), zip_begin, zip_end, + [=] __device__(const thrust::tuple& tu) { + return tu == invalid_tuple; + }); + size_t new_size = thrust::distance(zip_begin, end); + point_ids.resize(new_size, stream); + poly_ids.resize(new_size, stream); } template @@ -493,11 +563,12 @@ void RelateEngine::EvaluateImpl( const PointArrayView& point_array, const MultiPointArrayView& multi_point_array, const MultiPolygonArrayView& multi_poly_array, Predicate predicate, - Queue>& ids, bool inverse) { + rmm::device_uvector& point_ids, rmm::device_uvector& multi_poly_ids, + bool inverse) { using params_t = detail::LaunchParamsPointMultiPolygonQuery; - assert(point_array.empty() || multi_point_array.empty()); - size_t ids_size = ids.size(stream); + assert(point_ids.size() == multi_poly_ids.size()); + size_t ids_size = point_ids.size(); GPUSPATIAL_LOG_INFO( "Refine with ray-tracing, (multi-)point %zu, multi-polygon %zu, predicate %s, result size %zu, inverse %d", !point_array.empty() ? point_array.size() : multi_point_array.size(), @@ -506,37 +577,44 @@ void RelateEngine::EvaluateImpl( if (ids_size == 0) { return; } - // pair.first is point id; pair.second is multi polygon id - // Sort by multi polygon id - thrust::sort(rmm::exec_policy_nosync(stream), ids.data(), ids.data() + ids_size, - [] __device__(const thrust::pair& pair1, - const thrust::pair& pair2) { - return pair1.second < pair2.second; + auto zip_begin = thrust::make_zip_iterator( + thrust::make_tuple(point_ids.begin(), multi_poly_ids.begin())); + auto zip_end = thrust::make_zip_iterator( + thrust::make_tuple(point_ids.end(), multi_poly_ids.end())); + auto invalid_tuple = thrust::make_tuple(std::numeric_limits::max(), + std::numeric_limits::max()); + + // Sort by polygon id + thrust::sort(rmm::exec_policy_nosync(stream), zip_begin, zip_end, + [] __device__(const thrust::tuple& tu1, + const thrust::tuple& tu2) { + return thrust::get<1>(tu1) < thrust::get<1>(tu2); }); - rmm::device_uvector multi_poly_ids(ids_size, stream); + rmm::device_uvector uniq_multi_poly_ids(ids_size, stream); - thrust::transform(rmm::exec_policy_nosync(stream), ids.data(), ids.data() + ids_size, - multi_poly_ids.data(), - [] __device__(const thrust::pair& pair) { - return pair.second; - }); - auto multi_poly_ids_end = thrust::unique(rmm::exec_policy_nosync(stream), - multi_poly_ids.begin(), multi_poly_ids.end()); - multi_poly_ids.resize(thrust::distance(multi_poly_ids.begin(), multi_poly_ids_end), - stream); - multi_poly_ids.shrink_to_fit(stream); + thrust::copy(rmm::exec_policy_nosync(stream), multi_poly_ids.begin(), + multi_poly_ids.end(), uniq_multi_poly_ids.begin()); + + // Collect uniq polygon ids to estimate total BVH memory usage + auto uniq_multi_poly_ids_end = + thrust::unique(rmm::exec_policy_nosync(stream), uniq_multi_poly_ids.begin(), + uniq_multi_poly_ids.end()); + uniq_multi_poly_ids.resize( + thrust::distance(uniq_multi_poly_ids.begin(), uniq_multi_poly_ids_end), stream); + uniq_multi_poly_ids.shrink_to_fit(stream); auto bvh_bytes = - EstimateBVHSize(stream, multi_poly_array, ArrayView(multi_poly_ids)); - size_t avail_bytes = rmm::available_device_memory().first * config_.memory_quota; + EstimateBVHSize(stream, multi_poly_array, ArrayView(uniq_multi_poly_ids), + config_.segs_per_aabb); + size_t avail_bytes = + MemoryManager::instance().get_available_device_memory() * config_.memory_quota; auto n_batches = bvh_bytes / avail_bytes + 1; auto batch_size = (ids_size + n_batches - 1) / n_batches; - auto invalid_pair = thrust::make_pair(std::numeric_limits::max(), - std::numeric_limits::max()); + GPUSPATIAL_LOG_INFO( "Unique multi-polygons %zu, memory quota %zu MB, estimated BVH size %zu MB", - multi_poly_ids.size(), avail_bytes / (1024 * 1024), bvh_bytes / (1024 * 1024)); + uniq_multi_poly_ids.size(), avail_bytes / (1024 * 1024), bvh_bytes / (1024 * 1024)); for (int batch = 0; batch < n_batches; batch++) { auto ids_begin = batch * batch_size; @@ -544,47 +622,48 @@ void RelateEngine::EvaluateImpl( auto ids_size_batch = ids_end - ids_begin; // Extract multi polygon IDs in this batch - multi_poly_ids.resize(ids_size_batch, stream); + uniq_multi_poly_ids.resize(ids_size_batch, stream); - thrust::transform(rmm::exec_policy_nosync(stream), ids.data() + ids_begin, - ids.data() + ids_end, multi_poly_ids.data(), - [] __device__(const thrust::pair& pair) { - return pair.second; - }); + thrust::copy(rmm::exec_policy_nosync(stream), multi_poly_ids.begin() + ids_begin, + multi_poly_ids.begin() + ids_end, uniq_multi_poly_ids.begin()); // multi polygon ids have been sorted before - multi_poly_ids_end = thrust::unique(rmm::exec_policy_nosync(stream), - multi_poly_ids.begin(), multi_poly_ids.end()); - multi_poly_ids.resize(thrust::distance(multi_poly_ids.begin(), multi_poly_ids_end), - stream); - multi_poly_ids.shrink_to_fit(stream); + uniq_multi_poly_ids_end = + thrust::unique(rmm::exec_policy_nosync(stream), uniq_multi_poly_ids.begin(), + uniq_multi_poly_ids.end()); + uniq_multi_poly_ids.resize( + thrust::distance(uniq_multi_poly_ids.begin(), uniq_multi_poly_ids_end), stream); + uniq_multi_poly_ids.shrink_to_fit(stream); rmm::device_uvector IMs(ids_size_batch, stream); - rmm::device_uvector seg_begins(0, stream); - rmm::device_uvector uniq_part_begins(0, stream); rmm::device_buffer bvh_buffer(0, stream); rmm::device_uvector aabb_multi_poly_ids(0, stream), aabb_part_ids(0, stream), aabb_ring_ids(0, stream); + rmm::device_uvector> aabb_vertex_offsets(0, stream); + rmm::device_uvector uniq_part_begins(0, stream); - auto handle = BuildBVH(stream, multi_poly_array, ArrayView(multi_poly_ids), - seg_begins, uniq_part_begins, bvh_buffer, aabb_multi_poly_ids, - aabb_part_ids, aabb_ring_ids); + auto handle = + BuildBVH(stream, multi_poly_array, ArrayView(uniq_multi_poly_ids), + config_.segs_per_aabb, bvh_buffer, aabb_multi_poly_ids, aabb_part_ids, + aabb_ring_ids, aabb_vertex_offsets, uniq_part_begins); params_t params; params.points = point_array; params.multi_points = multi_point_array; params.multi_polygons = multi_poly_array; - params.multi_polygon_ids = ArrayView(multi_poly_ids); - params.ids = ArrayView>(ids.data() + ids_begin, - ids_size_batch); - params.seg_begins = ArrayView(seg_begins); + params.uniq_multi_polygon_ids = ArrayView(uniq_multi_poly_ids); + params.query_point_ids = point_ids.data() + ids_begin; + params.query_multi_polygon_ids = multi_poly_ids.data() + ids_begin; + params.query_size = ids_size_batch; params.uniq_part_begins = ArrayView(uniq_part_begins); params.IMs = ArrayView(IMs); params.handle = handle; params.aabb_multi_poly_ids = ArrayView(aabb_multi_poly_ids); params.aabb_part_ids = ArrayView(aabb_part_ids); params.aabb_ring_ids = ArrayView(aabb_ring_ids); + params.aabb_vertex_offsets = + ArrayView>(aabb_vertex_offsets); rmm::device_buffer params_buffer(sizeof(params_t), stream); @@ -596,166 +675,90 @@ void RelateEngine::EvaluateImpl( dim3{static_cast(ids_size_batch), 1, 1}, ArrayView((char*)params_buffer.data(), params_buffer.size())); - auto* p_IMs = IMs.data(); - auto* p_ids = ids.data(); - - thrust::transform(rmm::exec_policy_nosync(stream), - thrust::make_counting_iterator(0), - thrust::make_counting_iterator(ids_size_batch), - ids.data() + ids_begin, [=] __device__(uint32_t i) { - const auto& pair = p_ids[ids_begin + i]; - - auto IM = p_IMs[i]; - if (inverse) { - IM = IntersectionMatrix::Transpose(IM); - } - if (detail::EvaluatePredicate(predicate, IM)) { - return pair; - } else { - return invalid_pair; - } - }); + thrust::transform( + rmm::exec_policy_nosync(stream), + thrust::make_zip_iterator(thrust::make_tuple(point_ids.begin() + ids_begin, + multi_poly_ids.begin() + ids_begin, + IMs.begin())), + thrust::make_zip_iterator(thrust::make_tuple( + point_ids.begin() + ids_end, multi_poly_ids.begin() + ids_end, IMs.end())), + thrust::make_zip_iterator(thrust::make_tuple(point_ids.begin() + ids_begin, + multi_poly_ids.begin() + ids_begin)), + [=] __device__(const thrust::tuple& t) { + auto res = thrust::make_tuple(thrust::get<0>(t), thrust::get<1>(t)); + auto IM = thrust::get<2>(t); + + if (inverse) { + IM = IntersectionMatrix::Transpose(IM); + } + + return detail::EvaluatePredicate(predicate, IM) ? res : invalid_tuple; + }); } - auto end = thrust::remove_if( - rmm::exec_policy_nosync(stream), ids.data(), ids.data() + ids_size, - [=] __device__(const thrust::pair& pair) { - return pair == invalid_pair; - }); - size_t new_size = thrust::distance(ids.data(), end); - GPUSPATIAL_LOG_INFO("Refined, result size %zu", new_size); - ids.set_size(stream, new_size); + auto end = thrust::remove_if(rmm::exec_policy_nosync(stream), zip_begin, zip_end, + [=] __device__(const thrust::tuple& tu) { + return tu == invalid_tuple; + }); + size_t new_size = thrust::distance(zip_begin, end); + point_ids.resize(new_size, stream); + multi_poly_ids.resize(new_size, stream); } template size_t RelateEngine::EstimateBVHSize( const rmm::cuda_stream_view& stream, const PolygonArrayView& polys, - ArrayView poly_ids) { - auto n_polygons = poly_ids.size(); - rmm::device_uvector n_segs(n_polygons, stream); - auto* p_nsegs = n_segs.data(); - - LaunchKernel(stream, [=] __device__() { - using WarpReduce = cub::WarpReduce; - __shared__ WarpReduce::TempStorage temp_storage[MAX_BLOCK_SIZE / 32]; - auto lane = threadIdx.x % 32; - auto warp_id = threadIdx.x / 32; - auto global_warp_id = TID_1D / 32; - auto n_warps = TOTAL_THREADS_1D / 32; - - for (auto i = global_warp_id; i < n_polygons; i += n_warps) { - auto id = poly_ids[i]; - const auto& polygon = polys[id]; - uint32_t total_segs = 0; - - for (auto ring = lane; ring < polygon.num_rings(); ring += 32) { - total_segs += polygon.get_ring(ring).num_points(); - } - total_segs = WarpReduce(temp_storage[warp_id]).Sum(total_segs); - if (lane == 0) { - p_nsegs[i] = total_segs; - } - } - }); - auto total_segs = - thrust::reduce(rmm::exec_policy_nosync(stream), n_segs.begin(), n_segs.end()); - if (total_segs == 0) { + ArrayView poly_ids, int segs_per_aabb) { + auto num_aabbs = detail::ComputeNumAabbs(stream, polys, poly_ids, segs_per_aabb); + if (num_aabbs == 0) { return 0; } + // temporary but still needed to consider this part of memory - auto aabb_size = total_segs * sizeof(OptixAabb); + auto aabb_size = num_aabbs * sizeof(OptixAabb); auto bvh_bytes = rt_engine_->EstimateMemoryUsageForAABB( - total_segs, config_.bvh_fast_build, config_.bvh_fast_compact); - // BVH size and aabb_poly_ids, aabb_ring_ids - return aabb_size + bvh_bytes + 2 * sizeof(INDEX_T) * total_segs; + num_aabbs, config_.bvh_fast_build, config_.bvh_compact); + // BVH size and aabb_poly_ids, aabb_ring_ids, aabb_vertex_offsets + return aabb_size + bvh_bytes + 4 * sizeof(INDEX_T) * num_aabbs; } template size_t RelateEngine::EstimateBVHSize( const rmm::cuda_stream_view& stream, const MultiPolygonArrayView& multi_polys, - ArrayView multi_poly_ids) { - auto n_mult_polygons = multi_poly_ids.size(); - rmm::device_uvector n_segs(n_mult_polygons, stream); - auto* p_nsegs = n_segs.data(); - - LaunchKernel(stream, [=] __device__() { - using WarpReduce = cub::WarpReduce; - __shared__ WarpReduce::TempStorage temp_storage[MAX_BLOCK_SIZE / 32]; - auto lane = threadIdx.x % 32; - auto warp_id = threadIdx.x / 32; - auto global_warp_id = TID_1D / 32; - auto n_warps = TOTAL_THREADS_1D / 32; - - for (auto i = global_warp_id; i < n_mult_polygons; i += n_warps) { - auto id = multi_poly_ids[i]; - const auto& multi_polygon = multi_polys[id]; - uint32_t total_segs = 0; + ArrayView multi_poly_ids, int segs_per_aabb) { + auto num_aabbs = + detail::ComputeNumAabbs(stream, multi_polys, multi_poly_ids, segs_per_aabb); - for (int part_idx = 0; part_idx < multi_polygon.num_polygons(); part_idx++) { - auto polygon = multi_polygon.get_polygon(part_idx); - for (auto ring = lane; ring < polygon.num_rings(); ring += 32) { - total_segs += polygon.get_ring(ring).num_points(); - } - } - total_segs = WarpReduce(temp_storage[warp_id]).Sum(total_segs); - if (lane == 0) { - p_nsegs[i] = total_segs; - } - } - }); - auto total_segs = - thrust::reduce(rmm::exec_policy_nosync(stream), n_segs.begin(), n_segs.end()); - if (total_segs == 0) { - return 0; - } // temporary but still needed to consider this part of memory - auto aabb_size = total_segs * sizeof(OptixAabb); + auto aabb_size = num_aabbs * sizeof(OptixAabb); auto bvh_bytes = rt_engine_->EstimateMemoryUsageForAABB( - total_segs, config_.bvh_fast_build, config_.bvh_fast_compact); - // BVH size and aabb_multi_poly_ids, aabb_part_ids, aabb_ring_ids - return aabb_size + bvh_bytes + 3 * sizeof(INDEX_T) * total_segs; + num_aabbs, config_.bvh_fast_build, config_.bvh_compact); + // BVH size and aabb_multi_poly_ids, aabb_part_ids, aabb_ring_ids, aabb_vertex_offsets + return aabb_size + bvh_bytes + 5 * sizeof(INDEX_T) * num_aabbs; } template OptixTraversableHandle RelateEngine::BuildBVH( const rmm::cuda_stream_view& stream, const PolygonArrayView& polygons, ArrayView polygon_ids, - rmm::device_uvector& seg_begins, rmm::device_buffer& buffer, + int segs_per_aabb, rmm::device_buffer& buffer, rmm::device_uvector& aabb_poly_ids, - rmm::device_uvector& aabb_ring_ids) { + rmm::device_uvector& aabb_ring_ids, + rmm::device_uvector>& aabb_vertex_offsets) { auto n_polygons = polygon_ids.size(); - rmm::device_uvector n_segs(n_polygons, stream); - - // TODO: warp reduce - thrust::transform(rmm::exec_policy_nosync(stream), polygon_ids.begin(), - polygon_ids.end(), n_segs.begin(), - [=] __device__(const uint32_t& id) -> uint32_t { - const auto& polygon = polygons[id]; - uint32_t total_segs = 0; - - for (int ring = 0; ring < polygon.num_rings(); ring++) { - total_segs += polygon.get_ring(ring).num_points(); - } - return total_segs; - }); - - seg_begins = std::move(rmm::device_uvector(n_polygons + 1, stream)); - auto* p_seg_begins = seg_begins.data(); - seg_begins.set_element_to_zero_async(0, stream); - - thrust::inclusive_scan(rmm::exec_policy_nosync(stream), n_segs.begin(), n_segs.end(), - seg_begins.begin() + 1); - - uint32_t num_aabbs = seg_begins.back_element(stream); - + auto num_aabbs = detail::ComputeNumAabbs(stream, polygons, polygon_ids, segs_per_aabb); aabb_poly_ids = std::move(rmm::device_uvector(num_aabbs, stream)); aabb_ring_ids = std::move(rmm::device_uvector(num_aabbs, stream)); + aabb_vertex_offsets = + std::move(rmm::device_uvector>(num_aabbs, stream)); - auto* p_poly_ids = aabb_poly_ids.data(); - auto* p_ring_ids = aabb_ring_ids.data(); + auto* p_aabb_poly_ids = aabb_poly_ids.data(); + auto* p_aabb_ring_ids = aabb_ring_ids.data(); + auto* p_aabb_vertex_offsets = aabb_vertex_offsets.data(); - rmm::device_uvector aabbs(num_aabbs, stream); - auto* p_aabbs = aabbs.data(); + rmm::device_scalar d_tail(0, stream); + + auto* p_tail = d_tail.data(); LaunchKernel(stream.value(), [=] __device__() { auto lane = threadIdx.x % 32; @@ -763,191 +766,222 @@ OptixTraversableHandle RelateEngine::BuildBVH( auto n_warps = TOTAL_THREADS_1D / 32; // each warp takes a polygon - // i is the renumbered polygon id starting from 0 for (auto i = global_warp_id; i < n_polygons; i += n_warps) { auto poly_id = polygon_ids[i]; const auto& polygon = polygons[poly_id]; - auto tail = p_seg_begins[i]; // entire warp sequentially visit each ring for (uint32_t ring_idx = 0; ring_idx < polygon.num_rings(); ring_idx++) { auto ring = polygon.get_ring(ring_idx); - // this is like a hash function, its okay to overflow - OptixAabb aabb; - aabb.minZ = aabb.maxZ = i; - - // each lane takes a seg - for (auto seg_idx = lane; seg_idx < ring.num_segments(); seg_idx += 32) { - const auto& seg = ring.get_line_segment(seg_idx); - const auto& p1 = seg.get_p1(); - const auto& p2 = seg.get_p2(); - - aabb.minX = std::min(p1.x(), p2.x()); - aabb.maxX = std::max(p1.x(), p2.x()); - aabb.minY = std::min(p1.y(), p2.y()); - aabb.maxY = std::max(p1.y(), p2.y()); - - if (std::is_same_v) { - aabb.minX = next_float_from_double(aabb.minX, -1, 2); - aabb.maxX = next_float_from_double(aabb.maxX, 1, 2); - aabb.minY = next_float_from_double(aabb.minY, -1, 2); - aabb.maxY = next_float_from_double(aabb.maxY, 1, 2); - } - p_aabbs[tail + seg_idx] = aabb; - p_poly_ids[tail + seg_idx] = poly_id; - p_ring_ids[tail + seg_idx] = ring_idx; + auto aabbs_per_ring = (ring.num_segments() + segs_per_aabb - 1) / segs_per_aabb; + // e.g., num segs = 3, segs_per_aabb = 2 + // The first aabb covers seg 0,1, with vertex id (0,1,2) + // The second aabb covers seg 2, with vertex id (2,3) + // each lane takes an aabb + for (auto aabb_idx = lane; aabb_idx < aabbs_per_ring; aabb_idx += 32) { + INDEX_T local_vertex_begin = aabb_idx * segs_per_aabb; + INDEX_T local_vertex_end = + std::min((INDEX_T)(local_vertex_begin + segs_per_aabb), + (INDEX_T)ring.num_segments()); + + auto tail = atomicAdd(p_tail, 1); + + assert(tail < num_aabbs); + p_aabb_poly_ids[tail] = poly_id; + p_aabb_ring_ids[tail] = ring_idx; + p_aabb_vertex_offsets[tail] = + thrust::make_pair(local_vertex_begin, local_vertex_end); } - tail += ring.num_segments(); - // fill a dummy AABB, so we have aabb-vertex one-to-one relationship - if (lane == 0) { - p_aabbs[tail] = OptixAabb{0, 0, 0, 0, 0, 0}; - } - tail++; } - assert(p_seg_begins[i + 1] == tail); } }); + rmm::device_uvector aabbs(num_aabbs, stream); + + // Fill AABBs + thrust::transform(rmm::exec_policy_nosync(stream), + thrust::make_counting_iterator(0), + thrust::make_counting_iterator(num_aabbs), aabbs.begin(), + [=] __device__(const uint32_t& aabb_idx) { + OptixAabb aabb; + aabb.minX = std::numeric_limits::max(); + aabb.minY = std::numeric_limits::max(); + aabb.maxX = std::numeric_limits::lowest(); + aabb.maxY = std::numeric_limits::lowest(); + + auto poly_id = p_aabb_poly_ids[aabb_idx]; + auto ring_id = p_aabb_ring_ids[aabb_idx]; + auto vertex_offset_pair = p_aabb_vertex_offsets[aabb_idx]; + const auto& polygon = polygons[poly_id]; + const auto& ring = polygon.get_ring(ring_id); + + for (auto vidx = vertex_offset_pair.first; + vidx <= vertex_offset_pair.second; vidx++) { + const auto& v = ring.get_point(vidx); + float x = v.x(); + float y = v.y(); + + aabb.minX = fminf(aabb.minX, x); + aabb.maxX = fmaxf(aabb.maxX, x); + aabb.minY = fminf(aabb.minY, y); + aabb.maxY = fmaxf(aabb.maxY, y); + } + + if (std::is_same_v) { + aabb.minX = next_float_from_double(aabb.minX, -1, 2); + aabb.maxX = next_float_from_double(aabb.maxX, 1, 2); + aabb.minY = next_float_from_double(aabb.minY, -1, 2); + aabb.maxY = next_float_from_double(aabb.maxY, 1, 2); + } + // Using minZ/maxZ to store polygon id for better filtering + // Refer to polygon_point_query.cu + aabb.minZ = aabb.maxZ = poly_id; + return aabb; + }); + assert(rt_engine_ != nullptr); return rt_engine_->BuildAccelCustom(stream.value(), ArrayView(aabbs), buffer, - config_.bvh_fast_build, config_.bvh_fast_compact); + config_.bvh_fast_build, config_.bvh_compact); } template OptixTraversableHandle RelateEngine::BuildBVH( const rmm::cuda_stream_view& stream, const MultiPolygonArrayView& multi_polys, - ArrayView multi_poly_ids, rmm::device_uvector& seg_begins, - rmm::device_uvector& part_begins, rmm::device_buffer& buffer, + ArrayView multi_poly_ids, int segs_per_aabb, rmm::device_buffer& buffer, rmm::device_uvector& aabb_multi_poly_ids, rmm::device_uvector& aabb_part_ids, - rmm::device_uvector& aabb_ring_ids) { + rmm::device_uvector& aabb_ring_ids, + rmm::device_uvector>& aabb_vertex_offsets, + rmm::device_uvector& part_begins) { auto n_mult_polygons = multi_poly_ids.size(); - rmm::device_uvector n_segs(n_mult_polygons, stream); - auto* p_nsegs = n_segs.data(); - - LaunchKernel(stream, [=] __device__() { - using WarpReduce = cub::WarpReduce; - __shared__ WarpReduce::TempStorage temp_storage[MAX_BLOCK_SIZE / 32]; - auto lane = threadIdx.x % 32; - auto warp_id = threadIdx.x / 32; - auto global_warp_id = TID_1D / 32; - auto n_warps = TOTAL_THREADS_1D / 32; - - for (auto i = global_warp_id; i < n_mult_polygons; i += n_warps) { - auto id = multi_poly_ids[i]; - const auto& multi_polygon = multi_polys[id]; - uint32_t total_segs = 0; - - for (int part_idx = 0; part_idx < multi_polygon.num_polygons(); part_idx++) { - auto polygon = multi_polygon.get_polygon(part_idx); - for (auto ring = lane; ring < polygon.num_rings(); ring += 32) { - total_segs += polygon.get_ring(ring).num_points(); - } - } - total_segs = WarpReduce(temp_storage[warp_id]).Sum(total_segs); - if (lane == 0) { - p_nsegs[i] = total_segs; - } - } - }); - - seg_begins = std::move(rmm::device_uvector(n_mult_polygons + 1, stream)); - auto* p_seg_begins = seg_begins.data(); - seg_begins.set_element_to_zero_async(0, stream); - - thrust::inclusive_scan(rmm::exec_policy_nosync(stream), n_segs.begin(), n_segs.end(), - seg_begins.begin() + 1); - // each line seg is corresponding to an AABB and each ring includes an empty AABB - uint32_t num_aabbs = seg_begins.back_element(stream); + auto num_aabbs = + detail::ComputeNumAabbs(stream, multi_polys, multi_poly_ids, segs_per_aabb); + if (num_aabbs == 0) { + return 0; + } aabb_multi_poly_ids = std::move(rmm::device_uvector(num_aabbs, stream)); aabb_part_ids = std::move(rmm::device_uvector(num_aabbs, stream)); aabb_ring_ids = std::move(rmm::device_uvector(num_aabbs, stream)); + aabb_vertex_offsets = + std::move(rmm::device_uvector>(num_aabbs, stream)); + rmm::device_uvector aabb_seq_ids(num_aabbs, stream); - auto* p_multi_poly_ids = aabb_multi_poly_ids.data(); - auto* p_part_ids = aabb_part_ids.data(); - auto* p_ring_ids = aabb_ring_ids.data(); - - rmm::device_uvector aabbs(num_aabbs, stream); - auto* p_aabbs = aabbs.data(); - - rmm::device_uvector num_parts(n_mult_polygons, stream); + auto* p_aabb_multi_poly_ids = aabb_multi_poly_ids.data(); + auto* p_aabb_part_ids = aabb_part_ids.data(); + auto* p_aabb_ring_ids = aabb_ring_ids.data(); + auto* p_aabb_vertex_offsets = aabb_vertex_offsets.data(); + auto* p_aabb_seq_ids = aabb_seq_ids.data(); - thrust::transform(rmm::exec_policy_nosync(stream), multi_poly_ids.begin(), - multi_poly_ids.end(), num_parts.begin(), [=] __device__(uint32_t id) { - const auto& multi_polygon = multi_polys[id]; - return multi_polygon.num_polygons(); - }); + rmm::device_scalar d_tail(0, stream); - part_begins = std::move(rmm::device_uvector(n_mult_polygons + 1, stream)); - auto* p_part_begins = part_begins.data(); - part_begins.set_element_to_zero_async(0, stream); - thrust::inclusive_scan(rmm::exec_policy_nosync(stream), num_parts.begin(), - num_parts.end(), part_begins.begin() + 1); - num_parts.resize(0, stream); - num_parts.shrink_to_fit(stream); + auto* p_tail = d_tail.data(); LaunchKernel(stream.value(), [=] __device__() { auto lane = threadIdx.x % 32; auto global_warp_id = TID_1D / 32; auto n_warps = TOTAL_THREADS_1D / 32; - // each warp takes a multi polygon - // i is the renumbered polygon id starting from 0 + // each warp takes a polygon for (auto i = global_warp_id; i < n_mult_polygons; i += n_warps) { auto multi_poly_id = multi_poly_ids[i]; const auto& multi_polygon = multi_polys[multi_poly_id]; - auto tail = p_seg_begins[i]; - // entire warp sequentially visit each part for (uint32_t part_idx = 0; part_idx < multi_polygon.num_polygons(); part_idx++) { auto polygon = multi_polygon.get_polygon(part_idx); - // entire warp sequentially visit each ring for (uint32_t ring_idx = 0; ring_idx < polygon.num_rings(); ring_idx++) { auto ring = polygon.get_ring(ring_idx); - // this is like a hash function, its okay to overflow - OptixAabb aabb; - aabb.minZ = aabb.maxZ = p_part_begins[i] + part_idx; - - // each lane takes a seg - for (auto seg_idx = lane; seg_idx < ring.num_segments(); seg_idx += 32) { - const auto& seg = ring.get_line_segment(seg_idx); - const auto& p1 = seg.get_p1(); - const auto& p2 = seg.get_p2(); - - aabb.minX = std::min(p1.x(), p2.x()); - aabb.maxX = std::max(p1.x(), p2.x()); - aabb.minY = std::min(p1.y(), p2.y()); - aabb.maxY = std::max(p1.y(), p2.y()); - - if (std::is_same_v) { - aabb.minX = next_float_from_double(aabb.minX, -1, 2); - aabb.maxX = next_float_from_double(aabb.maxX, 1, 2); - aabb.minY = next_float_from_double(aabb.minY, -1, 2); - aabb.maxY = next_float_from_double(aabb.maxY, 1, 2); - } - p_aabbs[tail + seg_idx] = aabb; - p_multi_poly_ids[tail + seg_idx] = multi_poly_id; - p_part_ids[tail + seg_idx] = part_idx; - p_ring_ids[tail + seg_idx] = ring_idx; - } - tail += ring.num_segments(); - // fill a dummy AABB, so we have aabb-vertex one-to-one relationship - if (lane == 0) { - p_aabbs[tail] = OptixAabb{0, 0, 0, 0, 0, 0}; + auto aabbs_per_ring = (ring.num_segments() + segs_per_aabb - 1) / segs_per_aabb; + // e.g., num segs = 3, segs_per_aabb = 2 + // The first aabb covers seg 0,1, with vertex id (0,1,2) + // The second aabb covers seg 2, with vertex id (2,3) + // each lane takes an aabb + for (auto aabb_idx = lane; aabb_idx < aabbs_per_ring; aabb_idx += 32) { + INDEX_T local_vertex_begin = aabb_idx * segs_per_aabb; + INDEX_T local_vertex_end = + std::min((INDEX_T)(local_vertex_begin + segs_per_aabb), + (INDEX_T)ring.num_segments()); + + auto tail = atomicAdd(p_tail, 1); + + assert(tail < num_aabbs); + p_aabb_multi_poly_ids[tail] = multi_poly_id; + p_aabb_part_ids[tail] = part_idx; + p_aabb_ring_ids[tail] = ring_idx; + p_aabb_vertex_offsets[tail] = + thrust::make_pair(local_vertex_begin, local_vertex_end); + p_aabb_seq_ids[tail] = i; } - tail++; } } - assert(p_seg_begins[i + 1] == tail); } }); + rmm::device_uvector aabbs(num_aabbs, stream); + part_begins = std::move(rmm::device_uvector(n_mult_polygons + 1, stream)); + auto* p_part_begins = part_begins.data(); + part_begins.set_element_to_zero_async(0, stream); + rmm::device_uvector num_parts(n_mult_polygons, stream); + + thrust::transform(rmm::exec_policy_nosync(stream), multi_poly_ids.begin(), + multi_poly_ids.end(), num_parts.begin(), [=] __device__(uint32_t id) { + const auto& multi_polygon = multi_polys[id]; + return multi_polygon.num_polygons(); + }); + + thrust::inclusive_scan(rmm::exec_policy_nosync(stream), num_parts.begin(), + num_parts.end(), part_begins.begin() + 1); + num_parts.resize(0, stream); + num_parts.shrink_to_fit(stream); + + // Fill AABBs + thrust::transform(rmm::exec_policy_nosync(stream), + thrust::make_counting_iterator(0), + thrust::make_counting_iterator(num_aabbs), aabbs.begin(), + [=] __device__(const uint32_t& aabb_idx) { + OptixAabb aabb; + aabb.minX = std::numeric_limits::max(); + aabb.minY = std::numeric_limits::max(); + aabb.maxX = std::numeric_limits::lowest(); + aabb.maxY = std::numeric_limits::lowest(); + + auto multi_poly_id = p_aabb_multi_poly_ids[aabb_idx]; + auto part_id = p_aabb_part_ids[aabb_idx]; + auto ring_id = p_aabb_ring_ids[aabb_idx]; + auto vertex_offset_pair = p_aabb_vertex_offsets[aabb_idx]; + auto seq_id = p_aabb_seq_ids[aabb_idx]; + auto multi_polygon = multi_polys[multi_poly_id]; + const auto& polygon = multi_polygon.get_polygon(part_id); + const auto& ring = polygon.get_ring(ring_id); + + for (auto vidx = vertex_offset_pair.first; + vidx <= vertex_offset_pair.second; vidx++) { + const auto& v = ring.get_point(vidx); + float x = v.x(); + float y = v.y(); + + aabb.minX = fminf(aabb.minX, x); + aabb.maxX = fmaxf(aabb.maxX, x); + aabb.minY = fminf(aabb.minY, y); + aabb.maxY = fmaxf(aabb.maxY, y); + } + + if (std::is_same_v) { + aabb.minX = next_float_from_double(aabb.minX, -1, 2); + aabb.maxX = next_float_from_double(aabb.maxX, 1, 2); + aabb.minY = next_float_from_double(aabb.minY, -1, 2); + aabb.maxY = next_float_from_double(aabb.maxY, 1, 2); + } + + aabb.minZ = aabb.maxZ = p_part_begins[seq_id] + part_id; + return aabb; + }); assert(rt_engine_ != nullptr); + return rt_engine_->BuildAccelCustom(stream.value(), ArrayView(aabbs), buffer, - config_.bvh_fast_build, config_.bvh_fast_compact); + config_.bvh_fast_build, config_.bvh_compact); } // Explicitly instantiate the template for specific types template class RelateEngine, uint32_t>; diff --git a/c/sedona-libgpuspatial/libgpuspatial/src/rt/rt_engine.cpp b/c/sedona-libgpuspatial/libgpuspatial/src/rt/rt_engine.cpp index 7596e0cb3..9857be56c 100644 --- a/c/sedona-libgpuspatial/libgpuspatial/src/rt/rt_engine.cpp +++ b/c/sedona-libgpuspatial/libgpuspatial/src/rt/rt_engine.cpp @@ -14,12 +14,12 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -#include "gpuspatial/index/detail/rt_engine.hpp" -#include "gpuspatial/utils/cuda_utils.h" -#include "gpuspatial/utils/exception.h" +#include "gpuspatial/rt/rt_engine.hpp" +#include "gpuspatial/utils/cuda_utils.hpp" +#include "gpuspatial/utils/exception.hpp" #include "gpuspatial/utils/logger.hpp" -#include "rt/shaders/shader_config.h" +#include "rt/shaders/shader_config.hpp" #include "rmm/device_scalar.hpp" @@ -57,8 +57,6 @@ void context_log_cb(unsigned int level, const char* tag, const char* message, vo } // namespace namespace gpuspatial { -namespace details { - // --- RTConfig Method Definitions --- void RTConfig::AddModule(const Module& mod) { @@ -103,6 +101,12 @@ RTConfig get_default_rt_config(const std::string& ptx_root) { RTEngine::RTEngine() : initialized_(false) {} RTEngine::~RTEngine() { + cudaError_t probe = cudaPeekAtLastError(); + + if (probe == cudaErrorCudartUnloading) { + GPUSPATIAL_LOG_ERROR("CUDA runtime is unloaded"); + return; + } if (initialized_) { releaseOptixResources(); } @@ -112,6 +116,7 @@ void RTEngine::Init(const RTConfig& config) { if (initialized_) { releaseOptixResources(); } + GPUSPATIAL_LOG_INFO("Initialize RTEngine"); initOptix(config); createContext(); createModule(config); @@ -163,32 +168,34 @@ OptixTraversableHandle RTEngine::BuildAccelCustom(cudaStream_t cuda_stream, OPTIX_CHECK(optixAccelComputeMemoryUsage(optix_context_, &accelOptions, &build_input, 1, &blas_buffer_sizes)); - GPUSPATIAL_LOG_INFO( + GPUSPATIAL_LOG_DEBUG( "ComputeBVHMemoryUsage, AABB count: %u, temp size: %zu MB, output size: %zu MB", num_prims, blas_buffer_sizes.tempSizeInBytes / 1024 / 1024, blas_buffer_sizes.outputSizeInBytes / 1024 / 1024); rmm::device_buffer temp_buf(blas_buffer_sizes.tempSizeInBytes, cuda_stream); - out_buf.resize(blas_buffer_sizes.outputSizeInBytes, cuda_stream); if (compact) { + rmm::device_buffer uncompacted_buf(blas_buffer_sizes.outputSizeInBytes, cuda_stream); rmm::device_scalar compacted_size(cuda_stream); OptixAccelEmitDesc emitDesc; emitDesc.type = OPTIX_PROPERTY_TYPE_COMPACTED_SIZE; emitDesc.result = reinterpret_cast(compacted_size.data()); - OPTIX_CHECK(optixAccelBuild( - optix_context_, cuda_stream, &accelOptions, &build_input, 1, - reinterpret_cast(temp_buf.data()), blas_buffer_sizes.tempSizeInBytes, - reinterpret_cast(out_buf.data()), - blas_buffer_sizes.outputSizeInBytes, &traversable, &emitDesc, 1)); + OPTIX_CHECK(optixAccelBuild(optix_context_, cuda_stream, &accelOptions, &build_input, + 1, reinterpret_cast(temp_buf.data()), + blas_buffer_sizes.tempSizeInBytes, + reinterpret_cast(uncompacted_buf.data()), + uncompacted_buf.size(), &traversable, &emitDesc, 1)); auto size = compacted_size.value(cuda_stream); out_buf.resize(size, cuda_stream); OPTIX_CHECK(optixAccelCompact(optix_context_, cuda_stream, traversable, - reinterpret_cast(out_buf.data()), size, - &traversable)); + reinterpret_cast(out_buf.data()), + out_buf.size(), &traversable)); } else { + out_buf.resize(blas_buffer_sizes.outputSizeInBytes, cuda_stream); + OPTIX_CHECK(optixAccelBuild( optix_context_, cuda_stream, &accelOptions, &build_input, 1, reinterpret_cast(temp_buf.data()), blas_buffer_sizes.tempSizeInBytes, @@ -488,15 +495,14 @@ std::vector RTEngine::readData(const std::string& filename) { } void RTEngine::releaseOptixResources() { + GPUSPATIAL_LOG_INFO("Release OptiX resources"); for (auto& [id, res] : resources_) { - optixPipelineDestroy(res.pipeline); - optixProgramGroupDestroy(res.raygen_pg); - optixProgramGroupDestroy(res.miss_pg); - optixProgramGroupDestroy(res.hitgroup_pg); - optixModuleDestroy(res.module); + OPTIX_CHECK(optixPipelineDestroy(res.pipeline)); + OPTIX_CHECK(optixProgramGroupDestroy(res.raygen_pg)); + OPTIX_CHECK(optixProgramGroupDestroy(res.miss_pg)); + OPTIX_CHECK(optixProgramGroupDestroy(res.hitgroup_pg)); + OPTIX_CHECK(optixModuleDestroy(res.module)); } - optixDeviceContextDestroy(optix_context_); + OPTIX_CHECK(optixDeviceContextDestroy(optix_context_)); } - -} // namespace details } // namespace gpuspatial diff --git a/c/sedona-libgpuspatial/libgpuspatial/src/rt/shaders/box_query_backward.cu b/c/sedona-libgpuspatial/libgpuspatial/src/rt/shaders/box_query_backward.cu index 3ffdca9ea..f9a632dd3 100644 --- a/c/sedona-libgpuspatial/libgpuspatial/src/rt/shaders/box_query_backward.cu +++ b/c/sedona-libgpuspatial/libgpuspatial/src/rt/shaders/box_query_backward.cu @@ -14,10 +14,9 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -#include "gpuspatial/index/detail/launch_parameters.h" -#include "gpuspatial/relate/relate.cuh" -#include "ray_params.h" -#include "shader_config.h" +#include "gpuspatial/rt/launch_parameters.cuh" +#include "ray_params.cuh" +#include "shader_config.hpp" #include #include @@ -32,17 +31,22 @@ extern "C" __global__ void __intersection__gpuspatial() { using point_t = gpuspatial::ShaderPointType; constexpr int n_dim = point_t::n_dim; using ray_params_t = gpuspatial::detail::RayParams; - auto geom1_id = optixGetPayload_0(); - auto geom2_id = optixGetPrimitiveIndex(); - const auto& mbr1 = params.mbrs1[geom1_id]; - const auto& mbr2 = params.mbrs2[geom2_id]; - const auto& aabb1 = mbr1.ToOptixAabb(); - const auto aabb2 = mbr2.ToOptixAabb(); + auto rect1_id = optixGetPayload_0(); + auto rect2_id = optixGetPrimitiveIndex(); + const auto& rect1 = params.rects1[rect1_id]; + const auto& rect2 = params.rects2[rect2_id]; + const auto& aabb1 = rect1.ToOptixAabb(); + const auto aabb2 = rect2.ToOptixAabb(); ray_params_t ray_params(aabb1, false); if (ray_params.IsHit(aabb2)) { - if (mbr1.intersects(mbr2)) { - params.ids.Append(thrust::make_pair(geom1_id, geom2_id)); + if (rect1.intersects(rect2)) { + if (params.count == nullptr) { + auto tail = params.rect1_ids.Append(rect1_id); + params.rect2_ids[tail] = rect2_id; + } else { + atomicAdd(params.count, 1); + } } } } @@ -53,20 +57,18 @@ extern "C" __global__ void __raygen__gpuspatial() { using point_t = gpuspatial::ShaderPointType; constexpr int n_dim = point_t::n_dim; - for (uint32_t i = optixGetLaunchIndex().x; i < params.mbrs1.size(); + for (uint32_t i = optixGetLaunchIndex().x; i < params.rects1.size(); i += optixGetLaunchDimensions().x) { - const auto& mbr1 = params.mbrs1[i]; - auto aabb1 = mbr1.ToOptixAabb(); + const auto& rect1 = params.rects1[i]; + if (!rect1.valid()) continue; + auto aabb1 = rect1.ToOptixAabb(); gpuspatial::detail::RayParams ray_params(aabb1, false); - float3 origin, dir; + float3 origin{0, 0, 0}, dir{0, 0, 0}; - origin.x = ray_params.o.x; - origin.y = ray_params.o.y; - origin.z = 0; - - dir.x = ray_params.d.x; - dir.y = ray_params.d.y; - dir.z = 0; + for (int dim = 0; dim < n_dim; dim++) { + (&origin.x)[dim] = (&ray_params.o.x)[dim]; + (&dir.x)[dim] = (&ray_params.d.x)[dim]; + } float tmin = 0; float tmax = 1; diff --git a/c/sedona-libgpuspatial/libgpuspatial/src/rt/shaders/box_query_forward.cu b/c/sedona-libgpuspatial/libgpuspatial/src/rt/shaders/box_query_forward.cu index d85d63741..607d95649 100644 --- a/c/sedona-libgpuspatial/libgpuspatial/src/rt/shaders/box_query_forward.cu +++ b/c/sedona-libgpuspatial/libgpuspatial/src/rt/shaders/box_query_forward.cu @@ -14,9 +14,9 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -#include "gpuspatial/index/detail/launch_parameters.h" -#include "ray_params.h" -#include "shader_config.h" +#include "gpuspatial/rt/launch_parameters.cuh" +#include "ray_params.cuh" +#include "shader_config.hpp" #include #include @@ -31,20 +31,25 @@ extern "C" __global__ void __intersection__gpuspatial() { using point_t = gpuspatial::ShaderPointType; constexpr int n_dim = point_t::n_dim; using ray_params_t = gpuspatial::detail::RayParams; - auto geom1_id = optixGetPrimitiveIndex(); - uint64_t geom2_id = optixGetPayload_0(); - const auto& mbr1 = params.mbrs1[geom1_id]; - const auto& mbr2 = params.mbrs2[geom2_id]; - const auto& aabb1 = mbr1.ToOptixAabb(); - const auto aabb2 = mbr2.ToOptixAabb(); + auto rect1_id = optixGetPrimitiveIndex(); + uint64_t rect2_id = optixGetPayload_0(); + const auto& rect1 = params.rects1[rect1_id]; + const auto& rect2 = params.rects2[rect2_id]; + const auto& aabb1 = rect1.ToOptixAabb(); + const auto aabb2 = rect2.ToOptixAabb(); ray_params_t ray_params(aabb2, true); if (ray_params.IsHit(aabb1)) { // ray cast from AABB2 hits AABB1 ray_params = ray_params_t(aabb1, false); if (!ray_params.IsHit(aabb2)) { // ray cast from AABB1 does not hit AABB2 - if (mbr1.intersects(mbr2)) { - params.ids.Append(thrust::make_pair(geom1_id, geom2_id)); + if (rect1.intersects(rect2)) { + if (params.count == nullptr) { + auto tail = params.rect1_ids.Append(rect1_id); + params.rect2_ids[tail] = rect2_id; + } else { + atomicAdd(params.count, 1); + } } } } @@ -56,20 +61,20 @@ extern "C" __global__ void __raygen__gpuspatial() { using point_t = gpuspatial::ShaderPointType; constexpr int n_dim = point_t::n_dim; - for (uint32_t i = optixGetLaunchIndex().x; i < params.mbrs2.size(); + for (uint32_t i = optixGetLaunchIndex().x; i < params.rects2.size(); i += optixGetLaunchDimensions().x) { - const auto& mbr2 = params.mbrs2[i]; - auto aabb2 = mbr2.ToOptixAabb(); - gpuspatial::detail::RayParams ray_params(aabb2, true); - float3 origin, dir; + const auto& rect2 = params.rects2[i]; + + if (!rect2.valid()) continue; - origin.x = ray_params.o.x; - origin.y = ray_params.o.y; - origin.z = 0; + auto aabb2 = rect2.ToOptixAabb(); + gpuspatial::detail::RayParams ray_params(aabb2, true); + float3 origin{0, 0, 0}, dir{0, 0, 0}; - dir.x = ray_params.d.x; - dir.y = ray_params.d.y; - dir.z = 0; + for (int dim = 0; dim < n_dim; dim++) { + (&origin.x)[dim] = (&ray_params.o.x)[dim]; + (&dir.x)[dim] = (&ray_params.d.x)[dim]; + } float tmin = 0; float tmax = 1; diff --git a/c/sedona-libgpuspatial/libgpuspatial/src/rt/shaders/config_shaders.cmake b/c/sedona-libgpuspatial/libgpuspatial/src/rt/shaders/config_shaders.cmake index 56daf449a..13aac4e03 100644 --- a/c/sedona-libgpuspatial/libgpuspatial/src/rt/shaders/config_shaders.cmake +++ b/c/sedona-libgpuspatial/libgpuspatial/src/rt/shaders/config_shaders.cmake @@ -20,7 +20,7 @@ function(CONFIG_SHADERS SHADER_PTX_FILES) set(SHADER_POINT_TYPES "SHADER_POINT_FLOAT_2D;SHADER_POINT_DOUBLE_2D") set(SHADERS_DEPS "${PROJECT_SOURCE_DIR}/include/gpuspatial/geom" - "${PROJECT_SOURCE_DIR}/include/gpuspatial/index/detail") + "${PROJECT_SOURCE_DIR}/include/gpuspatial/rt") set(OUTPUT_DIR "${PROJECT_BINARY_DIR}/shaders_ptx") set(OPTIX_MODULE_EXTENSION ".ptx") diff --git a/c/sedona-libgpuspatial/libgpuspatial/src/rt/shaders/multipolygon_point_query.cu b/c/sedona-libgpuspatial/libgpuspatial/src/rt/shaders/multipolygon_point_query.cu index f96226c69..3a5c216ba 100644 --- a/c/sedona-libgpuspatial/libgpuspatial/src/rt/shaders/multipolygon_point_query.cu +++ b/c/sedona-libgpuspatial/libgpuspatial/src/rt/shaders/multipolygon_point_query.cu @@ -14,12 +14,12 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -#include "gpuspatial/geom/line_segment.cuh" -#include "gpuspatial/geom/ray_crossing_counter.cuh" -#include "gpuspatial/index/detail/launch_parameters.h" -#include "gpuspatial/relate/relate.cuh" -#include "gpuspatial/utils/floating_point.h" -#include "shader_config.h" +#include "gpuspatial/geom/ray_crossing_counter.hpp" +#include "gpuspatial/relate/relate.hpp" +#include "gpuspatial/rt/launch_parameters.cuh" +#include "gpuspatial/utils/floating_point.hpp" +#include "gpuspatial/utils/helpers.cuh" +#include "shader_config.hpp" #include #include @@ -44,35 +44,36 @@ extern "C" __global__ void __intersection__gpuspatial() { auto point_part_id = optixGetPayload_7(); const auto& multi_polygons = params.multi_polygons; - auto point_idx = params.ids[query_idx].first; - auto multi_polygon_idx = params.ids[query_idx].second; + auto point_idx = params.query_point_ids[query_idx]; + auto multi_polygon_idx = params.query_multi_polygon_ids[query_idx]; auto hit_multipolygon_idx = params.aabb_multi_poly_ids[aabb_id]; auto hit_part_idx = params.aabb_part_ids[aabb_id]; auto hit_ring_idx = params.aabb_ring_ids[aabb_id]; - + const auto& vertex_offsets = params.aabb_vertex_offsets[aabb_id]; // the seg being hit is not from the query polygon if (hit_multipolygon_idx != multi_polygon_idx || hit_part_idx != part_idx || hit_ring_idx != ring_idx) { return; } - uint32_t local_v1_idx = aabb_id - params.seg_begins[reordered_multi_polygon_idx]; - uint32_t global_v1_idx = v_offset + local_v1_idx; - uint32_t global_v2_idx = global_v1_idx + 1; - - auto vertices = multi_polygons.get_vertices(); - // segment being hit - const auto& v1 = vertices[global_v1_idx]; - const auto& v2 = vertices[global_v2_idx]; - + const auto& multi_polygon = multi_polygons[multi_polygon_idx]; + const auto& polygon = multi_polygon.get_polygon(part_idx); + const auto& ring = polygon.get_ring(ring_idx); RayCrossingCounter locator(crossing_count, point_on_seg); - if (!params.points.empty()) { - const auto& p = params.points[point_idx]; - locator.countSegment(p, v1, v2); - } else if (!params.multi_points.empty()) { - const auto& p = params.multi_points[point_idx].get_point(point_part_id); - locator.countSegment(p, v1, v2); + // For each segment in the AABB, count crossings + for (auto vertex_offset = vertex_offsets.first; vertex_offset < vertex_offsets.second; + ++vertex_offset) { + const auto& v1 = ring.get_point(vertex_offset); + const auto& v2 = ring.get_point(vertex_offset + 1); + + if (!params.points.empty()) { + const auto& p = params.points[point_idx]; + locator.countSegment(p, v1, v2); + } else if (!params.multi_points.empty()) { + const auto& p = params.multi_points[point_idx].get_point(point_part_id); + locator.countSegment(p, v1, v2); + } } optixSetPayload_5(locator.get_crossing_count()); @@ -82,22 +83,23 @@ extern "C" __global__ void __intersection__gpuspatial() { extern "C" __global__ void __raygen__gpuspatial() { using namespace gpuspatial; using point_t = gpuspatial::ShaderPointType; - const auto& ids = params.ids; const auto& multi_polygons = params.multi_polygons; - for (uint32_t i = optixGetLaunchIndex().x; i < ids.size(); + for (uint32_t i = optixGetLaunchIndex().x; i < params.query_size; i += optixGetLaunchDimensions().x) { - auto point_idx = ids[i].first; - auto multi_polygon_idx = ids[i].second; + auto point_idx = params.query_point_ids[i]; + auto multi_polygon_idx = params.query_multi_polygon_ids[i]; - auto it = thrust::lower_bound(thrust::seq, params.multi_polygon_ids.begin(), - params.multi_polygon_ids.end(), multi_polygon_idx); - assert(it != params.multi_polygon_ids.end()); + auto it = thrust::lower_bound(thrust::seq, params.uniq_multi_polygon_ids.begin(), + params.uniq_multi_polygon_ids.end(), multi_polygon_idx); + assert(it != params.uniq_multi_polygon_ids.end()); uint32_t reordered_multi_polygon_idx = - thrust::distance(params.multi_polygon_ids.begin(), it); - assert(params.multi_polygon_ids[reordered_multi_polygon_idx] == multi_polygon_idx); + thrust::distance(params.uniq_multi_polygon_ids.begin(), it); + assert(params.uniq_multi_polygon_ids[reordered_multi_polygon_idx] == + multi_polygon_idx); auto handle_point = [&](const point_t& p, uint32_t point_part_id, int& IM) { + assert(!p.empty()); float3 origin; // each polygon takes a z-plane origin.x = p.x(); @@ -108,7 +110,8 @@ extern "C" __global__ void __raygen__gpuspatial() { const auto& mbr = multi_polygon.get_mbr(); auto width = mbr.get_max().x() - mbr.get_min().x(); float tmin = 0; - float tmax = width; + // ensure the floating number is greater than the double + float tmax = next_float_from_double(width, 1, 2); // first polygon offset uint32_t part_offset = multi_polygons.get_prefix_sum_geoms()[multi_polygon_idx]; diff --git a/c/sedona-libgpuspatial/libgpuspatial/src/rt/shaders/point_query.cu b/c/sedona-libgpuspatial/libgpuspatial/src/rt/shaders/point_query.cu index 93f5ceb05..c728b4aa3 100644 --- a/c/sedona-libgpuspatial/libgpuspatial/src/rt/shaders/point_query.cu +++ b/c/sedona-libgpuspatial/libgpuspatial/src/rt/shaders/point_query.cu @@ -14,8 +14,8 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -#include "gpuspatial/index/detail/launch_parameters.h" -#include "shader_config.h" +#include "gpuspatial/rt/launch_parameters.cuh" +#include "shader_config.hpp" #include #include @@ -29,51 +29,38 @@ extern "C" __constant__ extern "C" __global__ void __intersection__gpuspatial() { auto aabb_id = optixGetPrimitiveIndex(); - auto geom2_id = optixGetPayload_0(); - const auto& point = params.points2[geom2_id]; - const auto& mbrs1 = params.mbrs1; + auto point_id = optixGetPayload_0(); + const auto& point = params.points[point_id]; + const auto& rect = params.rects[aabb_id]; - if (params.grouped) { - assert(!params.prefix_sum.empty()); - auto begin = params.prefix_sum[aabb_id]; - auto end = params.prefix_sum[aabb_id + 1]; - - for (auto offset = begin; offset < end; offset++) { - auto geom1_id = params.reordered_indices[offset]; - if (mbrs1.empty()) { - params.ids.Append(thrust::make_pair(geom1_id, geom2_id)); - } else { - const auto& mbr1 = mbrs1[geom1_id]; - - if (mbr1.covers(point.as_float())) { - params.ids.Append(thrust::make_pair(geom1_id, geom2_id)); - } - } - } - } else { - assert(!mbrs1.empty()); - auto geom1_id = aabb_id; - const auto& mbr1 = mbrs1[geom1_id]; - - if (mbr1.covers(point.as_float())) { - params.ids.Append(thrust::make_pair(geom1_id, geom2_id)); + if (rect.covers(point)) { + if (params.count == nullptr) { + auto tail = params.rect_ids.Append(aabb_id); + params.point_ids[tail] = point_id; + } else { + atomicAdd(params.count, 1); } } } extern "C" __global__ void __raygen__gpuspatial() { + using point_t = gpuspatial::ShaderPointType; + constexpr int n_dim = point_t::n_dim; float tmin = 0; float tmax = FLT_MIN; - for (uint32_t i = optixGetLaunchIndex().x; i < params.points2.size(); + for (uint32_t i = optixGetLaunchIndex().x; i < params.points.size(); i += optixGetLaunchDimensions().x) { - const auto& p = params.points2[i]; + const auto& p = params.points[i]; + if (p.empty()) { + continue; + } - float3 origin; + float3 origin{0, 0, 0}; - origin.x = p.get_coordinate(0); - origin.y = p.get_coordinate(1); - origin.z = 0; + for (int dim = 0; dim < n_dim; dim++) { + (&origin.x)[dim] = p.get_coordinate(dim); + } float3 dir = {0, 0, 1}; optixTrace(params.handle, origin, dir, tmin, tmax, 0, OptixVisibilityMask(255), diff --git a/c/sedona-libgpuspatial/libgpuspatial/src/rt/shaders/polygon_point_query.cu b/c/sedona-libgpuspatial/libgpuspatial/src/rt/shaders/polygon_point_query.cu index 97cb948d1..05066d793 100644 --- a/c/sedona-libgpuspatial/libgpuspatial/src/rt/shaders/polygon_point_query.cu +++ b/c/sedona-libgpuspatial/libgpuspatial/src/rt/shaders/polygon_point_query.cu @@ -14,11 +14,11 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -#include "gpuspatial/geom/line_segment.cuh" -#include "gpuspatial/geom/ray_crossing_counter.cuh" -#include "gpuspatial/index/detail/launch_parameters.h" -#include "gpuspatial/relate/relate.cuh" -#include "shader_config.h" +#include "gpuspatial/geom/ray_crossing_counter.hpp" +#include "gpuspatial/relate/relate.hpp" +#include "gpuspatial/rt/launch_parameters.cuh" +#include "gpuspatial/utils/helpers.cuh" +#include "shader_config.hpp" #include #include @@ -41,32 +41,34 @@ extern "C" __global__ void __intersection__gpuspatial() { auto point_on_seg = optixGetPayload_5(); auto point_part_id = optixGetPayload_6(); const auto& polygons = params.polygons; - auto point_idx = params.ids[query_idx].first; - auto polygon_idx = params.ids[query_idx].second; + auto point_idx = params.query_point_ids[query_idx]; + auto polygon_idx = params.query_polygon_ids[query_idx]; auto hit_polygon_idx = params.aabb_poly_ids[aabb_id]; auto hit_ring_idx = params.aabb_ring_ids[aabb_id]; + const auto& vertex_offsets = params.aabb_vertex_offsets[aabb_id]; // the seg being hit is not from the query polygon if (hit_polygon_idx != polygon_idx || hit_ring_idx != ring_idx) { return; } - uint32_t local_v1_idx = aabb_id - params.seg_begins[reordered_polygon_idx]; - uint32_t global_v1_idx = v_offset + local_v1_idx; - uint32_t global_v2_idx = global_v1_idx + 1; + auto ring = polygons[polygon_idx].get_ring(ring_idx); + RayCrossingCounter locator(crossing_count, point_on_seg); - auto vertices = polygons.get_vertices(); - // segment being hit - const auto& v1 = vertices[global_v1_idx]; - const auto& v2 = vertices[global_v2_idx]; + // For each segment in the AABB, count crossings + for (auto vertex_offset = vertex_offsets.first; vertex_offset < vertex_offsets.second; + ++vertex_offset) { + const auto& v1 = ring.get_point(vertex_offset); + const auto& v2 = ring.get_point(vertex_offset + 1); - RayCrossingCounter locator(crossing_count, point_on_seg); - if (!params.points.empty()) { - const auto& p = params.points[point_idx]; - locator.countSegment(p, v1, v2); - } else if (!params.multi_points.empty()) { - const auto& p = params.multi_points[point_idx].get_point(point_part_id); - locator.countSegment(p, v1, v2); + if (!params.points.empty()) { + const auto& p = params.points[point_idx]; + locator.countSegment(p, v1, v2); + } else if (!params.multi_points.empty()) { + const auto& p = params.multi_points[point_idx].get_point(point_part_id); + locator.countSegment(p, v1, v2); + } } + optixSetPayload_4(locator.get_crossing_count()); optixSetPayload_5(locator.get_point_on_segment()); } @@ -74,32 +76,30 @@ extern "C" __global__ void __intersection__gpuspatial() { extern "C" __global__ void __raygen__gpuspatial() { using namespace gpuspatial; using point_t = gpuspatial::ShaderPointType; - const auto& ids = params.ids; const auto& polygons = params.polygons; - for (uint32_t i = optixGetLaunchIndex().x; i < ids.size(); + for (uint32_t i = optixGetLaunchIndex().x; i < params.query_size; i += optixGetLaunchDimensions().x) { - auto point_idx = ids[i].first; - auto polygon_idx = ids[i].second; + auto point_idx = params.query_point_ids[i]; + auto polygon_idx = params.query_polygon_ids[i]; - auto it = thrust::lower_bound(thrust::seq, params.polygon_ids.begin(), - params.polygon_ids.end(), polygon_idx); - assert(it != params.polygon_ids.end()); - uint32_t reordered_polygon_idx = thrust::distance(params.polygon_ids.begin(), it); - assert(params.polygon_ids[reordered_polygon_idx] == polygon_idx); + auto it = thrust::lower_bound(thrust::seq, params.uniq_polygon_ids.begin(), + params.uniq_polygon_ids.end(), polygon_idx); + assert(it != params.uniq_polygon_ids.end()); + uint32_t reordered_polygon_idx = + thrust::distance(params.uniq_polygon_ids.begin(), it); + assert(params.uniq_polygon_ids[reordered_polygon_idx] == polygon_idx); auto handle_point = [&](const point_t& p, uint32_t point_part_id, int& IM) { - float3 origin; - // each polygon takes a z-plane - origin.x = p.x(); - origin.y = p.y(); + assert(!p.empty()); // cast ray toward positive x-axis float3 dir = {1, 0, 0}; const auto& polygon = polygons[polygon_idx]; const auto& mbr = polygon.get_mbr(); auto width = mbr.get_max().x() - mbr.get_min().x(); float tmin = 0; - float tmax = width; + // ensure the floating number is greater than the double + float tmax = next_float_from_double(width, 1, 2); // first polygon offset uint32_t ring_offset = polygons.get_prefix_sum_polygons()[polygon_idx]; @@ -119,7 +119,11 @@ extern "C" __global__ void __raygen__gpuspatial() { IM |= IntersectionMatrix::EXTER_INTER_2D | IntersectionMatrix::EXTER_BOUND_1D; uint32_t ring = 0; locator.Init(); - origin.z = reordered_polygon_idx; + float3 origin; + // each polygon takes a z-plane + origin.x = p.x(); + origin.y = p.y(); + origin.z = polygon_idx; // test exterior optixTrace(params.handle, origin, dir, tmin, tmax, 0, OptixVisibilityMask(255), OPTIX_RAY_FLAG_NONE, // OPTIX_RAY_FLAG_NONE, diff --git a/c/sedona-libgpuspatial/libgpuspatial/src/rt/shaders/ray_params.h b/c/sedona-libgpuspatial/libgpuspatial/src/rt/shaders/ray_params.cuh similarity index 95% rename from c/sedona-libgpuspatial/libgpuspatial/src/rt/shaders/ray_params.h rename to c/sedona-libgpuspatial/libgpuspatial/src/rt/shaders/ray_params.cuh index 447590a26..1e920400b 100644 --- a/c/sedona-libgpuspatial/libgpuspatial/src/rt/shaders/ray_params.h +++ b/c/sedona-libgpuspatial/libgpuspatial/src/rt/shaders/ray_params.cuh @@ -17,9 +17,9 @@ #pragma once -#include "gpuspatial/geom/box.cuh" -#include "gpuspatial/geom/point.cuh" -#include "gpuspatial/utils/cuda_utils.h" +#include "gpuspatial/geom/box.hpp" +#include "gpuspatial/geom/point.hpp" +#include "gpuspatial/utils/cuda_utils.hpp" #include #include diff --git a/c/sedona-libgpuspatial/libgpuspatial/src/rt/shaders/shader_config.h b/c/sedona-libgpuspatial/libgpuspatial/src/rt/shaders/shader_config.hpp similarity index 100% rename from c/sedona-libgpuspatial/libgpuspatial/src/rt/shaders/shader_config.h rename to c/sedona-libgpuspatial/libgpuspatial/src/rt/shaders/shader_config.hpp diff --git a/c/sedona-libgpuspatial/libgpuspatial/src/rt_spatial_index.cu b/c/sedona-libgpuspatial/libgpuspatial/src/rt_spatial_index.cu new file mode 100644 index 000000000..9f76af495 --- /dev/null +++ b/c/sedona-libgpuspatial/libgpuspatial/src/rt_spatial_index.cu @@ -0,0 +1,682 @@ + +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +#include "gpuspatial/index/rt_spatial_index.cuh" +#include "gpuspatial/rt/launch_parameters.cuh" +#include "gpuspatial/utils/launcher.hpp" +#include "gpuspatial/utils/logger.hpp" +#include "gpuspatial/utils/morton_code.hpp" +#include "gpuspatial/utils/stopwatch.hpp" + +#include "rt/shaders/shader_id.hpp" + +#include "rmm/exec_policy.hpp" + +#include +#include +#include +#include + +#define OPTIX_MAX_RAYS (1lu << 30) + +namespace gpuspatial { +namespace detail { + +template +static rmm::device_uvector ComputeAABBs(rmm::cuda_stream_view stream, + const ArrayView>& mbrs) { + rmm::device_uvector aabbs(mbrs.size(), stream); + + thrust::transform(rmm::exec_policy_nosync(stream), mbrs.begin(), mbrs.end(), + aabbs.begin(), [] __device__(const Box& mbr) { + // handle empty boxes + if (mbr.get_min().empty() || mbr.get_max().empty()) { + // empty box + OptixAabb empty_aabb; + empty_aabb.minX = empty_aabb.minY = empty_aabb.minZ = 0.0f; + empty_aabb.maxX = empty_aabb.maxY = empty_aabb.maxZ = -1.0f; + return empty_aabb; + } + return mbr.ToOptixAabb(); + }); + return std::move(aabbs); +} + +template +rmm::device_uvector ComputeAABBs( + rmm::cuda_stream_view stream, rmm::device_uvector& points, + rmm::device_uvector& prefix_sum, + rmm::device_uvector& reordered_indices, int group_size, + rmm::device_uvector>& mbrs) { + using scalar_t = typename POINT_T::scalar_t; + using box_t = Box; + constexpr int n_dim = POINT_T::n_dim; + static_assert(n_dim == 2 || n_dim == 3, "Only 2D and 3D points are supported"); + POINT_T min_world_corner, max_world_corner; + + min_world_corner.set_max(); + max_world_corner.set_min(); + + for (int dim = 0; dim < n_dim; dim++) { + auto min_val = thrust::transform_reduce( + rmm::exec_policy_nosync(stream), points.begin(), points.end(), + [=] __device__(const POINT_T& p) -> scalar_t { return p.get_coordinate(dim); }, + std::numeric_limits::max(), thrust::minimum()); + auto max_val = thrust::transform_reduce( + rmm::exec_policy_nosync(stream), points.begin(), points.end(), + [=] __device__(const POINT_T& p) -> scalar_t { return p.get_coordinate(dim); }, + std::numeric_limits::lowest(), thrust::maximum()); + min_world_corner.set_coordinate(dim, min_val); + max_world_corner.set_coordinate(dim, max_val); + } + + auto np = points.size(); + rmm::device_uvector morton_codes(np, stream); + // compute morton codes and reorder indices + thrust::transform(rmm::exec_policy_nosync(stream), points.begin(), points.end(), + morton_codes.begin(), [=] __device__(const POINT_T& p) { + POINT_T norm_p; + + for (int dim = 0; dim < n_dim; dim++) { + auto min_val = min_world_corner.get_coordinate(dim); + auto max_val = max_world_corner.get_coordinate(dim); + auto extent = min_val == max_val ? 1 : max_val - min_val; + auto norm_val = (p.get_coordinate(dim) - min_val) / extent; + norm_p.set_coordinate(dim, norm_val); + } + return detail::morton_code(norm_p.get_vec()); + }); + reordered_indices.resize(np, stream); + thrust::sequence(rmm::exec_policy_nosync(stream), reordered_indices.begin(), + reordered_indices.end()); + thrust::sort_by_key(rmm::exec_policy_nosync(stream), morton_codes.begin(), + morton_codes.end(), reordered_indices.begin()); + auto n_aabbs = (np + group_size - 1) / group_size; + mbrs.resize(n_aabbs, stream); + rmm::device_uvector aabbs(n_aabbs, stream); + rmm::device_uvector np_per_aabb(n_aabbs, stream); + + auto* p_reordered_indices = reordered_indices.data(); + auto* p_aabbs = aabbs.data(); + auto* p_np_per_aabb = np_per_aabb.data(); + ArrayView v_points(points); + ArrayView v_mbrs(mbrs); + // each warp takes an AABB and processes points_per_aabb points + LaunchKernel(stream, [=] __device__() mutable { + using WarpReduce = cub::WarpReduce; + // One temp storage slot per active warp + __shared__ typename WarpReduce::TempStorage temp_storage[MAX_BLOCK_SIZE / 32]; + const int warp_id = threadIdx.x / 32; + const int lane_id = threadIdx.x % 32; + // Calculate global ID of the warp to stride through AABBs + const int global_warp_id = (blockIdx.x * blockDim.x + threadIdx.x) / 32; + const int total_warps = (gridDim.x * blockDim.x) / 32; + + // Grid-Stride Loop: Each warp processes one AABB (one group of points) + for (uint32_t aabb_id = global_warp_id; aabb_id < n_aabbs; aabb_id += total_warps) { + INDEX_T idx_begin = aabb_id * group_size; + INDEX_T idx_end = thrust::min((INDEX_T)np, (INDEX_T)(idx_begin + group_size)); + int count = idx_end - idx_begin; + + // 1. Initialize Thread-Local Accumulators (Registers) + // Initialize to limits so empty/out-of-bounds threads don't affect reduction + scalar_t thread_min[n_dim]; + scalar_t thread_max[n_dim]; + +#pragma unroll + for (int d = 0; d < n_dim; d++) { + thread_min[d] = std::numeric_limits::max(); + thread_max[d] = std::numeric_limits::lowest(); + } + + // 2. Loop over the points in the group (Stride by 32) + // Every thread processes roughly group_size/32 points + for (int i = lane_id; i < count; i += 32) { + // Load index (Coalesced access to indices) + INDEX_T point_idx = p_reordered_indices[idx_begin + i]; + + // Load Point (Indirect access - unavoidable due to reordering) + const POINT_T& p = v_points[point_idx]; + +// Accumulate min/max locally in registers +#pragma unroll + for (int d = 0; d < n_dim; d++) { + scalar_t val = p.get_coordinate(d); + thread_min[d] = thrust::min(thread_min[d], val); + thread_max[d] = thrust::max(thread_max[d], val); + } + } + + // 3. Warp Reduction (Perform once per dimension per AABB) + POINT_T final_min, final_max; +#pragma unroll + for (int d = 0; d < n_dim; d++) { + // CUB WarpReduce handles the cross-lane communication + scalar_t agg_min = + WarpReduce(temp_storage[warp_id]).Reduce(thread_min[d], thrust::minimum<>()); + scalar_t agg_max = + WarpReduce(temp_storage[warp_id]).Reduce(thread_max[d], thrust::maximum<>()); + + // Only lane 0 holds the valid reduction result + if (lane_id == 0) { + final_min.set_coordinate(d, agg_min); + final_max.set_coordinate(d, agg_max); + } + } + + // 4. Store Results to Global Memory + if (lane_id == 0) { + p_np_per_aabb[aabb_id] = count; + + if (count > 0) { + box_t ext_mbr(final_min, final_max); + v_mbrs[aabb_id] = ext_mbr; + p_aabbs[aabb_id] = ext_mbr.ToOptixAabb(); + } else { + // Handle empty AABB case + OptixAabb empty_aabb; + empty_aabb.minX = empty_aabb.minY = empty_aabb.minZ = 0.0f; + empty_aabb.maxX = empty_aabb.maxY = empty_aabb.maxZ = -1.0f; + v_mbrs[aabb_id] = box_t(); + p_aabbs[aabb_id] = empty_aabb; + } + } + } + }); + prefix_sum.resize(n_aabbs + 1, stream); + prefix_sum.set_element_to_zero_async(0, stream); + thrust::inclusive_scan(rmm::exec_policy_nosync(stream), np_per_aabb.begin(), + np_per_aabb.end(), prefix_sum.begin() + 1); +#ifndef NDEBUG + auto* p_prefix_sum = prefix_sum.data(); + + thrust::for_each(rmm::exec_policy_nosync(stream), thrust::counting_iterator(0), + thrust::counting_iterator(aabbs.size()), + [=] __device__(size_t aabb_idx) { + auto begin = p_prefix_sum[aabb_idx]; + auto end = p_prefix_sum[aabb_idx + 1]; + const auto& aabb = p_aabbs[aabb_idx]; + + for (auto i = begin; i < end; i++) { + auto point_idx = p_reordered_indices[i]; + const auto& p = v_points[point_idx]; + for (int dim = 0; dim < n_dim; dim++) { + auto coord = p.get_coordinate(dim); + assert(coord >= (&aabb.minX)[dim] && coord <= (&aabb.maxX)[dim]); + assert(v_mbrs[aabb_idx].covers(p)); + } + } + }); +#endif + return std::move(aabbs); +} + +template +void RefineExactPoints(rmm::cuda_stream_view stream, ArrayView build_points, + ArrayView probe_points, ArrayView prefix_sum, + ArrayView reordered_indices, ArrayView rect_ids, + ArrayView point_ids, Queue& build_indices, + ArrayView probe_indices) { + auto d_queue = build_indices.DeviceObject(); + + LaunchKernel(stream, [=] __device__() mutable { + auto lane_id = threadIdx.x % 32; + auto global_warp_id = TID_1D / 32; + auto n_warps = TOTAL_THREADS_1D / 32; + + for (uint32_t i = global_warp_id; i < rect_ids.size(); i += n_warps) { + auto rect_id = rect_ids[i]; + auto point_id = point_ids[i]; + auto build_point_begin = prefix_sum[rect_id]; + auto build_point_end = prefix_sum[rect_id + 1]; + + for (uint32_t j = lane_id + build_point_begin; j < build_point_end; + j += WARP_SIZE) { + auto build_point_id = reordered_indices[j]; + const auto& build_point = build_points[build_point_id]; + const auto& probe_point = probe_points[point_id]; + if (build_point == probe_point) { + auto tail = d_queue.Append(build_point_id); + probe_indices[tail] = point_id; + } + } + } + }); +} +} // namespace detail + +template +RTSpatialIndex::RTSpatialIndex(const RTSpatialIndexConfig& config) + : config_(config), + stream_pool_(std::make_unique(config_.concurrency)), + indexing_points_(false), + handle_(0) {} + +template +void RTSpatialIndex::Clear() { + GPUSPATIAL_LOG_INFO("RTSpatialIndex %p (Free %zu MB), Clear", this, + rmm::available_device_memory().first / 1024 / 1024); + auto stream = rmm::cuda_stream_default; + bvh_buffer_.resize(0, stream); + bvh_buffer_.shrink_to_fit(stream); + rects_.resize(0, stream); + rects_.shrink_to_fit(stream); + points_.resize(0, stream); + points_.shrink_to_fit(stream); + stream.synchronize(); +} + +template +void RTSpatialIndex::PushBuild(const box_t* rects, uint32_t n_rects) { + GPUSPATIAL_LOG_INFO("RTSpatialIndex %p (Free %zu MB), PushBuild, rectangles %zu", this, + rmm::available_device_memory().first / 1024 / 1024, n_rects); + if (n_rects == 0) return; + auto stream = rmm::cuda_stream_default; + auto prev_size = rects_.size(); + + rects_.resize(rects_.size() + n_rects, stream); + CUDA_CHECK(cudaMemcpyAsync(rects_.data() + prev_size, rects, sizeof(box_t) * n_rects, + cudaMemcpyHostToDevice, stream)); +} + +template +void RTSpatialIndex::FinishBuilding() { + auto stream = rmm::cuda_stream_default; + + indexing_points_ = thrust::all_of(rmm::exec_policy_nosync(stream), rects_.begin(), + rects_.end(), [] __device__(const box_t& box) { + bool is_point = true; + for (int dim = 0; dim < n_dim; dim++) { + is_point &= box.get_min(dim) == box.get_max(dim); + } + return is_point; + }); + + rmm::device_uvector aabbs{0, stream}; + if (indexing_points_) { + points_.resize(rects_.size(), stream); + thrust::transform(rmm::exec_policy_nosync(stream), rects_.begin(), rects_.end(), + points_.begin(), + [] __device__(const box_t& box) { return box.get_min(); }); + aabbs = std::move(detail::ComputeAABBs(stream, points_, point_ranges_, + reordered_point_indices_, + config_.n_points_per_aabb, rects_)); + } else { + aabbs = std::move(detail::ComputeAABBs(stream, ArrayView(rects_))); + } + + handle_ = config_.rt_engine->BuildAccelCustom(stream, ArrayView(aabbs), + bvh_buffer_, config_.prefer_fast_build, + config_.compact); + + GPUSPATIAL_LOG_INFO( + "RTSpatialIndex %p (Free %zu MB), FinishBuilding Index on %s, Total geoms: %zu", + this, rmm::available_device_memory().first / 1024 / 1024, + indexing_points_ ? "Points" : "Rectangles", numGeometries()); +} + +template +void RTSpatialIndex::Probe(const box_t* rects, uint32_t n_rects, + std::vector* build_indices, + std::vector* probe_indices) { + // Formulating point and box queries into ray tracing queries: + // Reference: "Geng L, Lee R, Zhang X. LibRTS: A Spatial Indexing Library by Ray + // Tracing. InProceedings of the 30th ACM SIGPLAN Annual Symposium on Principles and + // Practice of Parallel Programming 2025" + if (n_rects == 0) return; + SpatialIndexContext ctx; + auto stream = stream_pool_->get_stream(); + rmm::device_uvector d_rects(n_rects, stream); + rmm::device_uvector d_points{0, stream}; + + CUDA_CHECK(cudaMemcpyAsync(d_rects.data(), rects, sizeof(box_t) * n_rects, + cudaMemcpyHostToDevice, stream)); + + bool probe_points = thrust::all_of(rmm::exec_policy_nosync(stream), d_rects.begin(), + d_rects.end(), [] __device__(const box_t& box) { + bool is_point = true; + for (int dim = 0; dim < n_dim; dim++) { + is_point &= box.get_min(dim) == box.get_max(dim); + } + return is_point; + }); + + if (probe_points) { + d_points.resize(d_rects.size(), stream); + thrust::transform(rmm::exec_policy_nosync(stream), d_rects.begin(), d_rects.end(), + d_points.begin(), + [] __device__(const box_t& box) { return box.get_min(); }); + d_rects.resize(0, stream); + d_rects.shrink_to_fit(stream); + + } else { + // Build a BVH over the MBRs of the stream geometries +#ifdef GPUSPATIAL_PROFILING + ctx.timer.start(stream); +#endif + rmm::device_uvector aabbs(n_rects, stream); + thrust::transform(rmm::exec_policy_nosync(stream), d_rects.begin(), d_rects.end(), + aabbs.begin(), + [] __device__(const box_t& mbr) { return mbr.ToOptixAabb(); }); + ctx.handle = config_.rt_engine->BuildAccelCustom( + stream, ArrayView(aabbs), ctx.bvh_buffer, config_.prefer_fast_build, + config_.compact); +#ifdef GPUSPATIAL_PROFILING + ctx.bvh_build_ms = ctx.timer.stop(stream); +#endif + } + + ctx.counter = std::make_unique>(0, stream); + + bool swap_ids = false; + + auto query = [&](bool counting) { +#ifdef GPUSPATIAL_PROFILING + ctx.timer.start(stream); +#endif + if (indexing_points_) { + if (probe_points) { + handleBuildPoint(ctx, ArrayView(d_points), counting); + } else { + handleBuildPoint(ctx, ArrayView(d_rects), counting); + swap_ids = true; + } + } else { + if (probe_points) { + handleBuildBox(ctx, ArrayView(d_points), counting); + } else { + handleBuildBox(ctx, ArrayView(d_rects), counting); + } + } +#ifdef GPUSPATIAL_PROFILING + ctx.rt_ms += ctx.timer.stop(stream); +#endif + }; + + // first pass: counting + query(true /* counting */); + + auto cap = ctx.counter->value(stream); + if (cap == 0) { + return; + } + allocateResultBuffer(ctx, cap); + // second pass: retrieve results + query(false /* counting */); + + auto result_size = ctx.build_indices.size(stream); + ArrayView v_build_indices(ctx.build_indices.data(), result_size); + ArrayView v_probe_indices(ctx.probe_indices.data(), result_size); + + if (swap_ids) { + // IMPORTANT: In this case, the BVH is built on probe side and points are + // cast on the build side, so the result pairs are (probe_id, build_id) instead of + // (build_id, probe_id). We need to swap the output buffers to correct this. + std::swap(v_build_indices, v_probe_indices); + } + +#ifdef GPUSPATIAL_PROFILING + Stopwatch sw; + sw.start(); +#endif + build_indices->resize(result_size); + CUDA_CHECK(cudaMemcpyAsync(build_indices->data(), v_build_indices.data(), + sizeof(index_t) * result_size, cudaMemcpyDeviceToHost, + stream)); + + probe_indices->resize(result_size); + CUDA_CHECK(cudaMemcpyAsync(probe_indices->data(), v_probe_indices.data(), + sizeof(index_t) * result_size, cudaMemcpyDeviceToHost, + stream)); + stream.synchronize(); +#ifdef GPUSPATIAL_PROFILING + sw.stop(); + ctx.copy_res_ms = sw.ms(); + GPUSPATIAL_LOG_INFO( + "RTSpatialIndex %p (Free %zu MB), Probe %s, Size: %zu, Results: %zu, Alloc: %.2f ms, BVH Build: %.2f ms, RT: %.2f ms, Copy res: %.2f ms", + this, rmm::available_device_memory().first / 1024 / 1024, + probe_points ? "Points" : "Rectangles", + probe_points ? d_points.size() : d_rects.size(), build_indices->size(), + ctx.alloc_ms, ctx.bvh_build_ms, ctx.rt_ms, ctx.copy_res_ms); +#endif +} + +template +void RTSpatialIndex::handleBuildPoint(SpatialIndexContext& ctx, + ArrayView points, + bool counting) const { + using launch_params_t = detail::LaunchParamsPointQuery; + + ctx.shader_id = GetPointQueryShaderId(); + ctx.launch_params_buffer.resize(sizeof(launch_params_t), ctx.stream); + ctx.h_launch_params_buffer.resize(sizeof(launch_params_t)); + auto& launch_params = + *reinterpret_cast(ctx.h_launch_params_buffer.data()); + + launch_params.rects = ArrayView(rects_); + launch_params.points = points; + launch_params.handle = handle_; + + uint32_t dim_x = std::min(OPTIX_MAX_RAYS, points.size()); + + if (counting) { + launch_params.count = ctx.counter->data(); + + CUDA_CHECK(cudaMemcpyAsync(ctx.launch_params_buffer.data(), &launch_params, + sizeof(launch_params_t), cudaMemcpyHostToDevice, + ctx.stream)); + + filter(ctx, dim_x); + } else { + auto cap = ctx.build_indices.capacity(); + Queue rect_ids; + rmm::device_uvector point_ids(cap, ctx.stream); + + rect_ids.Init(ctx.stream, cap); + + launch_params.count = nullptr; + launch_params.rect_ids = rect_ids.DeviceObject(); + launch_params.point_ids = ArrayView(point_ids); + + CUDA_CHECK(cudaMemcpyAsync(ctx.launch_params_buffer.data(), &launch_params, + sizeof(launch_params_t), cudaMemcpyHostToDevice, + ctx.stream)); + + filter(ctx, dim_x); + + detail::RefineExactPoints( + ctx.stream, ArrayView(points_), points, + ArrayView(point_ranges_), ArrayView(reordered_point_indices_), + ArrayView(rect_ids.data(), rect_ids.size(ctx.stream)), + ArrayView(point_ids), ctx.build_indices, + ArrayView(ctx.probe_indices)); + } +} + +template +void RTSpatialIndex::handleBuildPoint(SpatialIndexContext& ctx, + ArrayView rects, + bool counting) const { + using launch_params_t = detail::LaunchParamsPointQuery; + + ctx.shader_id = GetPointQueryShaderId(); + ctx.launch_params_buffer.resize(sizeof(launch_params_t), ctx.stream); + ctx.h_launch_params_buffer.resize(sizeof(launch_params_t)); + auto& launch_params = + *reinterpret_cast(ctx.h_launch_params_buffer.data()); + + launch_params.rects = rects; + launch_params.points = ArrayView(points_); + launch_params.handle = ctx.handle; + if (counting) { + launch_params.count = ctx.counter->data(); + } else { + launch_params.count = nullptr; + launch_params.rect_ids = ctx.build_indices.DeviceObject(); + launch_params.point_ids = ArrayView(ctx.probe_indices); + } + + CUDA_CHECK(cudaMemcpyAsync(ctx.launch_params_buffer.data(), &launch_params, + sizeof(launch_params_t), cudaMemcpyHostToDevice, + ctx.stream)); + + uint32_t dim_x = std::min(OPTIX_MAX_RAYS, points_.size()); + + filter(ctx, dim_x); +} + +template +void RTSpatialIndex::handleBuildBox(SpatialIndexContext& ctx, + ArrayView points, + bool counting) const { + using launch_params_t = detail::LaunchParamsPointQuery; + + ctx.shader_id = GetPointQueryShaderId(); + ctx.launch_params_buffer.resize(sizeof(launch_params_t), ctx.stream); + ctx.h_launch_params_buffer.resize(sizeof(launch_params_t)); + auto& launch_params = + *reinterpret_cast(ctx.h_launch_params_buffer.data()); + + launch_params.rects = ArrayView(rects_); + launch_params.points = points; + launch_params.handle = handle_; + if (counting) { + launch_params.count = ctx.counter->data(); + } else { + launch_params.count = nullptr; + launch_params.rect_ids = ctx.build_indices.DeviceObject(); + launch_params.point_ids = + ArrayView(ctx.probe_indices.data(), ctx.probe_indices.size()); + } + + CUDA_CHECK(cudaMemcpyAsync(ctx.launch_params_buffer.data(), &launch_params, + sizeof(launch_params_t), cudaMemcpyHostToDevice, + ctx.stream)); + + uint32_t dim_x = std::min(OPTIX_MAX_RAYS, points.size()); + + filter(ctx, dim_x); +} + +template +void RTSpatialIndex::handleBuildBox(SpatialIndexContext& ctx, + ArrayView rects, + bool counting) const { + // forward cast: cast rays from stream geometries with the BVH of build geometries + { + auto dim_x = std::min(OPTIX_MAX_RAYS, rects.size()); + + prepareLaunchParamsBoxQuery(ctx, rects, true /* forward */, counting); + filter(ctx, dim_x); + } + // backward cast: cast rays from the build geometries with the BVH of stream geometries + { + auto dim_x = std::min(OPTIX_MAX_RAYS, rects_.size()); + + prepareLaunchParamsBoxQuery(ctx, rects, false /* forward */, counting); + filter(ctx, dim_x); + } +} + +template +void RTSpatialIndex::allocateResultBuffer(SpatialIndexContext& ctx, + uint32_t capacity) const { +#ifdef GPUSPATIAL_PROFILING + ctx.timer.start(ctx.stream); +#endif + + GPUSPATIAL_LOG_INFO( + "RTSpatialIndex %p (Free %zu MB), Allocate result buffer, memory consumption %zu MB, capacity %u", + this, rmm::available_device_memory().first / 1024 / 1024, + (uint64_t)capacity * 2 * sizeof(index_t) / 1024 / 1024, capacity); + + ctx.build_indices.Init(ctx.stream, capacity); + ctx.probe_indices.resize(capacity, ctx.stream); +#ifdef GPUSPATIAL_PROFILING + ctx.alloc_ms += ctx.timer.stop(ctx.stream); +#endif +} + +template +void RTSpatialIndex::prepareLaunchParamsBoxQuery( + SpatialIndexContext& ctx, ArrayView probe_rects, bool forward, + bool counting) const { + using launch_params_t = detail::LaunchParamsBoxQuery; + ctx.launch_params_buffer.resize(sizeof(launch_params_t), ctx.stream); + ctx.h_launch_params_buffer.resize(sizeof(launch_params_t)); + auto& launch_params = + *reinterpret_cast(ctx.h_launch_params_buffer.data()); + + launch_params.rects1 = ArrayView(rects_); + launch_params.rects2 = probe_rects; + + if (forward) { + launch_params.handle = handle_; + ctx.shader_id = GetBoxQueryForwardShaderId(); + } else { + launch_params.handle = ctx.handle; + ctx.shader_id = GetBoxQueryBackwardShaderId(); + } + + if (counting) { + launch_params.count = ctx.counter->data(); + } else { + launch_params.count = nullptr; + launch_params.rect1_ids = ctx.build_indices.DeviceObject(); + launch_params.rect2_ids = ArrayView(ctx.probe_indices); + } + + CUDA_CHECK(cudaMemcpyAsync(ctx.launch_params_buffer.data(), &launch_params, + sizeof(launch_params_t), cudaMemcpyHostToDevice, + ctx.stream)); +} + +template +void RTSpatialIndex::filter(SpatialIndexContext& ctx, + uint32_t dim_x) const { +#ifdef GPUSPATIAL_PROFILING + ctx.timer.start(ctx.stream); +#endif + if (dim_x > 0) { + config_.rt_engine->Render(ctx.stream, ctx.shader_id, dim3{dim_x, 1, 1}, + ArrayView((char*)ctx.launch_params_buffer.data(), + ctx.launch_params_buffer.size())); + } +#ifdef GPUSPATIAL_PROFILING + ctx.rt_ms += ctx.timer.stop(ctx.stream); +#endif +} + +template +std::unique_ptr> CreateRTSpatialIndex( + const RTSpatialIndexConfig& config) { + auto index = std::make_unique>(config); + GPUSPATIAL_LOG_INFO( + "Create RTSpatialIndex %p, fast_build = %d, compact = %d, concurrency = %d", + index.get(), config.prefer_fast_build, config.compact, config.concurrency); + return std::move(index); +} + +template std::unique_ptr> CreateRTSpatialIndex( + const RTSpatialIndexConfig& config); +template std::unique_ptr> CreateRTSpatialIndex( + const RTSpatialIndexConfig& config); +template std::unique_ptr> CreateRTSpatialIndex( + const RTSpatialIndexConfig& config); +template std::unique_ptr> CreateRTSpatialIndex( + const RTSpatialIndexConfig& config); +} // namespace gpuspatial diff --git a/c/sedona-libgpuspatial/libgpuspatial/src/rt_spatial_refiner.cu b/c/sedona-libgpuspatial/libgpuspatial/src/rt_spatial_refiner.cu new file mode 100644 index 000000000..af74e688a --- /dev/null +++ b/c/sedona-libgpuspatial/libgpuspatial/src/rt_spatial_refiner.cu @@ -0,0 +1,548 @@ + +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +#include "gpuspatial/loader/parallel_wkb_loader.hpp" +#include "gpuspatial/refine/rt_spatial_refiner.cuh" +#include "gpuspatial/relate/relate_engine.cuh" +#include "gpuspatial/utils/logger.hpp" + +#include "rt/shaders/shader_id.hpp" + +#include "rmm/cuda_stream_pool.hpp" +#include "rmm/exec_policy.hpp" + + +#include +#include +#include + +#include +#include +#include +#include + +#define OPTIX_MAX_RAYS (1lu << 30) + +namespace gpuspatial { + +namespace detail { +template +void ReorderIndices(rmm::cuda_stream_view stream, INDEX_IT index_begin, + INDEX_IT index_end, + rmm::device_uvector& sorted_uniq_indices, + rmm::device_uvector& reordered_indices) { + auto sorted_begin = sorted_uniq_indices.begin(); + auto sorted_end = sorted_uniq_indices.end(); + thrust::transform(rmm::exec_policy_nosync(stream), index_begin, index_end, + reordered_indices.begin(), [=] __device__(uint32_t val) { + auto it = + thrust::lower_bound(thrust::seq, sorted_begin, sorted_end, val); + return thrust::distance(sorted_begin, it); + }); +} + +template +struct PipelineSlot { + rmm::cuda_stream_view stream; + std::unique_ptr loader; + std::future prep_future; + + RTSpatialRefiner::IndicesMap indices_map; + + // These will be moved out after every batch + rmm::device_uvector d_batch_build_indices; + rmm::device_uvector d_batch_probe_indices; + + PipelineSlot(rmm::cuda_stream_view s, const std::shared_ptr& tp, + typename LoaderT::Config config) + : stream(s), d_batch_build_indices(0, s), d_batch_probe_indices(0, s) { + loader = std::make_unique(tp); + loader->Init(config); + } +}; +} // namespace detail + +RTSpatialRefiner::RTSpatialRefiner(const RTSpatialRefinerConfig& config) + : config_(config) { + thread_pool_ = std::make_shared(config_.parsing_threads); + stream_pool_ = std::make_unique(config_.concurrency); + CUDA_CHECK(cudaDeviceSetLimit(cudaLimitStackSize, config_.stack_size_bytes)); + wkb_loader_ = std::make_unique(thread_pool_); + + ParallelWkbLoader::Config loader_config; + + loader_config.memory_quota = config_.wkb_parser_memory_quota; + + wkb_loader_->Init(loader_config); +} + +void RTSpatialRefiner::Clear() { + auto stream = rmm::cuda_stream_default; + wkb_loader_->Clear(stream); + build_geometries_.Clear(stream); +} + +void RTSpatialRefiner::PushBuild(const ArrowSchema* build_schema, + const ArrowArray* build_array) { + auto stream = rmm::cuda_stream_default; + + wkb_loader_->Parse(stream, build_schema, build_array, 0, build_array->length); +} + +void RTSpatialRefiner::FinishBuilding() { + auto stream = rmm::cuda_stream_default; + build_geometries_ = std::move(wkb_loader_->Finish(stream)); +} + +uint32_t RTSpatialRefiner::Refine(const ArrowSchema* probe_schema, + const ArrowArray* probe_array, Predicate predicate, + uint32_t* build_indices, uint32_t* probe_indices, + uint32_t len) { + if (len == 0) { + return 0; + } + + if (config_.pipeline_batches > 1) { + return RefinePipelined(probe_schema, probe_array, predicate, build_indices, + probe_indices, len); + } + + SpatialRefinerContext ctx; + ctx.cuda_stream = stream_pool_->get_stream(); + + IndicesMap probe_indices_map; + rmm::device_uvector d_probe_indices(len, ctx.cuda_stream); + + CUDA_CHECK(cudaMemcpyAsync(d_probe_indices.data(), probe_indices, + sizeof(uint32_t) * len, cudaMemcpyHostToDevice, + ctx.cuda_stream)); + + buildIndicesMap(ctx.cuda_stream, d_probe_indices.begin(), d_probe_indices.end(), + probe_indices_map); + + loader_t loader(thread_pool_); + loader_t::Config loader_config; + loader_config.memory_quota = config_.wkb_parser_memory_quota / config_.concurrency; + + loader.Init(loader_config); + loader.Parse(ctx.cuda_stream, probe_schema, probe_array, + probe_indices_map.h_uniq_indices.begin(), + probe_indices_map.h_uniq_indices.end()); + auto probe_geoms = std::move(loader.Finish(ctx.cuda_stream)); + + GPUSPATIAL_LOG_INFO( + "RTSpatialRefiner %p (Free %zu MB), Loaded Geometries, ProbeArray %ld, Loaded %u, Type %s", + this, rmm::available_device_memory().first / 1024 / 1024, probe_array->length, + probe_geoms.num_features(), + GeometryTypeToString(probe_geoms.get_geometry_type()).c_str()); + + RelateEngine relate_engine(&build_geometries_, + config_.rt_engine.get()); + RelateEngine::Config re_config; + + re_config.memory_quota = config_.relate_engine_memory_quota / config_.concurrency; + re_config.bvh_fast_build = config_.prefer_fast_build; + re_config.bvh_compact = config_.compact; + + relate_engine.set_config(re_config); + + rmm::device_uvector d_build_indices(len, ctx.cuda_stream); + CUDA_CHECK(cudaMemcpyAsync(d_build_indices.data(), build_indices, + sizeof(uint32_t) * len, cudaMemcpyHostToDevice, + ctx.cuda_stream)); + + GPUSPATIAL_LOG_INFO( + "RTSpatialRefiner %p (Free %zu MB), Evaluating %u Geometry Pairs with Predicate %s", + this, rmm::available_device_memory().first / 1024 / 1024, len, + PredicateToString(predicate)); + + ctx.timer.start(ctx.cuda_stream); + relate_engine.Evaluate(ctx.cuda_stream, probe_geoms, predicate, d_build_indices, + probe_indices_map.d_reordered_indices); + float refine_ms = ctx.timer.stop(ctx.cuda_stream); + auto new_size = d_build_indices.size(); + + GPUSPATIAL_LOG_INFO("RTSpatialRefiner %p (Free %zu MB), Refine time %f, new size %zu", + this, rmm::available_device_memory().first / 1024 / 1024, refine_ms, + new_size); + + d_probe_indices.resize(new_size, ctx.cuda_stream); + + thrust::gather(rmm::exec_policy_nosync(ctx.cuda_stream), + probe_indices_map.d_reordered_indices.begin(), + probe_indices_map.d_reordered_indices.end(), + probe_indices_map.d_uniq_indices.begin(), d_probe_indices.begin()); + + if (config_.sort_probe_indices) { + thrust::sort_by_key(rmm::exec_policy_nosync(ctx.cuda_stream), d_probe_indices.begin(), + d_probe_indices.end(), d_build_indices.begin()); + } + + CUDA_CHECK(cudaMemcpyAsync(build_indices, d_build_indices.data(), + sizeof(uint32_t) * new_size, cudaMemcpyDeviceToHost, + ctx.cuda_stream)); + + CUDA_CHECK(cudaMemcpyAsync(probe_indices, d_probe_indices.data(), + sizeof(uint32_t) * new_size, cudaMemcpyDeviceToHost, + ctx.cuda_stream)); + ctx.cuda_stream.synchronize(); + return new_size; +} + +uint32_t RTSpatialRefiner::RefinePipelined(const ArrowSchema* probe_schema, + const ArrowArray* probe_array, + Predicate predicate, uint32_t* build_indices, + uint32_t* probe_indices, uint32_t len) { + if (len == 0) return 0; + auto main_stream = stream_pool_->get_stream(); + + rmm::device_uvector d_build_indices(len, main_stream); + rmm::device_uvector d_probe_indices(len, main_stream); + + CUDA_CHECK(cudaMemcpyAsync(d_build_indices.data(), build_indices, + sizeof(uint32_t) * len, cudaMemcpyHostToDevice, + main_stream)); + CUDA_CHECK(cudaMemcpyAsync(d_probe_indices.data(), probe_indices, + sizeof(uint32_t) * len, cudaMemcpyHostToDevice, + main_stream)); + + thrust::sort_by_key(rmm::exec_policy_nosync(main_stream), d_probe_indices.begin(), + d_probe_indices.end(), d_build_indices.begin()); + + rmm::device_uvector d_final_build_indices(len, main_stream); + rmm::device_uvector d_final_probe_indices(len, main_stream); + + uint32_t tail_offset = 0; + + // Capture device ID for thread safety + int device_id; + CUDA_CHECK(cudaGetDevice(&device_id)); + + // Pipeline Config + const int NUM_SLOTS = 2; + int n_batches = config_.pipeline_batches; + size_t batch_size = (len + n_batches - 1) / n_batches; + + GPUSPATIAL_LOG_INFO( + "RTSpatialRefiner %p, pipeline refinement, total len %u, batches %d, batch size %zu", + this, len, n_batches, batch_size); + + // Resource allocation for slots + using loader_t = ParallelWkbLoader; + loader_t::Config loader_config; + loader_config.memory_quota = + config_.wkb_parser_memory_quota / config_.concurrency / NUM_SLOTS; + + rmm::cuda_stream_pool local_pool(NUM_SLOTS); + std::vector>> slots; + + for (int i = 0; i < NUM_SLOTS; ++i) { + slots.push_back(std::make_unique>( + local_pool.get_stream(), thread_pool_, loader_config)); + } + + // Engine Setup (Shared across slots) + RelateEngine relate_engine(&build_geometries_, + config_.rt_engine.get()); + RelateEngine::Config re_config; + re_config.memory_quota = + config_.relate_engine_memory_quota / config_.concurrency / NUM_SLOTS; + re_config.bvh_fast_build = config_.prefer_fast_build; + re_config.bvh_compact = config_.compact; + relate_engine.set_config(re_config); + + // --- BACKGROUND TASK (CPU Phase) --- + // This lambda handles: buildIndicesMap + WKB Parsing + auto prepare_batch_task = [&](detail::PipelineSlot* slot, + size_t offset, size_t count) { + // 1. Critical: Set context for this thread + CUDA_CHECK(cudaSetDevice(device_id)); + + // 2. Wait for GPU to finish previous work on this slot + slot->stream.synchronize(); + + // 3. Prepare Indices (CPU + H2D) + const uint32_t* batch_probe_ptr = d_probe_indices.data() + offset; + buildIndicesMap(slot->stream, batch_probe_ptr, batch_probe_ptr + count, + slot->indices_map); + + // 4. Parse WKB (CPU Heavy) + slot->loader->Clear(slot->stream); + slot->loader->Parse(slot->stream, probe_schema, probe_array, + slot->indices_map.h_uniq_indices.begin(), + slot->indices_map.h_uniq_indices.end()); + + // Return future geometries (H2D copy happens on Finish) + return slot->loader->Finish(slot->stream); + }; + + // --- PIPELINE PRIMING --- + // Start processing Batch 0 immediately in background + size_t first_batch_len = std::min(batch_size, (size_t)len); + slots[0]->prep_future = std::async(std::launch::async, prepare_batch_task, + slots[0].get(), 0, first_batch_len); + + main_stream.synchronize(); // Ensure allocation is done before main loop + + // --- MAIN PIPELINE LOOP --- + for (size_t offset = 0; offset < len; offset += batch_size) { + int curr_idx = (offset / batch_size) % NUM_SLOTS; + int next_idx = (curr_idx + 1) % NUM_SLOTS; + auto& curr_slot = slots[curr_idx]; + auto& next_slot = slots[next_idx]; + size_t current_batch_len = std::min(batch_size, len - offset); + + // 1. WAIT & RETRIEVE: Get Geometries from Background Task + // This will block only if CPU work for this batch is slower than GPU work for + // previous batch + dev_geometries_t probe_geoms; + if (curr_slot->prep_future.valid()) { + probe_geoms = std::move(curr_slot->prep_future.get()); + } + + // 2. KICKOFF NEXT: Start CPU work for Batch (N+1) + size_t next_offset = offset + batch_size; + if (next_offset < len) { + size_t next_len = std::min(batch_size, len - next_offset); + next_slot->prep_future = std::async(std::launch::async, prepare_batch_task, + next_slot.get(), next_offset, next_len); + } + + // 3. GPU EXECUTION PHASE + const uint32_t* batch_build_ptr = d_build_indices.data() + offset; + + // Copy build indices for this batch + curr_slot->d_batch_build_indices.resize(current_batch_len, curr_slot->stream); + CUDA_CHECK(cudaMemcpyAsync(curr_slot->d_batch_build_indices.data(), batch_build_ptr, + sizeof(uint32_t) * current_batch_len, + cudaMemcpyHostToDevice, curr_slot->stream)); + + // Relate/Refine + // Note: Evaluate filters d_batch_build_indices in-place + relate_engine.Evaluate(curr_slot->stream, probe_geoms, predicate, + curr_slot->d_batch_build_indices, + curr_slot->indices_map.d_reordered_indices); + + // 4. GATHER & APPEND RESULTS + // We need the size to know how much to gather + size_t new_size = curr_slot->d_batch_build_indices.size(); + + if (new_size > 0) { + // Gather original probe indices + curr_slot->d_batch_probe_indices.resize(new_size, curr_slot->stream); + thrust::gather(rmm::exec_policy_nosync(curr_slot->stream), + curr_slot->indices_map.d_reordered_indices.begin(), + curr_slot->indices_map.d_reordered_indices.end(), + curr_slot->indices_map.d_uniq_indices.begin(), + curr_slot->d_batch_probe_indices.begin()); + + // Append to Final Buffers (Device-to-Device Copy) + CUDA_CHECK(cudaMemcpyAsync(d_final_build_indices.data() + tail_offset, + curr_slot->d_batch_build_indices.data(), + sizeof(uint32_t) * new_size, cudaMemcpyDeviceToDevice, + curr_slot->stream)); + + CUDA_CHECK(cudaMemcpyAsync(d_final_probe_indices.data() + tail_offset, + curr_slot->d_batch_probe_indices.data(), + sizeof(uint32_t) * new_size, cudaMemcpyDeviceToDevice, + curr_slot->stream)); + + tail_offset += new_size; + } + } + + // --- FINALIZATION --- + + // Wait for all streams to finish writing to final buffers + for (auto& slot : slots) { + slot->stream.synchronize(); + } + + // Shrink probe vector to actual size for sorting + d_final_probe_indices.resize(tail_offset, main_stream); + d_final_build_indices.resize(tail_offset, main_stream); + + if (config_.sort_probe_indices) { + thrust::sort_by_key(rmm::exec_policy_nosync(main_stream), + d_final_probe_indices.begin(), + d_final_probe_indices.end(), // Sort only valid range + d_final_build_indices.begin()); + } + + // Final Copy to Host + CUDA_CHECK(cudaMemcpyAsync(build_indices, d_final_build_indices.data(), + sizeof(uint32_t) * tail_offset, cudaMemcpyDeviceToHost, + main_stream)); + + CUDA_CHECK(cudaMemcpyAsync(probe_indices, d_final_probe_indices.data(), + sizeof(uint32_t) * tail_offset, cudaMemcpyDeviceToHost, + main_stream)); + + main_stream.synchronize(); + return tail_offset; +} + +uint32_t RTSpatialRefiner::Refine(const ArrowSchema* build_schema, + const ArrowArray* build_array, + const ArrowSchema* probe_schema, + const ArrowArray* probe_array, Predicate predicate, + uint32_t* build_indices, uint32_t* probe_indices, + uint32_t len) { + if (len == 0) { + return 0; + } + + auto cuda_stream = stream_pool_->get_stream(); + SpatialRefinerContext ctx; + + ctx.cuda_stream = cuda_stream; + + IndicesMap build_indices_map, probe_indices_map; + rmm::device_uvector d_indices(len, cuda_stream); + + CUDA_CHECK(cudaMemcpyAsync(d_indices.data(), build_indices, sizeof(uint32_t) * len, + cudaMemcpyHostToDevice, cuda_stream)); + buildIndicesMap(cuda_stream, d_indices.begin(), d_indices.end(), build_indices_map); + + CUDA_CHECK(cudaMemcpyAsync(d_indices.data(), probe_indices, sizeof(uint32_t) * len, + cudaMemcpyHostToDevice, cuda_stream)); + buildIndicesMap(cuda_stream, d_indices.begin(), d_indices.end(), probe_indices_map); + d_indices.resize(0, cuda_stream); + d_indices.shrink_to_fit(cuda_stream); + + loader_t loader(thread_pool_); + loader_t::Config loader_config; + loader_config.memory_quota = config_.wkb_parser_memory_quota / config_.concurrency; + loader.Init(loader_config); + loader.Parse(ctx.cuda_stream, build_schema, build_array, + build_indices_map.h_uniq_indices.begin(), + build_indices_map.h_uniq_indices.end()); + auto geoms1 = std::move(loader.Finish(ctx.cuda_stream)); + + loader.Clear(ctx.cuda_stream); + loader.Parse(ctx.cuda_stream, probe_schema, probe_array, + probe_indices_map.h_uniq_indices.begin(), + probe_indices_map.h_uniq_indices.end()); + auto geoms2 = std::move(loader.Finish(ctx.cuda_stream)); + + GPUSPATIAL_LOG_INFO( + "RTSpatialRefiner %p (Free %zu MB), Loaded Geometries, build_array %ld, Loaded %u, Type %s, probe_array %ld, Loaded %u, Type %s", + this, rmm::available_device_memory().first / 1024 / 1024, build_array->length, + geoms1.num_features(), GeometryTypeToString(geoms1.get_geometry_type()).c_str(), + probe_array->length, geoms2.num_features(), + GeometryTypeToString(geoms2.get_geometry_type()).c_str()); + + RelateEngine relate_engine(&geoms1, config_.rt_engine.get()); + RelateEngine::Config re_config; + + re_config.memory_quota = config_.relate_engine_memory_quota / config_.concurrency; + re_config.bvh_fast_build = config_.prefer_fast_build; + re_config.bvh_compact = config_.compact; + + relate_engine.set_config(re_config); + + GPUSPATIAL_LOG_INFO( + "RTSpatialRefiner %p (Free %zu MB), Evaluating %u Geometry Pairs with Predicate %s", + this, rmm::available_device_memory().first / 1024 / 1024, len, + PredicateToString(predicate)); + + ctx.timer.start(ctx.cuda_stream); + + relate_engine.Evaluate(ctx.cuda_stream, geoms2, predicate, + build_indices_map.d_reordered_indices, + probe_indices_map.d_reordered_indices); + float refine_ms = ctx.timer.stop(ctx.cuda_stream); + + auto new_size = build_indices_map.d_reordered_indices.size(); + GPUSPATIAL_LOG_INFO("RTSpatialRefiner %p (Free %zu MB), Refine time %f, new size %zu", + this, rmm::available_device_memory().first / 1024 / 1024, refine_ms, + new_size); + rmm::device_uvector d_build_indices(new_size, ctx.cuda_stream); + rmm::device_uvector d_probe_indices(new_size, ctx.cuda_stream); + + thrust::gather(rmm::exec_policy_nosync(ctx.cuda_stream), + build_indices_map.d_reordered_indices.begin(), + build_indices_map.d_reordered_indices.end(), + build_indices_map.d_uniq_indices.begin(), d_build_indices.begin()); + + thrust::gather(rmm::exec_policy_nosync(ctx.cuda_stream), + probe_indices_map.d_reordered_indices.begin(), + probe_indices_map.d_reordered_indices.end(), + probe_indices_map.d_uniq_indices.begin(), d_probe_indices.begin()); + + if (config_.sort_probe_indices) { + thrust::sort_by_key(rmm::exec_policy_nosync(ctx.cuda_stream), d_probe_indices.begin(), + d_probe_indices.end(), d_build_indices.begin()); + } + + CUDA_CHECK(cudaMemcpyAsync(build_indices, d_build_indices.data(), + sizeof(uint32_t) * new_size, cudaMemcpyDeviceToHost, + ctx.cuda_stream)); + + CUDA_CHECK(cudaMemcpyAsync(probe_indices, d_probe_indices.data(), + sizeof(uint32_t) * new_size, cudaMemcpyDeviceToHost, + ctx.cuda_stream)); + ctx.cuda_stream.synchronize(); + return new_size; +} + +template +void RTSpatialRefiner::buildIndicesMap(rmm::cuda_stream_view stream, INDEX_IT index_begin, + INDEX_IT index_end, + IndicesMap& indices_map) const { + auto len = thrust::distance(index_begin, index_end); + auto& d_uniq_indices = indices_map.d_uniq_indices; + auto& h_uniq_indices = indices_map.h_uniq_indices; + + d_uniq_indices.resize(len, stream); + CUDA_CHECK(cudaMemcpyAsync(d_uniq_indices.data(), index_begin, sizeof(uint32_t) * len, + cudaMemcpyDeviceToDevice, stream)); + + thrust::sort(rmm::exec_policy_nosync(stream), d_uniq_indices.begin(), + d_uniq_indices.end()); + auto uniq_end = thrust::unique(rmm::exec_policy_nosync(stream), d_uniq_indices.begin(), + d_uniq_indices.end()); + auto uniq_size = thrust::distance(d_uniq_indices.begin(), uniq_end); + + d_uniq_indices.resize(uniq_size, stream); + h_uniq_indices.resize(uniq_size); + + CUDA_CHECK(cudaMemcpyAsync(h_uniq_indices.data(), d_uniq_indices.data(), + sizeof(uint32_t) * uniq_size, cudaMemcpyDeviceToHost, + stream)); + + auto& d_reordered_indices = indices_map.d_reordered_indices; + + d_reordered_indices.resize(len, stream); + detail::ReorderIndices(stream, index_begin, index_end, d_uniq_indices, + d_reordered_indices); +} + +std::unique_ptr CreateRTSpatialRefiner( + const RTSpatialRefinerConfig& config) { + auto refiner = std::make_unique(config); + GPUSPATIAL_LOG_INFO( + "Create RTSpatialRefiner %p, fast_build = %d, compact = %d, " + "parsing_threads = %u, concurrency = %u, pipeline_batches = %u, " + "wkb_parser_memory_quota = %.2f, relate_engine_memory_quota = %.2f", + refiner.get(), config.prefer_fast_build, config.compact, config.parsing_threads, + config.concurrency, config.pipeline_batches, config.wkb_parser_memory_quota, + config.relate_engine_memory_quota); + return std::move(refiner); +} + +} // namespace gpuspatial diff --git a/c/sedona-libgpuspatial/libgpuspatial/src/spatial_joiner.cu b/c/sedona-libgpuspatial/libgpuspatial/src/spatial_joiner.cu deleted file mode 100644 index 03aafaa27..000000000 --- a/c/sedona-libgpuspatial/libgpuspatial/src/spatial_joiner.cu +++ /dev/null @@ -1,483 +0,0 @@ - -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. -#include "gpuspatial/index/detail/launch_parameters.h" -#include "gpuspatial/index/relate_engine.cuh" -#include "gpuspatial/index/spatial_joiner.cuh" -#include "gpuspatial/loader/parallel_wkb_loader.h" -#include "gpuspatial/utils/logger.hpp" -#include "gpuspatial/utils/stopwatch.h" - -#include "rt/shaders/shader_id.hpp" - -#include "rmm/exec_policy.hpp" - -#define OPTIX_MAX_RAYS (1lu << 30) -namespace gpuspatial { - -namespace detail { - -template -static rmm::device_uvector ComputeAABBs( - rmm::cuda_stream_view stream, const ArrayView>>& mbrs) { - rmm::device_uvector aabbs(mbrs.size(), stream); - - thrust::transform(rmm::exec_policy_nosync(stream), mbrs.begin(), mbrs.end(), - aabbs.begin(), [] __device__(const Box>& mbr) { - OptixAabb aabb{0, 0, 0, 0, 0, 0}; - auto min_corner = mbr.get_min(); - auto max_corner = mbr.get_max(); - for (int dim = 0; dim < N_DIM; dim++) { - (&aabb.minX)[dim] = min_corner[dim]; - (&aabb.maxX)[dim] = max_corner[dim]; - } - return aabb; - }); - return std::move(aabbs); -} - -} // namespace detail - -void SpatialJoiner::Init(const Config* config) { - config_ = *dynamic_cast(config); - GPUSPATIAL_LOG_INFO("SpatialJoiner %p (Free %zu MB), Initialize, Concurrency %u", this, - rmm::available_device_memory().first / 1024 / 1024, - config_.concurrency); - details::RTConfig rt_config = details::get_default_rt_config(config_.ptx_root); - rt_engine_.Init(rt_config); - - loader_t::Config loader_config; - - thread_pool_ = std::make_shared(config_.parsing_threads); - build_loader_ = std::make_unique(thread_pool_); - build_loader_->Init(loader_config); - stream_pool_ = std::make_unique(config_.concurrency); - ctx_pool_ = ObjectPool::create(config_.concurrency); - CUDA_CHECK(cudaDeviceSetLimit(cudaLimitStackSize, config_.stack_size_bytes)); - Clear(); -} - -void SpatialJoiner::Clear() { - GPUSPATIAL_LOG_INFO("SpatialJoiner %p (Free %zu MB), Clear", this, - rmm::available_device_memory().first / 1024 / 1024); - bvh_buffer_ = nullptr; - geometry_grouper_.Clear(); - auto stream = rmm::cuda_stream_default; - build_loader_->Clear(stream); - build_geometries_.Clear(stream); - stream.synchronize(); -} - -void SpatialJoiner::PushBuild(const ArrowSchema* schema, const ArrowArray* array, - int64_t offset, int64_t length) { - GPUSPATIAL_LOG_INFO("SpatialJoiner %p (Free %zu MB), PushBuild, offset %ld, length %ld", - this, rmm::available_device_memory().first / 1024 / 1024, offset, - length); - build_loader_->Parse(rmm::cuda_stream_default, array, offset, length); -} - -void SpatialJoiner::FinishBuilding() { - auto stream = rmm::cuda_stream_default; - - build_geometries_ = std::move(build_loader_->Finish(stream)); - - GPUSPATIAL_LOG_INFO( - "SpatialJoiner %p (Free %zu MB), FinishBuilding, n_features: %ld, type %s", this, - rmm::available_device_memory().first / 1024 / 1024, - build_geometries_.num_features(), - GeometryTypeToString(build_geometries_.get_geometry_type())); - - if (build_geometries_.get_geometry_type() == GeometryType::kPoint) { - geometry_grouper_.Group(stream, build_geometries_, config_.n_points_per_aabb); - handle_ = buildBVH(stream, geometry_grouper_.get_aabbs(), bvh_buffer_); - } else { - auto aabbs = detail::ComputeAABBs(stream, build_geometries_.get_mbrs()); - handle_ = buildBVH(stream, ArrayView(aabbs), bvh_buffer_); - } - - relate_engine_ = RelateEngine(&build_geometries_, &rt_engine_); - RelateEngine::Config re_config; - - re_config.memory_quota = config_.relate_engine_memory_quota; - re_config.bvh_fast_build = config_.prefer_fast_build; - re_config.bvh_fast_compact = config_.compact; - - relate_engine_.set_config(re_config); -} - -void SpatialJoiner::PushStream(Context* base_ctx, const ArrowSchema* schema, - const ArrowArray* array, int64_t offset, int64_t length, - Predicate predicate, std::vector* build_indices, - std::vector* stream_indices, - int32_t array_index_offset) { - auto* ctx = (SpatialJoinerContext*)base_ctx; - ctx->cuda_stream = stream_pool_->get_stream(); - -#ifdef GPUSPATIAL_PROFILING - Stopwatch sw; - sw.start(); -#endif - ctx->array_index_offset = array_index_offset; - - if (ctx->stream_loader == nullptr) { - ctx->stream_loader = std::make_unique(thread_pool_); - loader_t::Config loader_config; - - ctx->stream_loader->Init(loader_config); - } - ctx->stream_loader->Parse(ctx->cuda_stream, array, offset, length); - ctx->stream_geometries = std::move(ctx->stream_loader->Finish(ctx->cuda_stream)); - - auto build_type = build_geometries_.get_geometry_type(); - auto stream_type = ctx->stream_geometries.get_geometry_type(); - - GPUSPATIAL_LOG_INFO( - "SpatialJoiner %p, PushStream, build features %zu, type %s, stream features %zu, type %s", - this, build_geometries_.num_features(), - GeometryTypeToString(build_geometries_.get_geometry_type()), - ctx->stream_geometries.num_features(), - GeometryTypeToString(ctx->stream_geometries.get_geometry_type())); - -#ifdef GPUSPATIAL_PROFILING - sw.stop(); - ctx->parse_ms += sw.ms(); -#endif - - if (build_type == GeometryType::kPoint) { - if (stream_type == GeometryType::kPoint) { - handleBuildPointStreamPoint(ctx, predicate, build_indices, stream_indices); - } else { - handleBuildPointStreamBox(ctx, predicate, build_indices, stream_indices); - } - } else { - if (stream_type == GeometryType::kPoint) { - handleBuildBoxStreamPoint(ctx, predicate, build_indices, stream_indices); - } else { - handleBuildBoxStreamBox(ctx, predicate, build_indices, stream_indices); - } - } -#ifdef GPUSPATIAL_PROFILING - printf("parse %lf, alloc %lf, filter %lf, refine %lf, copy_res %lf ms\n", ctx->parse_ms, - ctx->alloc_ms, ctx->filter_ms, ctx->refine_ms, ctx->copy_res_ms); -#endif -} - -void SpatialJoiner::handleBuildPointStreamPoint(SpatialJoinerContext* ctx, - Predicate predicate, - std::vector* build_indices, - std::vector* stream_indices) { - allocateResultBuffer(ctx); - - ctx->shader_id = GetPointQueryShaderId(); - assert(ctx->stream_geometries.get_geometry_type() == GeometryType::kPoint); - - using launch_params_t = detail::LaunchParamsPointQuery; - ctx->launch_params_buffer = - std::make_unique(sizeof(launch_params_t), ctx->cuda_stream); - ctx->h_launch_params_buffer.resize(sizeof(launch_params_t)); - auto& launch_params = *(launch_params_t*)ctx->h_launch_params_buffer.data(); - - launch_params.grouped = true; - launch_params.prefix_sum = geometry_grouper_.get_prefix_sum(); - launch_params.reordered_indices = geometry_grouper_.get_reordered_indices(); - launch_params.mbrs1 = ArrayView(); // no MBRs for point - launch_params.points2 = ctx->stream_geometries.get_points(); - launch_params.handle = handle_; - launch_params.ids = ctx->results.DeviceObject(); - CUDA_CHECK(cudaMemcpyAsync(ctx->launch_params_buffer->data(), &launch_params, - sizeof(launch_params_t), cudaMemcpyHostToDevice, - ctx->cuda_stream)); - - uint32_t dim_x = std::min(OPTIX_MAX_RAYS, ctx->stream_geometries.num_features()); - - filter(ctx, dim_x); - refine(ctx, predicate, build_indices, stream_indices); -} - -void SpatialJoiner::handleBuildBoxStreamPoint(SpatialJoinerContext* ctx, - Predicate predicate, - std::vector* build_indices, - std::vector* stream_indices) { - allocateResultBuffer(ctx); - - ctx->shader_id = GetPointQueryShaderId(); - assert(ctx->stream_geometries.get_geometry_type() == GeometryType::kPoint); - - using launch_params_t = detail::LaunchParamsPointQuery; - ctx->launch_params_buffer = - std::make_unique(sizeof(launch_params_t), ctx->cuda_stream); - ctx->h_launch_params_buffer.resize(sizeof(launch_params_t)); - auto& launch_params = *(launch_params_t*)ctx->h_launch_params_buffer.data(); - - launch_params.grouped = false; - launch_params.mbrs1 = build_geometries_.get_mbrs(); - launch_params.points2 = ctx->stream_geometries.get_points(); - launch_params.handle = handle_; - launch_params.ids = ctx->results.DeviceObject(); - CUDA_CHECK(cudaMemcpyAsync(ctx->launch_params_buffer->data(), &launch_params, - sizeof(launch_params_t), cudaMemcpyHostToDevice, - ctx->cuda_stream)); - - uint32_t dim_x = std::min(OPTIX_MAX_RAYS, ctx->stream_geometries.num_features()); - - filter(ctx, dim_x); - refine(ctx, predicate, build_indices, stream_indices); -} - -void SpatialJoiner::handleBuildPointStreamBox(SpatialJoinerContext* ctx, - Predicate predicate, - std::vector* build_indices, - std::vector* stream_indices) { - allocateResultBuffer(ctx); - - ctx->shader_id = GetPointQueryShaderId(); - assert(build_geometries_.get_geometry_type() == GeometryType::kPoint); - - using launch_params_t = detail::LaunchParamsPointQuery; - ctx->launch_params_buffer = - std::make_unique(sizeof(launch_params_t), ctx->cuda_stream); - ctx->h_launch_params_buffer.resize(sizeof(launch_params_t)); - auto& launch_params = *(launch_params_t*)ctx->h_launch_params_buffer.data(); - - auto aabbs = detail::ComputeAABBs(ctx->cuda_stream, ctx->stream_geometries.get_mbrs()); - auto handle = buildBVH(ctx->cuda_stream, ArrayView(aabbs), ctx->bvh_buffer); - - // mbrs1 are from stream; points2 are from build - launch_params.grouped = false; - launch_params.mbrs1 = ctx->stream_geometries.get_mbrs(); - launch_params.points2 = build_geometries_.get_points(); - launch_params.handle = handle; - launch_params.ids = ctx->results.DeviceObject(); - CUDA_CHECK(cudaMemcpyAsync(ctx->launch_params_buffer->data(), &launch_params, - sizeof(launch_params_t), cudaMemcpyHostToDevice, - ctx->cuda_stream)); - - uint32_t dim_x = std::min(OPTIX_MAX_RAYS, build_geometries_.num_features()); - // IMPORTANT: In this case, the BVH is built from stream geometries and points2 are - // build geometries, so the result pairs are (stream_id, build_id) instead of (build_id, - // stream_id). We need to swap the output buffers to correct this. - filter(ctx, dim_x, true); - refine(ctx, predicate, build_indices, stream_indices); -} - -void SpatialJoiner::handleBuildBoxStreamBox(SpatialJoinerContext* ctx, - Predicate predicate, - std::vector* build_indices, - std::vector* stream_indices) { - allocateResultBuffer(ctx); - - // forward cast: cast rays from stream geometries with the BVH of build geometries - { - auto dim_x = std::min(OPTIX_MAX_RAYS, ctx->stream_geometries.num_features()); - - prepareLaunchParamsBoxQuery(ctx, true); - filter(ctx, dim_x); - refine(ctx, predicate, build_indices, stream_indices); - ctx->results.Clear(ctx->cuda_stream); // results have been copied, reuse space - } - // need allocate again as the previous results buffer has been shrinked to fit - allocateResultBuffer(ctx); - // backward cast: cast rays from the build geometries with the BVH of stream geometries - { - auto dim_x = std::min(OPTIX_MAX_RAYS, build_geometries_.num_features()); - auto v_mbrs = ctx->stream_geometries.get_mbrs(); - rmm::device_uvector aabbs(v_mbrs.size(), ctx->cuda_stream); - - thrust::transform(rmm::exec_policy_nosync(ctx->cuda_stream), v_mbrs.begin(), - v_mbrs.end(), aabbs.begin(), - [] __device__(const box_t& mbr) { return mbr.ToOptixAabb(); }); - - // Build a BVH over the MBRs of the stream geometries - ctx->handle = - buildBVH(ctx->cuda_stream, ArrayView(aabbs.data(), aabbs.size()), - ctx->bvh_buffer); - prepareLaunchParamsBoxQuery(ctx, false); - filter(ctx, dim_x); - refine(ctx, predicate, build_indices, stream_indices); - } -} - -OptixTraversableHandle SpatialJoiner::buildBVH( - const rmm::cuda_stream_view& stream, const ArrayView& aabbs, - std::unique_ptr& buffer) { - auto buffer_size_bytes = rt_engine_.EstimateMemoryUsageForAABB( - aabbs.size(), config_.prefer_fast_build, config_.compact); - - if (buffer == nullptr || buffer->size() < buffer_size_bytes) { - buffer = std::make_unique(buffer_size_bytes, stream); - } - - return rt_engine_.BuildAccelCustom(stream, aabbs, *buffer, config_.prefer_fast_build, - config_.compact); -} - -void SpatialJoiner::allocateResultBuffer(SpatialJoinerContext* ctx) { -#ifdef GPUSPATIAL_PROFILING - ctx->timer.start(ctx->cuda_stream); -#endif - int64_t avail_bytes = rmm::available_device_memory().first; - auto stream_type = ctx->stream_geometries.get_geometry_type(); - if (stream_type != GeometryType::kPoint) { - // need to reserve space for the BVH of stream - auto n_aabbs = ctx->stream_geometries.get_mbrs().size(); - - avail_bytes -= rt_engine_.EstimateMemoryUsageForAABB( - n_aabbs, config_.prefer_fast_build, config_.compact); - } - - if (avail_bytes <= 0) { - throw std::runtime_error( - "Not enough memory to allocate result space for spatial index"); - } - - uint64_t reserve_bytes = ceil(avail_bytes * config_.result_buffer_memory_reserve_ratio); - reserve_bytes = reserve_bytes / config_.concurrency + 1; - // two index_t for each result pair (build index, stream index) and another index_t for - // the temp storage - uint32_t n_items = reserve_bytes / (2 * sizeof(index_t) + sizeof(index_t)); - - GPUSPATIAL_LOG_INFO( - "SpatialJoiner %p, Allocate result buffer quota %zu MB, queue size %u", this, - reserve_bytes / 1024 / 1024, n_items); - - ctx->results.Init(ctx->cuda_stream, n_items); - ctx->results.Clear(ctx->cuda_stream); -#ifdef GPUSPATIAL_PROFILING - ctx->alloc_ms += ctx->timer.stop(ctx->cuda_stream); -#endif -} - -void SpatialJoiner::prepareLaunchParamsBoxQuery(SpatialJoinerContext* ctx, bool foward) { - using launch_params_t = detail::LaunchParamsBoxQuery; - ctx->launch_params_buffer = - std::make_unique(sizeof(launch_params_t), ctx->cuda_stream); - ctx->h_launch_params_buffer.resize(sizeof(launch_params_t)); - auto& launch_params = *(launch_params_t*)ctx->h_launch_params_buffer.data(); - - assert(ctx->stream_geometries.get_geometry_type() != GeometryType::kPoint); - - launch_params.mbrs1 = build_geometries_.get_mbrs(); - launch_params.mbrs2 = ctx->stream_geometries.get_mbrs(); - if (foward) { - launch_params.handle = handle_; - ctx->shader_id = GetBoxQueryForwardShaderId(); - } else { - launch_params.handle = ctx->handle; - ctx->shader_id = GetBoxQueryBackwardShaderId(); - } - - launch_params.ids = ctx->results.DeviceObject(); - CUDA_CHECK(cudaMemcpyAsync(ctx->launch_params_buffer->data(), &launch_params, - sizeof(launch_params_t), cudaMemcpyHostToDevice, - ctx->cuda_stream)); -} - -void SpatialJoiner::filter(SpatialJoinerContext* ctx, uint32_t dim_x, bool swap_id) { -#ifdef GPUSPATIAL_PROFILING - ctx->timer.start(ctx->cuda_stream); -#endif - Stopwatch sw; - sw.start(); - if (dim_x > 0) { - rt_engine_.Render(ctx->cuda_stream, ctx->shader_id, dim3{dim_x, 1, 1}, - ArrayView((char*)ctx->launch_params_buffer->data(), - ctx->launch_params_buffer->size())); - } - auto result_size = ctx->results.size(ctx->cuda_stream); - sw.stop(); - GPUSPATIAL_LOG_INFO( - "SpatialJoiner %p, Filter stage, Launched %u rays, Found %u candidates, time %lf ms", - this, dim_x, result_size, sw.ms()); - if (swap_id && result_size > 0) { - // swap the pair (build_id, stream_id) to (stream_id, build_id) - thrust::for_each(rmm::exec_policy_nosync(ctx->cuda_stream), ctx->results.data(), - ctx->results.data() + result_size, - [] __device__(thrust::pair & pair) { - thrust::swap(pair.first, pair.second); - }); - } - ctx->results.shrink_to_fit(ctx->cuda_stream); - -#ifdef GPUSPATIAL_PROFILING - ctx->filter_ms += ctx->timer.stop(ctx->cuda_stream); -#endif -} - -void SpatialJoiner::refine(SpatialJoinerContext* ctx, Predicate predicate, - std::vector* build_indices, - std::vector* stream_indices) { -#ifdef GPUSPATIAL_PROFILING - ctx->timer.start(ctx->cuda_stream); -#endif - relate_engine_.Evaluate(ctx->cuda_stream, ctx->stream_geometries, predicate, - ctx->results); -#ifdef GPUSPATIAL_PROFILING - ctx->refine_ms += ctx->timer.stop(ctx->cuda_stream); -#endif - auto n_results = ctx->results.size(ctx->cuda_stream); - -#ifdef GPUSPATIAL_PROFILING - ctx->timer.start(ctx->cuda_stream); -#endif - rmm::device_uvector tmp_result_buffer(n_results, ctx->cuda_stream); - - thrust::transform( - rmm::exec_policy_nosync(ctx->cuda_stream), ctx->results.data(), - ctx->results.data() + n_results, tmp_result_buffer.begin(), - [] __device__(const thrust::pair& pair) -> uint32_t { - return pair.first; - }); - auto prev_size = build_indices->size(); - build_indices->resize(build_indices->size() + n_results); - - CUDA_CHECK(cudaMemcpyAsync(build_indices->data() + prev_size, tmp_result_buffer.data(), - sizeof(uint32_t) * n_results, cudaMemcpyDeviceToHost, - ctx->cuda_stream)); - - auto array_index_offset = ctx->array_index_offset; - - thrust::transform( - rmm::exec_policy_nosync(ctx->cuda_stream), ctx->results.data(), - ctx->results.data() + n_results, tmp_result_buffer.begin(), - [=] __device__(const thrust::pair& pair) -> uint32_t { - return pair.second + array_index_offset; - }); - - stream_indices->resize(stream_indices->size() + n_results); - - CUDA_CHECK(cudaMemcpyAsync(stream_indices->data() + prev_size, tmp_result_buffer.data(), - sizeof(uint32_t) * n_results, cudaMemcpyDeviceToHost, - ctx->cuda_stream)); -#ifdef GPUSPATIAL_PROFILING - ctx->copy_res_ms += ctx->timer.stop(ctx->cuda_stream); -#endif - ctx->cuda_stream.synchronize(); -} - -std::unique_ptr CreateSpatialJoiner() { - return std::make_unique(); -} - -void InitSpatialJoiner(StreamingJoiner* index, const char* ptx_root, - uint32_t concurrency) { - SpatialJoiner::SpatialJoinerConfig config; - config.ptx_root = ptx_root; - config.concurrency = concurrency; - index->Init(&config); -} - -} // namespace gpuspatial diff --git a/c/sedona-libgpuspatial/libgpuspatial/test/CMakeLists.txt b/c/sedona-libgpuspatial/libgpuspatial/test/CMakeLists.txt index 719d0909f..bcf69239f 100644 --- a/c/sedona-libgpuspatial/libgpuspatial/test/CMakeLists.txt +++ b/c/sedona-libgpuspatial/libgpuspatial/test/CMakeLists.txt @@ -53,8 +53,19 @@ if(GPUSPATIAL_BUILD_TESTS) PRIVATE $<$:--expt-extended-lambda --expt-relaxed-constexpr>) - add_executable(joiner_test main.cc array_stream.cc joiner_test.cu) - target_link_libraries(joiner_test + add_executable(index_test main.cc index_test.cu) + target_link_libraries(index_test + PRIVATE cuda + GTest::gtest_main + GTest::gmock_main + gpuspatial + GEOS::geos + GEOS::geos_c) + target_compile_options(index_test + PRIVATE $<$:--expt-extended-lambda + --expt-relaxed-constexpr>) + add_executable(refiner_test main.cc array_stream.cc refiner_test.cu) + target_link_libraries(refiner_test PRIVATE cuda GTest::gtest_main GTest::gmock_main @@ -65,7 +76,7 @@ if(GPUSPATIAL_BUILD_TESTS) Arrow::arrow_static Parquet::parquet_static nanoarrow::nanoarrow_ipc) - target_compile_options(joiner_test + target_compile_options(refiner_test PRIVATE $<$:--expt-extended-lambda --expt-relaxed-constexpr>) @@ -83,14 +94,19 @@ if(GPUSPATIAL_BUILD_TESTS) --expt-relaxed-constexpr>) add_executable(c_wrapper_test main.cc c_wrapper_test.cc array_stream.cc) - target_link_libraries(c_wrapper_test PRIVATE GTest::gtest_main GTest::gmock_main - gpuspatial_c nanoarrow::nanoarrow_ipc) + target_link_libraries(c_wrapper_test + PRIVATE GTest::gtest_main + GTest::gmock_main + gpuspatial_c + GEOS::geos + GEOS::geos_c + geoarrow_geos + nanoarrow::nanoarrow_ipc) include(GoogleTest) gtest_discover_tests(gpuspatial_testing_test) gtest_discover_tests(array_stream_test) gtest_discover_tests(loader_test) - gtest_discover_tests(joiner_test) gtest_discover_tests(relate_test) endif() diff --git a/c/sedona-libgpuspatial/libgpuspatial/test/c_wrapper_test.cc b/c/sedona-libgpuspatial/libgpuspatial/test/c_wrapper_test.cc index 60c247399..c56d2f1da 100644 --- a/c/sedona-libgpuspatial/libgpuspatial/test/c_wrapper_test.cc +++ b/c/sedona-libgpuspatial/libgpuspatial/test/c_wrapper_test.cc @@ -24,40 +24,136 @@ #include #include #include "array_stream.hpp" +#include "geoarrow_geos/geoarrow_geos.hpp" #include "nanoarrow/nanoarrow.hpp" -namespace TestUtils { -std::string GetTestDataPath(const std::string& relative_path_to_file); +TEST(RuntimeTest, InitializeRuntime) { + GpuSpatialRuntime runtime; + GpuSpatialRuntimeCreate(&runtime); + GpuSpatialRuntimeConfig config; + + std::string ptx_root = TestUtils::GetTestShaderPath(); + config.ptx_root = ptx_root.c_str(); + config.device_id = 0; + config.use_cuda_memory_pool = false; + ASSERT_EQ(runtime.init(&runtime, &config), 0); + + runtime.release(&runtime); +} + +TEST(RuntimeTest, ErrorTest) { + GpuSpatialRuntime runtime; + GpuSpatialRuntimeCreate(&runtime); + GpuSpatialRuntimeConfig runtime_config; + + runtime_config.ptx_root = "/invalid/path/to/ptx"; + runtime_config.device_id = 0; + runtime_config.use_cuda_memory_pool = false; + + EXPECT_NE(runtime.init(&runtime, &runtime_config), 0); + + const char* raw_error = runtime.get_last_error(&runtime); + printf("Error received: %s\n", raw_error); + + std::string error_msg(raw_error); + + EXPECT_NE(error_msg.find("No such file or directory"), std::string::npos) + << "Error message was corrupted or incorrect. Got: " << error_msg; + + runtime.release(&runtime); +} + +TEST(SpatialIndexTest, InitializeIndex) { + GpuSpatialRuntime runtime; + GpuSpatialRuntimeCreate(&runtime); + GpuSpatialRuntimeConfig runtime_config; + + std::string ptx_root = TestUtils::GetTestShaderPath(); + runtime_config.ptx_root = ptx_root.c_str(); + runtime_config.device_id = 0; + runtime_config.use_cuda_memory_pool = true; + runtime_config.cuda_memory_pool_init_precent = 10; + ASSERT_EQ(runtime.init(&runtime, &runtime_config), 0); + + SedonaFloatIndex2D index; + GpuSpatialIndexConfig index_config; + + index_config.runtime = &runtime; + index_config.concurrency = 1; + + ASSERT_EQ(GpuSpatialIndexFloat2DCreate(&index, &index_config), 0); + + index.release(&index); + runtime.release(&runtime); +} + +TEST(RefinerTest, InitializeRefiner) { + GpuSpatialRuntime runtime; + GpuSpatialRuntimeCreate(&runtime); + GpuSpatialRuntimeConfig runtime_config; + + std::string ptx_root = TestUtils::GetTestShaderPath(); + runtime_config.ptx_root = ptx_root.c_str(); + runtime_config.device_id = 0; + runtime_config.use_cuda_memory_pool = true; + runtime_config.cuda_memory_pool_init_precent = 10; + ASSERT_EQ(runtime.init(&runtime, &runtime_config), 0); + + SedonaSpatialRefiner refiner; + GpuSpatialRefinerConfig refiner_config; + + refiner_config.runtime = &runtime; + refiner_config.concurrency = 1; + + ASSERT_EQ(GpuSpatialRefinerCreate(&refiner, &refiner_config), 0); + + refiner.release(&refiner); + runtime.release(&runtime); } class CWrapperTest : public ::testing::Test { protected: void SetUp() override { - // Initialize the GpuSpatialJoiner - GpuSpatialJoinerCreate(&joiner_); - struct GpuSpatialJoinerConfig config_; - std::string ptx_root = TestUtils::GetTestDataPath("shaders_ptx"); + std::string ptx_root = TestUtils::GetTestShaderPath(); + + GpuSpatialRuntimeCreate(&runtime_); + GpuSpatialRuntimeConfig runtime_config; + + runtime_config.ptx_root = ptx_root.c_str(); + runtime_config.device_id = 0; + runtime_config.use_cuda_memory_pool = true; + runtime_config.cuda_memory_pool_init_precent = 10; + ASSERT_EQ(runtime_.init(&runtime_, &runtime_config), 0); + + GpuSpatialIndexConfig index_config; + + index_config.runtime = &runtime_; + index_config.concurrency = 1; + + ASSERT_EQ(GpuSpatialIndexFloat2DCreate(&index_, &index_config), 0); - // Set up the configuration - config_.concurrency = 2; // Example concurrency level - config_.ptx_root = ptx_root.c_str(); + GpuSpatialRefinerConfig refiner_config; - ASSERT_EQ(joiner_.init(&joiner_, &config_), 0); - // Initialize the context + refiner_config.runtime = &runtime_; + refiner_config.concurrency = 1; + + ASSERT_EQ(GpuSpatialRefinerCreate(&refiner_, &refiner_config), 0); } void TearDown() override { - // Clean up - joiner_.release(&joiner_); + refiner_.release(&refiner_); + index_.release(&index_); + runtime_.release(&runtime_); } - - struct GpuSpatialJoiner joiner_; + GpuSpatialRuntime runtime_; + SedonaFloatIndex2D index_; + SedonaSpatialRefiner refiner_; }; TEST_F(CWrapperTest, InitializeJoiner) { + using fpoint_t = gpuspatial::Point; + using box_t = gpuspatial::Box; // Test if the joiner initializes correctly - struct GpuSpatialJoinerContext context_; - joiner_.create_context(&joiner_, &context_); auto poly_path = TestUtils::GetTestDataPath("arrowipc/test_polygons.arrows"); auto point_path = TestUtils::GetTestDataPath("arrowipc/test_points.arrows"); @@ -73,6 +169,8 @@ TEST_F(CWrapperTest, InitializeJoiner) { int n_row_groups = 100; + geoarrow::geos::ArrayReader reader; + for (int i = 0; i < n_row_groups; i++) { ASSERT_EQ(ArrowArrayStreamGetNext(poly_stream.get(), build_array.get(), &error), NANOARROW_OK); @@ -84,23 +182,138 @@ TEST_F(CWrapperTest, InitializeJoiner) { ASSERT_EQ(ArrowArrayStreamGetSchema(point_stream.get(), stream_schema.get(), &error), NANOARROW_OK); - joiner_.push_build(&joiner_, build_schema.get(), build_array.get(), 0, - build_array->length); - joiner_.finish_building(&joiner_); + class GEOSCppHandle { + public: + GEOSContextHandle_t handle; + + GEOSCppHandle() { handle = GEOS_init_r(); } + + ~GEOSCppHandle() { GEOS_finish_r(handle); } + }; + GEOSCppHandle handle; + + reader.InitFromEncoding(handle.handle, GEOARROW_GEOS_ENCODING_WKB); + + geoarrow::geos::GeometryVector geom_build(handle.handle); + + geom_build.resize(build_array->length); + size_t n_build; + + ASSERT_EQ(reader.Read(build_array.get(), 0, build_array->length, + geom_build.mutable_data(), &n_build), + GEOARROW_GEOS_OK); + auto* tree = GEOSSTRtree_create_r(handle.handle, 10); + std::vector rects; + + for (size_t build_idx = 0; build_idx < build_array->length; build_idx++) { + auto* geom = geom_build.borrow(build_idx); + auto* box = GEOSEnvelope_r(handle.handle, geom); + + double xmin, ymin, xmax, ymax; + int result = GEOSGeom_getExtent_r(handle.handle, box, &xmin, &ymin, &xmax, &ymax); + ASSERT_EQ(result, 1); + box_t bbox(fpoint_t((float)xmin, (float)ymin), fpoint_t((float)xmax, (float)ymax)); + + rects.push_back(bbox); + + GEOSGeom_setUserData_r(handle.handle, (GEOSGeometry*)geom, (void*)build_idx); + GEOSSTRtree_insert_r(handle.handle, tree, box, (void*)geom); + GEOSGeom_destroy_r(handle.handle, box); + } + + index_.clear(&index_); + ASSERT_EQ(index_.push_build(&index_, (float*)rects.data(), rects.size()), 0); + ASSERT_EQ(index_.finish_building(&index_), 0); - joiner_.push_stream(&joiner_, &context_, stream_schema.get(), stream_array.get(), 0, - stream_array->length, GpuSpatialPredicateContains, 0); + geoarrow::geos::GeometryVector geom_stream(handle.handle); + size_t n_stream; + geom_stream.resize(stream_array->length); - void* build_indices_ptr; - void* stream_indices_ptr; + ASSERT_EQ(reader.Read(stream_array.get(), 0, stream_array->length, + geom_stream.mutable_data(), &n_stream), + GEOARROW_GEOS_OK); + + std::vector queries; + + for (size_t stream_idx = 0; stream_idx < stream_array->length; stream_idx++) { + auto* geom = geom_stream.borrow(stream_idx); + double xmin, ymin, xmax, ymax; + int result = GEOSGeom_getExtent_r(handle.handle, geom, &xmin, &ymin, &xmax, &ymax); + ASSERT_EQ(result, 1); + box_t bbox(fpoint_t((float)xmin, (float)ymin), fpoint_t((float)xmax, (float)ymax)); + queries.push_back(bbox); + } + + SedonaSpatialIndexContext idx_ctx; + index_.create_context(&idx_ctx); + + index_.probe(&index_, &idx_ctx, (float*)queries.data(), queries.size()); + + uint32_t* build_indices_ptr; + uint32_t* probe_indices_ptr; uint32_t build_indices_length; - uint32_t stream_indices_length; + uint32_t probe_indices_length; - joiner_.get_build_indices_buffer(&context_, (void**)&build_indices_ptr, - &build_indices_length); - joiner_.get_stream_indices_buffer(&context_, (void**)&stream_indices_ptr, - &stream_indices_length); - } + index_.get_build_indices_buffer(&idx_ctx, &build_indices_ptr, &build_indices_length); + index_.get_probe_indices_buffer(&idx_ctx, &probe_indices_ptr, &probe_indices_length); + + uint32_t new_len; + ASSERT_EQ( + refiner_.refine(&refiner_, build_schema.get(), build_array.get(), + stream_schema.get(), stream_array.get(), + SedonaSpatialRelationPredicate::SedonaSpatialPredicateContains, + (uint32_t*)build_indices_ptr, (uint32_t*)probe_indices_ptr, + build_indices_length, &new_len), + 0); - joiner_.destroy_context(&context_); + std::vector build_indices((uint32_t*)build_indices_ptr, + (uint32_t*)build_indices_ptr + new_len); + std::vector probe_indices((uint32_t*)probe_indices_ptr, + (uint32_t*)probe_indices_ptr + new_len); + + struct Payload { + GEOSContextHandle_t handle; + const GEOSGeometry* geom; + std::vector build_indices; + std::vector stream_indices; + SedonaSpatialRelationPredicate predicate; + }; + + Payload payload; + payload.predicate = SedonaSpatialRelationPredicate::SedonaSpatialPredicateContains; + payload.handle = handle.handle; + + for (size_t offset = 0; offset < n_stream; offset++) { + auto* geom = geom_stream.borrow(offset); + GEOSGeom_setUserData_r(handle.handle, (GEOSGeometry*)geom, (void*)offset); + payload.geom = geom; + + GEOSSTRtree_query_r( + handle.handle, tree, geom, + [](void* item, void* data) { + auto* geom_build = (GEOSGeometry*)item; + auto* payload = (Payload*)data; + auto* geom_stream = payload->geom; + + if (GEOSContains_r(payload->handle, geom_build, geom_stream) == 1) { + auto build_id = (size_t)GEOSGeom_getUserData_r(payload->handle, geom_build); + auto stream_id = + (size_t)GEOSGeom_getUserData_r(payload->handle, geom_stream); + payload->build_indices.push_back(build_id); + payload->stream_indices.push_back(stream_id); + } + }, + (void*)&payload); + } + + ASSERT_EQ(payload.build_indices.size(), build_indices.size()); + ASSERT_EQ(payload.stream_indices.size(), probe_indices.size()); + TestUtils::sort_vectors_by_index(payload.build_indices, payload.stream_indices); + TestUtils::sort_vectors_by_index(build_indices, probe_indices); + for (size_t j = 0; j < build_indices.size(); j++) { + ASSERT_EQ(payload.build_indices[j], build_indices[j]); + ASSERT_EQ(payload.stream_indices[j], probe_indices[j]); + } + index_.destroy_context(&idx_ctx); + } } diff --git a/c/sedona-libgpuspatial/libgpuspatial/test/data/cities/Makefile b/c/sedona-libgpuspatial/libgpuspatial/test/data/cities/Makefile index 5b04c384b..ac2eb06d8 100644 --- a/c/sedona-libgpuspatial/libgpuspatial/test/data/cities/Makefile +++ b/c/sedona-libgpuspatial/libgpuspatial/test/data/cities/Makefile @@ -19,7 +19,7 @@ URL := https://raw.githubusercontent.com/geoarrow/geoarrow-data/v0.2.0/natural-e INPUT_FILE := natural-earth_cities_geo.parquet PYTHON_SCRIPT := ../gen_points.py OUTPUT_POINTS := generated_points.parquet -NUM_POINTS := 1000 +NUM_POINTS := 10000 .PHONY: all clean generate diff --git a/c/sedona-libgpuspatial/libgpuspatial/test/data/cities/generated_points.parquet b/c/sedona-libgpuspatial/libgpuspatial/test/data/cities/generated_points.parquet index 4ad348b3ad636fc7743a031c12602e041b72ba29..02454736078006a44aa11c9f9a58ef8ee94959e6 100644 GIT binary patch literal 452407 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

LpgNSq6-}U8ezPHoP!@Pe|#yXM8ko? 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�gtxRLWhV@0e}kNj07$e#=8I=|W)q9X=R`47kwSb5tgXhns^xhzHZ4Ty~)! z)&X#LnZwcH2je=shHn`#1+HTA6ajF_`YQJXJTsrbBNd**zv~<&G`~?gb`r@1QB2GfPp0R%7W?`er2U_l5_B=jxIxj1Ain0x2$Ol@V09~OxXwRCp7<2;s$~adrdqdp%;JsTNa%INzyj~9bXI-wN>c^hrYq^ic zJDMX}IrFjgG-<7V-mhnY02PqT47gF7-OBmVt}}&YnI=SI zV~9ap2h}o-D~_=cx)ST!30lu9- zjiL2(1+owA*mx3jBHf~v4$!;PE9Q>CF0aYBgbyOe*p-`fX%y)V~E7r(F}VJs#nw#>;J%Ca60Oh*USjItij$+X)P>9 zi+B3~!cm3XrjaI3xZQmWvG)O=hlqdRwOeq(XTFt@E~l^aBk%rHx{Wt<<{bD}|KG-w z)k*tqukdDW&Y$JRa}S{=zwSrC&l@ix4LSJGrqA+yH~$Cm3&B5{U}Zcvkp~QVcADsO zsyS9m@3_9CJrH8OSW2J8kMK!j5ney@EvO2CYMc6zt7E(mNY92hxlye0Os@QwDi)l|ZL7mZ^!57+;T&KT{$JxtH=Ea!lcEQbEFzv^UWobX(W zJtbofoGTP%MWiu3(CwNo>?kt*NkFzUVz0EU zE2is>J)L=!H}wIt{tPQRqwHqJoqbNv|JI%!!0Cou1rOq#y;L&7fe8Kv%CsRuygEs@ zQzfN0<=#J)8i8|M8Q;P_R#+o-up^eDJpA!kZ~puD^m6jA&yKK$Y9?uUne6npdwXiu~R literal 33179 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 diff --git a/c/sedona-libgpuspatial/libgpuspatial/test/data/countries/Makefile b/c/sedona-libgpuspatial/libgpuspatial/test/data/countries/Makefile index 147a332bd..f154c4416 100644 --- a/c/sedona-libgpuspatial/libgpuspatial/test/data/countries/Makefile +++ b/c/sedona-libgpuspatial/libgpuspatial/test/data/countries/Makefile @@ -19,7 +19,7 @@ URL := https://raw.githubusercontent.com/geoarrow/geoarrow-data/v0.2.0/natural-e INPUT_FILE := natural-earth_countries_geo.parquet PYTHON_SCRIPT := ../gen_points.py OUTPUT_POINTS := generated_points.parquet -NUM_POINTS := 1000 +NUM_POINTS := 10000 .PHONY: all clean generate diff --git a/c/sedona-libgpuspatial/libgpuspatial/test/data/countries/generated_points.parquet b/c/sedona-libgpuspatial/libgpuspatial/test/data/countries/generated_points.parquet index 32d8dcc27d80786d314b3c1b7997a6243731aedf..70af404435b7168c068d70e3dc797e9adb38f469 100644 GIT binary patch literal 452487 zcmeF)dr*}1<2QU%RMbULQBfD&Bcd*fii*1Eo{!g_kLaR%MASu5QBfB~MMYf{6;HL) z0*h%aWo2cxl$Mp%QdU-0OD(XN)lynkR!cqa`Tg!^o@eH{pMUPT|GB@x%xhp^hME0v zz2AJU{d}(LQggL)#wCxd>KZog*8OB+oZmNKT+*1Nq^kSLNlB>#k_M!vBn^Z?kPL%i z2n>Z37zV>(1dt#VM#3magV8Vs#zH!bgYhr{GGHQ1g2_OJOqc>wfdW*Z0Ua2?ge;f_ z*)Sbuz)WC44$OktkPCBQF3f{Gm=6nJA>_j%SPV;m4F#|imH`L2zym%AKnNlbg9M}? z133_&041nE4I0pb4)kCEBbdMpg|Hk}fCY+RC9DD~*uV}BaDoepVKtP%8dwYKzzwCa z9yUN3Y=lj)8OmV`Y=v!50o!2*>;w-~!Y#4!ak^m{cr#dLJb^(!*B$=Pzy)l z7}UXWH~}Z29!|k&I0I+l96SP#f)5(tF?byO&*fQ#@XJOx2$g{R>e zxCGC_bMQP|h8N&PcnPk+%kT=k3L$8N*Wh)y3fJHbcoVL}Tktl#12^DZcn{u(Fto!5 z@FCoUkKkkY1a85n@ELp#x8V!;625{6bimi}4Md?6x}Y0k&;xhiTeu6~!T0b3#Gw~{ zgrDFZ+=rjx7kB`_!f)_9JcK{sPxuQG&d427^9R)7VHU?r>qE7-se4se1CieWXBz#3Q!>%a}A zupTx*8Ek}2uo=o>3v7jLPyyRv2kZn7RKhOU4OOrQ_QF1>hW&5=4nhqag2QkGyif~A z;TY7xaX0}dp&m}bX*dIC;T${ykAe>x;4yd{{LlzZ&K{XoaWY z8Mp+`!gKIET!t6mMR*CWz{~Ioyb2*`gV*47xC+gpc53_ylgjr|=nk4!7Y8_!7Q?2z0>L@C`(v6S|-qV$cJ3;9Iy0-@*6r1H_>h zeuSUk9^8kY;TL!Szrt_uJ3NFx;7|Aq63_>K!#}AhQ|^yK{{I-&|L-5;!3O~dK?Guu zfD~jP2LcqJ1Qn=316t659t>av6PTe8mct6LKoP8jRbT}h*ueo#a6vJwh7woB;8}PMo`=iu0=x(>!4-HJUV&F31a0sdybf348oU8-!gY8H-iCMJ2D}UJ!TS(~ zcK853gq!dYd<>t!E%+2ZgU{hMd;wp=R}g^?_!_=}D0D&>bVCe!;0}BXci}tu9)5s0 z^umwu6WoLQ@H6}Z58zk$4St7*@CW<}e?bEJ;BWW``TuSL@_&CqfBrEZd=P*TL?8wU zNI?d2AV2|1P=OjWpamW1!2m`uff)*6IjjH+6v0YZ1y-0fHK$!n_x4P!xq>I+n@rr!w%R99;k#}up6pi5A20~P!0Rx033uGI0T2`2za3u zj>0jhgX3@lPC`ALg41vY&cZo(1Rez+G{9rDw_T!EM26?hdw&<3x;>u?pW!5i=8U!9BPSKf^EZ0Dgtv;CFZkf54yc7bKt${)T^$|IVq%|94aS^N;c1g8+me0x?KH z3Nnxb0SZup3e=zhE$BcG1~7sN%uoo+VFg&A2v))>u!0Tj-~cDMpcqy|39Ny&unyc% z3hQA5l)*;W1e>88w!l`{1{JU!cEC>XKqc&g-B1O4U@z>0YS<44;2_k%Avg?2zzel- z6ple19ETHd66)a;oQ5-S7S6#V@F@780Um?L!4Hkl1kDhD7B~-2zy-JnPr_3WgjRSO zo`Fm7EIbF#!)15@UWAw63cL)jz^f2~Hh2wQhpTW6-hemZI=lsM!#i*T-i7zzeF#H4 zd;lN9P51~thEL!Yd)KR_IM z;Yauh?!kTd8GeBW@GJZVzr#cL1O9}+AOU^wH~fSAe=!aD@0`}3e~bqo1Rw+vh(Q8U zkbxWsP=FFtpauMM!2ROk6#jqMmU=6H=b>N0l zSPvVZ3^u|h*bL>c1-8OAsDSOT19pN3Dq$DwhAP+tdto0`!+tmb2cZTI!C^Q8UZ{nm za183;IGli!P!FfzG@OC6a1I`UN5KaT@EAM}erSXyXodi^zC7F2f7(BD@4w;AMCPUWE{}!E5k3T!m}!2D}N^;VpO@-hmtNF1!ctLm1lO z1Nabb!bk8id;+)NQ}_%%huiQ4dCTtO?w!A95wo1q-Gz*g7>6|fz4z)tW$CG3LTPz8HnFYJSA*bfKbAk@GiI1ER?3$<_* zjzJw9hZArT>fscehBI&$&cP$_DEOcO9)rih4~@_S%@BYVI1f+21-J-L!c!20R(Kko zflKf#JO|IiWq1KzgqPq7ybQ0vs}O=Vcnw~Mt8fk8fH&bfyajK=J8%Quh4Pp$obp20d^GzJ~L}uz?*M-~<;G!)hpjHLw=efg4I; zJ#2t7*a(|oGnB&?*b3XA0=B~r*a;q}gk7*3s$dW7g?&&B`{4i_gc>*mhv5i#p%#w9 zF{p#%Z~{(3J)DBma0br8Id}vf1s^oPWAHfmp%I#(83ND(=iv#s02kp&cnX5h3Qxl` za0#A;=iqs`3@^Zo@Df~sm*Ew76++MkufgkZ6|TV>@FrY`x8QAf2X4T-@E*JmVQ7aB z;6u0xAHm1)3EYBD;WPLgZo?PwC42=D=zy=`8;C+DbU`=7pa<^2w{RD}gYV%7h(j;@ z2tUC+xDP+WFYo|;a1ow_ryvNe@H9LF zm*81=4xWe0@B+LDFToXf8D4=`Ap~vk8oUly;TpUFZ^CtW3*Lrz;0C-4@4@>JhIaS> zK7^a_5qu1vz%BR`K7-HUHhckJ!dDQ14)_|rfhcrB7j#1mdf*Ox3wPl=_#S?MIP}7g z@DtpF`|vaT0uSI<_zixChwumd34cKX`rvQ)2l;av6PTe8mct6LKoP8jRbT}h*ueo#a6vJwh7woB z;8}PMo`=iu0=x(>!4-HJUV&F31a0sdybf348oU8-!gY8H-iCMJ2D}UJ!TS(~cK853 zgq!dYd<>t!E%+2ZgU{hMd;wp=R}g^?_!_=}D0D&>bVCe!;0}BXci}tu9)5s0^umwu z6WoLQ@H6}Z58zk$4St7*@CW<}e?bEJ;BWW`k|sT?>fir|f#3lj1Rw+vh(Q8UkbxWs zP=FFtpauMM!2ROk6#jqMmU=6H=b>N0lSPvVZ z3^u|h*bL>c1-8OAsDSOT19pN3Dq$DwhAP+tdto0`!+tmb2cZTI!C^Q8UZ{nma183; zIGli!P!FfzG@OC6a1I`UN5KaT@EAM}erSXyXodi^zC7F2f7(BD@4w;AMCPUWE{}!E5k3T!m}!2D}N^;VpO@-hmtNF1!ctLm1lO1Nabb z!bk8id;+)NQ}_%%huiQ4dvz20dS1}PYR%a3V?Cxp91{HP64KzTb(>0b^MLg{sQPP zfc|p;90UKdbATzwH(&yAZbN_m`}5zQ|9@fr4{b{xFly3&UCKKMFFzRu!w?t>DKHF% z!w4WjDvX3tkOre+42*?z7zg8F0%X8Mm;{r744E(mrUC`1Km$53fC*VJ4YFZ6%z&A| zf*hCyvmqDez+9LIc`zRqz(UA}MX(r_02>NmDJ%mHaN+;w4S@dnKgOni{{J64|Hm4@ zp56WL0QMI^{~7>}fj8k_HvdNfY(Lmv0R08fUjYBw0@!%0zX19RpuYhAwFR)|On(9N z7eIdj{A&xq_IQ5*^cO&X0sLzVpzw+Q0_ZP*{sQ>d7J%lN{sQPPfc^sb*A{^ErTzlw zFM$36_;(gSa#F_lhqxRVuYMr(?*#h)Tnf~`69~se|4yL)OWc00*H4`X>PYu@itP(?7?RKyyCtKLNmT&IbYf zzwQOle*(~-|NrE>flQ%(gZxkbra%Av`R~6H5XZp3>`H(s6Te0Nso(bJzd!%|`TrN@ zfAmkt|HPmA^WUHU{`~*@@{cP4hyC8a0DwPy{{le&0>Hm$0f7DY)TICV_~(Co#$7p* z1`p`3g8nM#-wuRh;9pb)|IeL)sXhNB_cuU)1N1k*zqJ8qlm6@GU;VoQ`d0({cLV(E zRs)ig=>7Ts-*jWZWF1|4w*TQE_(ysQPQw{E3+Lbwcocll0FS}r;D<(Nf@TOn3!H~1 z-~wEPC*dgwLMuEC&%h;k7M_FW;WE4cFTzW31zv_%;8h4g8@vXu!&SHjZ@|Cu6o7HD zr2h;6fBgQp1N+|&{8ttLvwsHgzi9^0|8y|)Xg%~#0R9hpI#^bJ{`>Ra|8&5A?FqoN z|E2u1M^5|i<-}3{;b2UwCaI79pc+(~54(C7e95`~jO8R$)7aE4h2`jWCa!~Fw;Ni5EY=)h1?E{8-J z6i&+Hq*@0>lJYqt{e$93Y|g0YphOaflSWA<4G?ffTa(EHB%Cq+WYz$JGd7yc9-!u= zQw9?Q^qg_l!IlAL&UpV|_W%oLLUgcafR&R$8R8w_^<@eL^9O!5y24sdfOM~8$4 zlyS(Eq2U4LoJ{M`$bbsY6#vlp01szsbZBBg6^BAeAq}kNP^~HCfi)bOKZP~W%b`b8 z*aPc049YNKU_FOv9cCGLmXqZl<{s$dOp6Zl4D@rdDZ{-31DxsB;l6_d5GuKaI z4GMGSMM>;IH#vEfRASIA&U|aCWzcQT0)MJ|P=vEEn(7%8<>XUFdI!Zgi>xDkgYI$` z`$q-`#W_o&BSVAkaoChm;Xw~L1=dlKK@T}g{iEW85}akxQHeoGTn;6Tl$^}vTGPnM zDO{dEjg?H|@}p_&Nk$<#1namYOM|+YfTnS~2H<`heTF3a3 zv$-<=m|!xCE02x|CFgPp%Ghvn9#>%<8%fURD*a>Q$!xAFIyRBa;i@U=q`?BN#+ptZ zEa7VX>8!y7R~JoZ4_0&alySshJ=b6zXBlkf8vWzkgDqTBbew0fm20Mq_YQV)3$5dQ zgG;!}{o{j!-P{$?@u9(GTnlAFcyKwl$T}f1xPrUVKOsKY!(A1fkQiLWwNf%jL#nwp zYX*5p4cG3^U=8td9nlQ-kUFlDGLaZk&vjWRT85nE7W*f3Iyo`qDz}V6 zCJnvL-Do9~hu+|B@{?IZ!`#hLGJEJvZaF2B7lEM6yWAcADZ!y}?#}3x(9nBa4`ph2=mT!0b!uej?uXo6{;Bby3GVLb z)WpyvUKNExN=fGJu~Nt>DZIUY3M+-g+ZUy zO7)~rcr_H7H-*7FWTp91vU!L7v|tL0cO*&+rR4Iw6nZ!%k5_A@M^f^6NB#783Y&K< zN>8M4cy$y8X_$a_+{z#ilkiUX8LVLh?_`w09;W8iQ<%gsJ@1s2X&Gkbo%S=`!z{cr zQKn~@m3NksvFY{WhGb6*V z@SgV1j1Ld-o{7#(48O{|L}8IeT<1M&WsyhR;63MOu||Y>&qrD85jT04DLKT5Tf7&n zIhGN(c`y2N+#@2qm!dhI5mDY1$}I1Q81H54EZ>N`yjT3Qf+OO*SEI8+Bku7+l-c1C z4|r|X*^vm zFlAntl*ezk&Wn)p`5*Y_#Yt@bhtYWn5{G}2l1EAv@ISKVky9o7kNtV9RD%CWG>@IC z=HH^sCsOtNPp$JUsb>CX{`u}y3;*-zd{3&Cf19$vo9g6$VO`)$E#ZIZUl2@n^S_EN z2&I(=pxTZKmRUev3F#E|DAQQZ{!93_x{Df zkwN|s(Z!*Wm-unYlJLmO{9fyl$jB@FAN@<>BSZY3qDvAZuk!Cv*rZX{`S-1C@~9j9 zpZ#pss4)MRD4RX%CjSAYfEaa)|EsmYGU_(}H-CY9RD}O~w7@ee%6~{%>Kzs1|6yI~ z8+Dier+;a1RGj};bZKbR?tAKhb50QAvU%DunzxmLV9@$aSZY1w%Wzo-~Reh061$ zF$BYGJYQP2V0a@hn8p%}=;Vdcas?zRKb)2)NVV}JY59VYjr@2TTQI7VpGe~f(x?K` zXn|m~O+X$k5sYaRutpPtv7G|;Xtf}nDkMhh1>`n;}y_5CG0VE0tQt|jHwqeZBomavx2Nfse6o1Fs)PS8RHjZ zQ)S*U0l{>e%s1wOU`C@XI3_5V*(nQ+xg=mw<>4`x1vxf(WXu)8tVVf!Oh_=hQ=S-e zRgg<1NMo-H=GX}G*c*bmjRb3KSTL`XV2`~i$fGKVv9|>CZ3@fS+kyp+3isHEU}2}i zGd3#7rz*W;V}eCCrElzA!Qw_`aBN(#q*EChdr!cos={L*2nuYf$k>O1rH!ii*o0tN zrz$ZvNywq9N$JT#u1!r&PZ9DO)vR=qkl(3hr>6-8R1J}yE)?1{mh=pvs8QoiCkw@$ z8c#Y!D4}Y-=?tONruC&~3uTSkU^+`E@6?9UbA<#|7f#O;Dr~w)dcIKEsEenwg{n?n zBAp{tQ}v{A0-?sHCy$c|wT*h#I6|oF)U(H_g?g%i7^fE+YzE6Xv(VURaF4SHO`QhM zIIGZ1HG0Q6g@rbwZ(NCRd808n&MjQgX$*}k6I!UI@VIhekX2v>EQ z6630bR;rmazFKIrnaSg8g!V==YrI$J=rps(*9o1}LSlTq&}A#Mj6W+ZZY*?<_X$^b z7JA0}g(cMG-tht98ryQ;_zS|djmv}MgTi&4%R}QY3Ek8c;qjM+rM4B3@mGZF8&|}~ zhlCqCS0u(?6_!ygqzTuB8*LWygd4(5jTY8~uyAvyg+1Y>u$)>%Ot>Z7Vk@#txGmh; zSmd4%5pL@&@=S;dE2t~I6Jo;cww1mKyYC8jG_DLzhzoait_)4MC-hKPg(o}^R@zoY zCOj1GYFrhckPzx@f=6 zX35A99cZ+yILm9auFVzvw$P?At z9FdHC(a}anJcBJd*6B!Oa71-fCuyQUblm17Pn3vGG&)%m3DL<;Cwrn=R8Msg6ZN7~ zHkW0hS#-M5<(_B}o#}LWCR#;jsm0!jPSH7Av2S9D=#j?a;6%6R(az$~#4?eOx;i|u zT-0D&9hq1mdaQAEe4lplPBL0J=f@FO%98m z?{u>#-xOV@mJ*Y1iC(akS|;BXz1Udlo*WUq)LH7892H%muJ=xkiC(s?_f5VldZlrF zaB^JqYUldUWbrjy8JV0S zextFBMJ9>g>?~uG)5O=Q8wql{_$}K;3pqplcH>4jnJj*%bEAh$5#OM0@{$?icWs+| zrdoW9x`oKpi$Aq(v1FRXpEYiAXIjLccW&`yTE(}iTfLc1 z@fWtOzRVKwmyKJ4nQrk{om)ehW#R~RTR5{^++o`m$*d56-MB5D=@Ea^xh;`dC5}=n zNK>lCowf?{lp1kYV+Cu9SKQrM!Jbklj#0N0Q|iS%w(XWFXT^6Kx4WnK#NT#q_e}AN z?^1Vorv${`*>?D*To8ZXxFa|vDE^^yM`+3=ah$p{Jms>u*S0e<<%;;n#+~sgA@NV0 zI}=l`itkZ9q^Z}%_iY~X)EnZT8$GP4Vev1W9`@9m;s?}9V(KmNueM6d)Z60U8Y|sX zBjVpXD?L+pN5v1RyS!6l;y-M=d{gg=|7_e9oEjJZ)wwG)^`1CE-5s9#K-_2B9hv%2 z{CDH-_|$~>pU&NhsY#L~S`~?sEE!<0B2!W%1DmQ?6q006R~4I*CP}94At>pR!S+2C zN`_=e(;hd4EE(Fh$3vk=QfPa<6ozD&eXoy_Eg9alH%MVgMs)2BQF0|D+P*L)Pm*fi z7op@!MmFt>Q`nMGUHcLgjwFp%O`-}UqwUpXszfrTshUM4BxAd(*;KV8owlE#>Lugs z`z=(nWPH6VxgRg;qnNRZFP$8Zxa$LTjpF(Yz9RR}Gt1Ct=VI5wv;< z(|*W8J1fa*I^?GLB-6SMd1!t~Htn#N7LZK0ANJ8MNMnO%oNv`Z2e?MRq* zS(0Nv5}{p@%xXFkr-dZ5yN)DiS0%YLFNuC#GRN*E({D)THhEd}uw-7BmrcJZ$)nX0 z^jnho_F4=5wq!w5t(zW^EbOZF(4&%k+EFh(CRt=Z>Z9M4EN(g)q{k&ox{ikE_atoE zu`vCCq`-bGLVqY(+H@>VPe_(^9ZS%Yq#RluiIFVj+Uv-S6e+K%j>RBJ`CWBvMw(PW zJ5Dgtr9%603nN1+YC7&_kfq|T;~oY@Dxsb5G8j^+{e+K^EtNH$2r^hwdDn>$BUeh$ zPKFtIQic6wgpn^*Hl2(!*iu#3$pnKVRnzK8Oo3EmuO~AlQf*T`i%CdzUG;3HTB@g= zBA9xq!G6lZG)s+5r`$}7)YNs#!?a4xw9{UuQ(9<0?PHcmmp7dbGTqV@U8h6LGO2}j zCd@3C7TM24m=)5MO=sdvk91YnnFO;+YNeeeWmQXU_Os-y8mYbMEGx?^b#$F&XVpoa zv~xsOz0_qtXURG%Ep9sJ&hklDcb)TO`K2YaN4!}9=^FbZzN`zqBUx9Z>zf{pXN9C2x*knrU6q#6e57gDr5o)&^0XV$O-(-5w6Jt@ zmybQ|rnH>aKuo(O-C}RBOxt~1y0xjnJuM>L*45yd7L``e9`jC%Nw?b{^G&-e-O=<| za9Uitv+J?Yw0lwy?eXxm2hvLW_}HrC_7i?r8S4M^JKO5=16wF>}XSSJew^$*43QI=E&-30n&7V?6^HZ zo-UD{XbP~V6S9+C0rqsYte)0FOxMd!*;_2r&9c)?E$-I(} ze%?2|MD|G2`QUW7?9s0Cq3LBZAMJ_o^m18){fWr*3fW^#PsFEtWRG_}k(gd3^V2Sn zW>m`>?H9;1YGh4K7g#gAvgWP}>=|{k0PP|%qh8ixzi63pR(8JWqI-r<_C(i3&kVop z0_{ofjDYN-{Yl@93$iDho(#?i%AV?aGBo3oEJ%AQJma#g)&5ju#ueGqO;5#Vgk;Zj zJ(ZYoRd$IMB+a}od)6K#&%7aft|`cx8J0cY6=cu6DZ5N-C1&1|yEVlf^uICdhj{GL=GATzO|HyusoFkEc+;o|hL&!hry3Ec|%Wu(M zAaeBbPwg*Qa?JA2nqF|{Smd8~z2M2Q%5T$N^yWC_U)W#t<&?<3YBdF9<* zSJ<=aEbq0y8ku!P{$tat@mV4HPhGDjW?hxvqlHMbugmY-L*&^v zDi zh5m*&mq843yy45uCWbe^5zJ)~Bf8%R<>nG3`kUe0JR;TcW+XSC7}@-0JeN(3>V7kk z%OTR}*GY2(#AwHL@*D{o=y=;Vr-Yc){C04To0#1Fc4$r+L8iYGo>NX_I^K!QsUW5_zZ0M1 zA*Ob}lbBOQQ0O;EbE^re;|6(d4MA(Z!J6wO=-oHibL$8O{as>iJ;8LmYngkN$ZCGq zJ=aG}>wecW*H2{A-}BB55Yrv+`Q~0AW;DMSoEs!&cE1;zdx>Dt-w)5dOyoG;kIcP7 z%xZo=J~u?n?tVWp_bQP~50mCyC+0Z9{syGtx?{vbFnPAuvEAT;kD!KQy0p7($# zaC{h<_mEiH{9$}vf>_r5VPamAfPP z|CE@oR~Q_hTIQP-#^z7m^DPQf_otruR)v}VnRmWZQRw*0H@`%&y!o@>e79mn_h+H` zWeN-Z^YHv~MUmt4$ovY$%I44G^F4}H-Jd7sS1GLY+oT263Y+6Lc|ncB-h7+2z^ia{ z-)1kUQ#k2g5DV%RF2@&^1!on-&0n||_!O(Vzwj*ZD@y2JdKUx~YaCzt7FR2 zAgEZ^{bgvuC54;*Rd~T=MXBSf$bu`1_03gbJyEPX)0EZpP9i^Dx!=)g$8IZ%Ev{BJI_{De*C?Bs@3IzqmCfCE*^BFx0s42u;(BF^<2%dZv&!?$-?Wigcn~{wmN=@EWV5H zmL<29FE;nOmqe5=b@zIfM3q2RKC{yQ+!E6`Fi(Hi6u#@tMq#$cCzZ4;~trvqI#qG9*a#o>8iIJ_bu!U)!WVY-E6Yzo$mV{Hbr%V{@&xcLFAfKYwX z{eWGdR^6iiN)+f-pE`cE6qr??HUH`^u&6%o{?$`pRo$ll<}GllzHt2JD=1NY+5B6u zz^(eK`?pX*nJPm6JzP+(>TvuXDX36=-TZsJz@z%6`}agal`2YqNLpI0>U2CLFRf8^ zH9ur6^{TqNAF`L$sbcg$h^6(a9>*V+rDs)ln*VSw^{Kw?{=>7>uewYB)4MdF`p)sE zZ|Mcq_sxF>mj+cobpIJzdPxc{54;!8uSpSu4_EZu!ob&sAP zExWF|??{lB-BA79oM0^rtA6QDu$SFbJ)rjy%WkQDb@W-5-B$h9+~-~vQT^WC=UEn2 zJ*5BbT^3XQ;rQFP?5^t1=D&l>;;O&8{|+s?r%KTO2`_t~>T~=PS@ux%ck@5-WeL?k z-Tx$(C8?7bNvWJ<^#Es5CMQKbFp!kPA*lz&k_tF!>SV?M1t(oS*g2qxlc63G7*NU~ ztB1x0RB|Zl6vn_>4nsZ6Ik16~tsWj2*vet4N5lrUadOop#-Mgio;uYzsDqQQ9vK+a z%VDcW#Rm0pIO;S;aw=D#9_>udd zcSSubFrt?iQqPW!=;K{g=Q2pC{OjsDPEscShI(#*l*13J=fy|`{F~}LMyi5;OFiG2 zTExGtUJytvBis1*)NID6cK!o( zfpb&`|Dk$mU{o(ZpWQ{mBx>7*VNEl;k1q_YUIi^97t&s)Bvp zt|@X(=nz(DRt6^Y3O$-tu?c;`Dvgzqkt(Xz*qj-eq8g1okdY(uY88{%|<6VQ+z|SDL~E;`(-_~pmWR{8}nr*SnN^w+E!I)Ajj%l_#r!t)c}C3)IfC%r?GuRR)|_e$8>V=;Q4grluvFjA!g?Qtg~Q!3G(2rzP_g!W{N zQ6N=o>lsXiRIfedWEM%y+S37Msnnu96Ju6Nt=hAUtXip4d(N5FAT7~863A+my0wqS zvf89&S|4LtyR=-};GEVWtyuV#{fz8XS+%y&nVl)C(KZFLb7Wp^ zb1b_+R;LXxrYmIi+7{>ZBH3B(`M~s2nNRyfY>`1@fEP%Zwa_{Fe3wXHJp)w)Vw9PN_VieJPexDUWKeFlN=tW7?OUvl`@g zwXXzbwaVk#S7Wo<#jL- zGl>-48-d&$f~0#hmRmri>8>;8D2R03Th2K}M27C|z?@Qota~Rmr;?!PZZPK75)9qD z&bbXlw(h;a+*X36dp|a}jmXu78S~nSJYBnUUI&q{`yeo{mtgBYjLqvKIJ%pRyi|oi z_mML%Qz6lP9LUR25V}udc?Al!?iORdLZR1v>YQJsFzY@G%r8|~bf3rOS1PQ!+l&RZ z3a9Q1=Yj@BiSEn5f>woF_f>2`o1#n?VJvJ{lAsCEs#N-Q zcNvRol>yy%&czMN3%c(Ei(8dJ-4C(FZOYx3baBR#cI9PVuX9O<@`~=qz>;2NNcU50 zNuTnn?jD1ks=BVb?__7HZs>jvuya&l-7hhAf$FC20i!^nx~2QoSx}_9t@|xdP^yaP zevcJYs-n7wjHR`znC=hf(gxLC-JgM_t*W^0uh`Ny)jeH;v8-M7K-cG7)}eZ+`#Z3# zSC!EH6I<4&O427WIjQPo{QwsyQ=Ot8*uu$Clk|gnI0fo7eKM1)P^aq$ySPQ_4E>N6 zZmF8AAKJsMR8#aROkS;;p&#brHK?=o!&`W*YLORO`mHZn7Vd-o(Lh&)CN7%pfKnS2A848U^W*RYJ<&X&NS8->@DUTqu1c*F&7x?3{GaD!dP!`xeAMnXAQ+I zg{4NHVRcVorO|IFVJ@#V1`KOl%NvZlFBsOgEN?Xi4eNTAw;3-P+{_j2#><9M*NP6~ z6~p?L6}`rgVMEV~KI2tG8Pk$#x^CF$vSgZW7&f(7a!g^v<{nFd>87EaS)?%CGHh`b z6`5`uwzd?Nnj(g6Jw=tKsG)+nvepzcY3B&H5Reh!;V-?eyYECxpaal9XDaO4m)*Lg*xUa`rU`{huGi?fU zx^chDR%FgF9%!+Zn#sn4J+?|S#aP3%*P0o|LoR!RIoo)+#olUW8ISbX+swH}FVoR( z&NJ4!93AF-}^Z*?79eRa$5n*PUAUOaYJE=@sXC|)pdI#RwS9OGRsme z$);5Z1M91F?xW=~mxCCzl5xlv(BH@)TBSY*jCz1_00)Iv7B)3dSCLNVQ7 zZmP8~Oz*ljHCVDu@3m}dwXjU@_iSpj-qUMP}1yEn7;9ET+$U zwp12bO}Cj_Ym1zwFI-z2ib_mhwrp)Ja+|*D+1gfAW{NPkwHK9}I$Ya2iYiQBw`}Vz z@|eEq+16K7Wr{K@Qdd@+I$af+D{D+$EfqN{y{7J-ih`AOrWkX(Vr9Ll$F;p^HC%)tt*43A9{AQt-NH4Gk3PHylm=q z?d({2#q?v#&fb+F(@#A+`&M2x-D7%ES6w&VcX={b-7x*!;>lSRHvQ7$DOh#W^nh8Z zSar+vtE;kT)os&nEtRFKBBtMaDl1n-O%Iv7YFEWff4Fuvth#IZvt?K7s<`Q|o?UIL z?wJzI-R-L$nEG71J61h3{oS&=cU8jlPtWeYRY~Tgtg2LNvUxypRi-t?Jn(!~j+JB{ zbf>Donr2SU+M}?hn+F%~DY9mmhn(M2Y9*V8-q}-WrI=H)_SRY%=3&Kq8?4#p;pg|Z zT3O~1clNefbIqizeeKpfb87Lv4r{)7I@KmHk1npxv`Nfk z&R6Hy2=mxG)de=SIX!E?!lpNmE8bsZGn>bs-(PC8m?zxXUum$=NH!&Ic}R5oUbi)M9d5C)K)s8 z=KQRqwT_s1QSs3R$6fQ{^G919ar2TpN823t%`6N++dIl69ii$taW-pJ;Gq7s}3`Xmzp*<#$fBIdcn%tds4|yh26s$qr|Jq4NC6 zUMIUyb?0QClT)b9s!w$Z3N^*`nJ!78_I!Pgizw9HsV{J;3-wv26fS+Cq4-ph%UozY zf2!1FDKyAZ-R3GQv}B!Wca;|w6`$#F z{a=(^`Cm+5+z%mCLXlLGG!vCpDlJEwv}v!|?`(Hw+S8(aFVVj532A1ig!mRMr-V=m zAxjBqCMqF>$}`XNA3VRzecgH8*W5Grp3mp=Ue5VM;>h)-Gt5{VWwLaMnTSKw%cRg$ z92G57LNjo*`Z6sv3rC+UGeh%m4E1s+vnj@32AtnyMF-l9^H;ANKwEGDXyqu{iVLi-oI%@hL6enBD8vP;S4pvY zaUp1x5~~k)yuM0{)sG9EtTJN_;lk9bomj)TaI~7l8o@=>R|l}(;7&|d$Fat7k?J+s ztO;BcT2svu;-c$o8d=l0n8}(B)*LQY{mcMs9v6q68D%Zt;_J`MuoiI%lV_G#V)#V$ zS}C?TJ_)T=VoTtY>ua^xlK7O#S~Io`K2`m!6I&LahMpy{+@o!di=_&!SV_#*WSP8<(>F?xZ-@xhnWUkKn3@uia& z;y4JtOuasv!@!rL_0=3UzM{Uqk>iiAoUHHQ1mmmJxdWUqd^O4)bWzVSp1nu z?h+>vU#rfO@=e8`MR`iT8TfPcJT2cW{P{_qnQtDxPMz=MTZF%W@=3mB`1*Q&fNvF^ zJIRmpt;O@yFJ}AJ;rZyrYF{4yV*SNN-v<1p$%`Gn&G-iOO9Q?w_(t^7sBbI2ss7T8 zZ#(|-l?KE`tjE$8_fKM@Ga_%PJYAq>u4j%Zv=m% zzA?b>4gSB$#yG!me5-m>w%-K44Q;CS6XI{yH#PcA<8Mtib@^{xXDK^(#*PvV=S6 z6_UR^;coqv0Dncoy~!(a{wjn%^{d(bYJ~gf)oOn%;X(b?Mt@Dh!^x{1{yKzy^=kwE z2803h+Ni$~VX*$%jK3-2(d4xye?S;gZ;=YHCOk%4lmhGsPwHE=0vriXCtJ({TnNMJ z*PQ}92+z>#qyQhn^ZM%n0Yt)!$?I_e2w_D1Ms@&$@DjaI9l$2Ms=v_~;7@oxd7~pB znD9pZzkz@-!YKORXh0-ktp2~5fLOxY$^VuD5((q#tx|!hgm-AGQeXz*eSND|U>4!S zWUEpu$+oz_T7pphW_Dm5VG6xj9mpems=wJ7 z*g*I^d9x$1nJ}$>YapVP3t%DQKAR1MMIMjSzm;cLW5zA^e)`hzlAgEU0&82Tc%uqn*`3 zLc*W=&c>i=!r#fxj-WZhqI%ar&^%!Y?HUbQAS~B+%>*qH{!Ml*1&I;GFiHN?>}jgVl)Z zSUuIjSmJtaPh+qqaf6_zBUpzhiMc%xY(SJ^-5w1#B1&^_&jgziHwtbq1p}fCrdKM& znz)J8s}y2K+|2FO3UMTE5%ijcxDaJAcbq~zh;pnuq!1tCR_>jE5F&A#;7(i!LX^kc z%?@D@x3lh6hp>qX+`Ek-{=^-EyB#6HL`BTKfsinw66@Y*NF-62dv7Krmbg=JZz&{^ zsDkN}I-W}0#p+W!oAb8RdrbBYXJRJx#AUUy~ zj)obLoVibD!c0lW1W%X30LcY2EER4|a%BxGh1-$bxWiiEjwE-%uvxeZ$piDuDcpnP z$$CZ#_aS+4p9O>yN#25Iap4Hb2lG5ToI%2|o>zynNqFw_#&CZULGZjIJeWkpych@% zBav7yM#CdXWbTWZ@K_Q>@M0-Ek%VAIq#{yDRMv=6L_!9M#vRd$$Rg1NBW4kKBnIZC zQ$!Jo$$CkOC?lcVmjMw~B$nW1TtqF2jd_(FQAgsiUR6i%NWR=xjS&qbKf$Yxh-Q*M z=Jh~C3n_s0dNiVy6v%x&6VXlz61-lDfTUo|8>th$q!88{r4xOmn|C;CaDf;VO- zhDc$UQKu8bq;S?K>BI;rf;$>;;tlD9U^MQ;I4Ke{mVIJ^6vY~=J|QGUbH^G_Op{^+ zV;v{vNU@l=11IK5ajdtaCl*NY+_y6)7D)+$w@W9)$cdP7sYr2h5^G#3Qi7b!9oLGK zB&P_*%_3#UshD?8k+S48);m(9JUN~FE+A5od{Xc(E>eY@fq9=DsYX7UFdqgY4anK752KMre_JR|+OOqJqg)7{NeP7`d7y7>$Y~*Kh?hQL*GR0>M&LBDoeLl!{IzpJfS^ zqBF?nxI(SyEb@7Q&@4KST!)!*iY_8wU`>&t%gFWIsetGzGFLDa7hOx{VLoL?*OB?G zPu0;p@4av=zX{nfA z@)g#!QcNHDDtB5drk{LGFl`nyL~g;%IK>Q;ud`-IF(c#~+?jxwH{|~WGjTEF_L};*@UIoKma=rH4DG6)Q=(EtoTlm7(-vzB4l2)v;L01MauRSWU`9!MBcB9ZEmu`#`J#Wq|d4G}eeR$o)POYf5<} z_`VbiC_|WesW@xOW7fP2OQY z&BVo0-U@y##U&(C#xV<0@u`$|tOcd`49a`%f>wMM<%3|sEIyAif%)weUqt!H`b~;2 zqfByt2gFxV1cKjj@wF5o=1+Ef9c7C3r#hZT`NaLx7~eqoEcnwA-%Odt{2hpIq0F%U zj>flAX1RZ7;@c@-1b>&}A!QD;D3#Dl`N~>UO6a3}<1T6?^i#eI7R?fdDD#*lr-Wh3 z57rVXVTAINyA+V{hVn}w8s|7=0kfQ)FhTjvTCPqIQvPt48xy7}e+A1O33HT1%)fzz zdCC&&-)O=DWtsbLCSj5CPw;OkK@1VYib*GmBP-Zq%83%lN}iZ@q9n3PC}y50gNS2S zI48;?tJy2aiSo!A-ip9PMP#jTMSP+PB7t3*lc68-=Tu69FQF6_-x3MmDj=3T$ zObSL6v1U-Rl_#N{oQ3Qb zN|-0-A!^ul&dEi{9`-tNav7q|TNjvIgRf_3pXq$LqrEFDV@@b=&~i1Q~D4+o}_k4 zKcX*`G*1~q46stpDZ_{%TZ)`Af*jyU1*W_~4hp5>Q^pY^taMJw1Y*pVu1OIhCOqk; zlxgIUP`Wc^4l%`U988%<%-9>pQWg+%-p1LKMZ`k5aXCed3a~QLsp8bbY#HTL392Pe zMmtrKY9*90PnDrsV>dac%2I9Eo5-p1R9oJrz*I%*5#grzR28Znc5_au8uci9b4@Ci zYR}u;l&VQ}5N_^F)uB3Kw+yBlP@ULY#!`)_&b%$NsixFp!Y#|Gfa-#kl}@v!y0T@J z)9k2jJX!5DN2VcJWPV=C8vgOEWK2$HBTwofJ>MfLuPeZ6a*sVEf3@VPj zwI+>C#q+i{rTJ3{!mXWY!Bir4+hAH4mBijQmX;7nCG)n;ro~bz!fnfGiBtqDFP)xB zrLyIf(=(_vp1gK?7L_iPH&4%_GO*j7(~GD~_I7f585QMi4@|G3vV_~?(`%`0tU^wD z9hJjYs7dEheR&E^=?zprp+aYRGu0ovV=%pi8o=H$mflJYI(ccPytvYA{w& z`eZLPgsrH2vX6S4r>K3hpBgGuG(R~+4Z|uqpB$!!vz5pvN2n1zrNEPKs3(L<@h8Wr zkyz!NlM~b^wsOr$AvKz(+;no98Y5KhJUK^=#qJzDIZusa?;Jb1K#k|^oISZnO%U!} zJ}E{^#HvVVh|`kTD#{rWv}B%&c7`M^MW|w)Awx^W?sCqMrKPcVku&6J>AYQm8H%)% z!d>whDzpr&YEFh4?G#(JCId@5%~Ng4(4=JwRXa0uXj$0ZgBb?2Z1(Q43?o_&Z})75 zDJ@sHdpQHp@~~>sr>trDY&GRmcC-SXn)WG2TA@(Q{FDo=2)oDmln1Sty@!0thgQPd z6L^Y9D;4gEKZVfBudd(>|t%9fCbjqJrDOB$~6-=wbVg^rz(W=>)u~U(> z8Xji$R4na`5VL$LkyeYvN}o=pon>Q{PiN51@vz#bvuNjqSo70)v^uPY^XVem1-1tH zbQ!IlrxAF%ipCXc#GkIE@vwVyPS?@+?7cOod9;hXy-lYZXqSY0J5M*$8nF8YPq)w- z+55&$x6+z;`({tK(=H45EuV(8W~`=kW-sjuTT?l+k9L)(sh!zRyC&2$&m5w)V6~hx zhiTW@TI9?T+6|soVCEaze?qPJ%yC*PRy!wig4V{?uE`YAZt}F7GN);`gxZ~%bF_Bs z{=v+7S_gaoSmpw)led31bCK31+`pVDMu%7(=`3-2H(N(JOM>3R)6ve7q~8|mm}kk* zd$GFCS+evyY+Z7eJpC?DH!w?)eov?ypQS?Y!|LT^snPGV^=h)P^anh>rYueRL!n-0 zmJYoit3Q}!Kp$Z1k7XIr2YLFlS*G+yLjC0|Kp(;yNM~EqAF~aVv+d|lcm~?pj`XKO z1M_Sb`Y_heIopH&jBQBH_Mt!L83tw(=`Vza@!1G{1bZMSn?ZlcK2Vd*roZAHXv+4d zzZM?o%uWcVzrh|H%nqZEvJZ}BN7Bc52WPWm>2HMxm$MV;<5(l^hus^U``cXAT*B8sih0CCOJ8E^eMJUO%9L# ziD%N3(?I_$H0jJ~rcYxJ4d%4aXV{0va$4!LyhF1&?es6gL(4gkK8H1x&h4dtWt%GJ z_R+ubOto|S>EDH>=D9=kd90ap?lAoa+l-t$LjTD#3(S2(|0Ohw&mE^PV9j%KC+NS~ z<~6xO`X8QoQ|>hVuh6_RcaFY@wHVBur!TQB#&Q?v%RGzO+(r67p~Z5p7(+}0Nau+& zR&aoFo&;khA86-EGFD9i^E?@bxW-}UJXywS&S7$%JYx<2aA2MyW9`)8_&gPcgob5K zo*H8v$Fe35%UI91Y|7JQY?!j_%+p~=YFG{C88D8|Qovh8)L+obSWf%C`y3Co;B8*~I4~ z40#RPoO}jjJIA&rpUqI<+cxFebcIF2&6g7?v=7%wqI7i0vBN@v4BeVIjjGa?Q zmh%%CDjIgu1*wc(96RNL42CM-PP-tBv3ttSydaOErg7A{pop=DbCg_A#!%-U4J@c) zV5W}77t}Ja8umE_bqo!TeN6$6v6pY(RM5cKH)Y>h(9F=(a2PCTVQ6t2#tK>)+I)xE zf_BFKDTn0($k5SnlrHRL=yDvD3;P&)d`IoVeun;(qj}*F!$8Byxp0_a$Z;YUjxY}J zodOHrFb+;R#TSk^x}I4789A?Q=aj~2-8QyE2o&j#Bsc8iv8G3Jm0IS*q=$5^6D%OW)d~L z2aCg)B#!r3aU_$>_ns||Wm2ZRmx~jbh=z}JNh*`d@lh_xVAA+L+9g>``jn4(Ngk7- zfpab?Vlp{6a!DBz<>LZNs+g=PTzp9_ldXZzDXC*}IQW_p9@CePZz^eE`c2_GOPZPf z8ic`;7G?m4Fjmsa4CE7LOWK)1Q-tLb$PCsXN|*LBLpVg`(mv*KK2f{0pBXwuG%p=u zhG~$TONW`^91^*7gc-pn1(v>Ho|q!VmyRLl zIcBT|Ww3Of8ONcFl`b&j`IOnxMP|YjWw}%gP1Hc7%f!(n4x(HpfhO}2?J`L;WePDb zlR;B8sLo}wXc~t~E|W*o`P9HNMfBtpHNH#*&CsCbl&PVoIJBBFEP9$xYbw)3GpA^s zWjbh<27R#10L|vm$I6V*96o)v%oNR?qA!;LG*5#eU2cu$a~R6yc4z^gpK6%cLK@RzRaMXzxDl`H$ut9*a$%6|0P zl)ri95Za;<;9NP3UgrdmD@V{9{D8p9H|T#;0r8dNXsbqGPUQsJ#tE#c6rwlzflZau z=&h;1&dNEoT_b3)avtsA1dUZLpq>1n*~&$2pqJtB-jwahR z<-|p8QC5nHK4e4(F)`73;`?=S1@G56{$G<6vCU#)iC@L%|NUFtgmg?9xkIugsnnNA zgJPVld9MsL*iSj`eNWs6n${luG<59<3@bFQwPDlXIbEe!U-sI=l~pd<$*)Dg`g1XB z5%B=pf#0_%x!_=;2l?Km_6=b6=nLetXw}H~r>iTo&l6zVlwkT_vdcvQ#p?RNz*z9KCiyifuj%3bn%Djk8#`Y^X-F%z&{ zVmNd1s27x_Xz4pZCK$J8sHQ68z@=6(wa5?dFz~++rS=shsQf49p58e;q{#Z+KW^y_ zGpNSb*ZlPX58UT;bn6gU6npuK-3$?`xbcSy6klDONzz!(=F-wBYN5JB{<#7EW zJdFS5Fu|cRLEYf-0n!#H088b_cRm}7jwOZ*x-_6@8fE$GAp@E;>o^~pr9imNo_PL) zG{ClpWalQBLzNrpkC!}kp~V4LnZmsYtV;25`B!EK&rO~f_3O5W{#|3Y_@PW#bfJ6k zWHAZQ24jo4*kfBU82NqEeeZ1sP|WS15|WICcM8{5deHFj71MpTeLo7CU)5Ke-FASA_3(?XB@>7> z6>q=!mJ0S>AIg7QMhB8Bo!gt9B7iN(y2r3{0v9Efui0x6;rwr-PO)MpOf+cxSbAI< zs+%M&d|!(KYJ~!~)cdNIE zSVAOp*OjKyLqN=>)BpO~dlt5zo2s9=k>SX_10$>|BUs>DZ&@peK+T8#+7+uwz=nI{ zw?Q=x8vME)@|Qpd-z07cww^%1-Y!`MlZ#aNa=E1Mxr__+;9iEBdX7L*au=y%+CFrI|7WHlcWPI@Nh5Q;Hx)k58GMGme;O%0SPk3b_4W;)ovd(fBYoE zYGs|99YNj@8jjWm8_=N6Gh6OuhB>GJXoI{R_cF zF!|VSa*9ra<9}@$nUbbJam~#xb5$=eClLng%x%Co)136AMg~CcMe22%S_ApKBWFit zsK6_ddIxSrf!*fW1>I2t0BIv;;@|ec;P&*`40k<{w8Put!WLH;+!jtvSzTC0poXbxF#t?hv?@V()k)+Y_>rZiVO8>j9PMp33w{1c)J3 zUz&^Sx~6lQg2pzx!@mC>gGFN^Y~QZf8y|xM!J|RGo}teG!)UA7Rbx6(!HshB zZK&YY=5-R^AA7)yT73-%E-*pOg@sq$*Xdw>q_#Rs&jxm?oy%xG>e@Q|%?>tvH-=PW zE65AzJHDdc5EvvdecXl^AlBnY{HA084QzJGHul)UbECS!@@+H#6MeZEO8P+Jy53@h zI2CGI+>KgaP5>FL?e`uj*Z`h~%ZAs|Ucj+-y2xxZ6|(o2Cp?bv1V^gH|8#8jgpTIC zVkwRjc+i%ACj2e~%=u+V{CdCuA>Z}OZ?0j&iTAr3@{cfJ?W5Qp+ORhKbKlSZyf^~t zUawRy9(Dq6ejXXh{IbG3&Be73+_q8zKN##D77vf%UDE~XYycfdTIJJj#s#VK9x!k4TCicjUKoA+raiaJaShQ%xNeS_h@g5JIhBUE`!`Zx#iJ@&4LYW@v(1`jyY)JO^0zXD&OKq6cK7 zU0+*#a{%+M!JF)MdH`nXsuNym3}`<|Xq7!g1BcfCu5bD71{3W|@FvTv;LhQo;6gn* zR3_-ptR3}+E(_h8wk%V@`?vgz)D1M)YKJ}hqs|((XjQ)pa3F%#3SsUxDHJeV#UnN~ zF-7rr;?#jAQGNN;meqdW9Y%N*wwN8H!-m5T$+_AdAP;%UJKy6B6h^Lhn};GmhEBZ~ zw#5!ko1HXF#(IM#&)b?E+Fmf496^G0+jAs-8*Q50=BV= z?(JiAVDv@IgeXTMaWohuFZrs-_MIT7&|~1 z;f_Vy%z+mEsGA#x2F<=CZoKl34)@jeQbv@0VDSCbJNoInfh+*!t-<^YoC?Z?{?R2Xz z9R}s))T4hET%bu)!QeG(I*c8JM|OUr0^RPA(06W3XgW!dom%z=&?jh1HC-Rp-atD- z=JfzJ;i2p2e9`y3z#@x}he5URgVRYyI4JSZ3^6%|0(h-*y|D8L9N(3VBqdN_Uw%CL z&)fj29ZjzMG-wMeHZMz_6yY}CEU8^TTs+~a!rn`37TlqHi`(eXP8v{HDVuj9mkxOU zjtU5}c<3Q>bIYTTM?e}E{CXFL!s$1UL!HcMz+U5l_aK1@R8BigB5rnol6G-Qzuf~$ zw-nfu!Vxgh#_hQC(H822>j+A(;ebkY#i>TFGDw{N9KjX1fK2_Y`u%I&p-O3<(}mZY z0cT(K_2(OzF!)^D-!5%;ARZfY!{Y>iF4m{lURLk~8AoS7M$6N|boqtRcC0;Me-7Wx zKS%)!+6l2Q4_E-xU#Rc+Gan!!Az`QSg#?h;Lz?IXA}|vEaJC&Mf!GagmKiS{;lkwK zQlIy>Fz3I#t4B}LV65Duo;RZRC7XBeSnwMflu>`5$jjjauBp8Z`FP4qzkf2y4Pn;I>71KW6XV_ zo*-7Rr6K&P8}L2wbawW;E7YyCJ;Q(D3VWB-(-OOwAXqwCb&s1wcZJ4 zG!#{B@X>`vk5%vb_!B|aBk#buOb>v3{?}kpv=dr27A$U$aDfH-v{AH$26}TG<(f8m zfCFbdE`5*nfsxyIO*al&gVsxlYp)TEVFr>^Hq)yIRh{?r(|)>vwi+kB5Pd3~QcG8^ zP$EF`SN0C+N=F#-mE1KGK!a8~nL(n)IB*(m*3&p8!b!=e9%%md1huDoI4Kf);a;^i z#yhGDNF4N7xw+&|*V`G{U8OTPD0wFTtmOtGU|;%Aoo8nWYWMd^IQzJRfbR}uTv6BpKt@?T6aSC60~6=KflOQdoRSUUw-#vHwtXu_QZUyL%`6M#ZMM_ z`mo5dq&L`!0EicMzkAeW35+mDPL@9~gurtFTO_^)_Qc(-&#PtvpYK<#-&}Nt?Rt}L zN5dSz3!8}DUuT(65;wWeXz&;eJ707=W!4h}w;eUMv#^2(iTVX9p>+7k(i|?!`oObk*=!kUJk0yn@&u--LYLZ|U6lmZ!O(B+|m(n!~elwvtv}?n5cY+ZDk9GUIjn1zp4u7GMn5A3kjgMg`LTj zRe`*Hbtm-;Jz-R)iRz{^KCnIf$0>gX9>CYimvYxzf(S;d!Yif&Z1_agdZ0@Ok*h!W zrFtSzEGkp=@fmaQEAEC;Q6&?CzLUYLuI>agt&Ke@!FY%q3^hDer3ob)LO$QJISl9T zj0>Np<3M{c=Z3>oS2)L8cynd#AyC%$>s-x-18`x-lQqhDbZ|OI_F(Hl3atBN@%ZpI z9H<(r6`C+Sp^jSfR^@9hu(>xur>KSw6N?0eH(p4CYqlSL`A`^eVfI?M;^V{cY0JJh zI|966ER*+E-i`tSUr$~0y?hKtcFx~XVbY;WjEhcpkEmcE<#6-r z3U4^R<-_OE*Sn$RCha}ZF%ydY zTOi+7nYZx&xFnW;i2;2oy7f(0it0*vhw9EGZ?JN=b?-A%8rXe|?IUY1y1sA>&Q~x% zqP^nhnazrDyXLcZnrR5^RWR~MyKM~@RqpA3b`y=0ar^e1Iu1;5*489n@BxV@@PZhD z4=|1K?NBHp0O|Cz@sqDyK#|Wk`~B5U5IfpJcd51kVtJJTa*GV0P;te--Uo%_^Gz>{ zcFO=W;{a-ih$qXEbfj`)?lG>1L5iNA#hhb^hJiJD_HkL(nf_Ric^9iHb&kPXx2H=FBJjMa7)$d?WjB4 zTb`|^lnE{8k3zTgHL?w?W|cX5V&UpaX#yS+iBozXuXYj>ET^G7QAj{$U7xT-hN z>J8m{tmcoo5#Y$;)shw?PoR3O`=>Gig$E3VG-jx7aM6EkuIB_9C|v#?>GX>RemZ8z zDL<#fXNMGWhCUHsd-B`2C$|uR+(gC)sW(tGb7@My&2Sx zRR?NYaIk%taYtFx9x^(eI!8(zpv#B(xe$^SNG<-hCi@ZrUhNKfvC9PoZ@v+i%k&Nd zH3cr~p1J6pzx;rD=?D7!F190&~2faMSzdb;))hY}Mt<=W>q$_I%<-nJ{nAzUtx~t4t@7?0%9_K{lA|gT>eL{ z?tvl|{vF+X^v5C%5}Wa9iK?P`XA*YvjE5`O5UQWEPm~wTzlYhYZbM*L+C9Q{0RdDs zKE}z1IDk**&t~a=aRJZI+WhCODat<{ul>?mbwIngeb~b%mOy4-)ZYeqEL>P}x@%o+ z2lgAJvhJ>>!@ma&!gV$2@SQqwpNkX~j;D6hYW4TSq10tL+gDh4{=+Or&kqL~-hhc$ zK|$-u-6om49f940YsIDhHo!4TG9lpx14LZVi}<(_1;NH5PrjN6bSiD$AK(yy_2tH; zGhVKMw`!vA-z9I5_^aH!mZ1hG2Oq*;@0ma{zUjqrSrP9hhkZM`Q>25qU%9YH%NEE# zF!ay3Zvn+pdOAzolUPngS=TeA=*f#smD^MBJ7o;;(vn{y!>&bXatTcDnT)0E;&FhBZQtf#k>o z>N?lxV7ajT)!(o7(C)(8AvqxxwCm$2k-1bTRI2{7e24^e1`_Ii_fx^<9#XO93mS0w z@C|*GcMJq0PGW!C4#1mgJBp_+FroInBSSxmXh6;Iwo&+L0L=|#bb2%pIDcL-?Mny( zWz^4`)%>;tts7IXi~u@hdnKr7$a;YlYlxRHt9@Xq>{V;sQzW1(7U%Szy-45NnyR&b z4#3-24n`*ixkCeUQ^LhDOQ@2*dHxC221Lgl%fRkLfJKIpRNN{Lm`LtUE*d!qo1K5} za9?x=Zn3KDk#GdYJKktb`sxEChE25wv|IpnTl}V5WnQrPP=oJPo83^Y;x56s#~P@5 zJgD2W1A%r)s-VXmfT3`SNwW+sC?2G+m6RZwKdX}@OLGsypXUP?E2l*9X;qqc8x_q{ zz5LXB*EWmz_FcKkrx=LX-d*#!%?(^Q?4>m}M}%`72Uf3MWIzvSuP3*b1n;uhL6KN{ zsN;NlI$N+GhCV{5J%byq!2#ie>8`ob&z{09yFUT+Sq0M#M z?g#xenB8NMRo~8lWkS4)(cQt|9W$UK|JCfVKLW>Zj7#$6G(jRSmN#}-4-VJn4u0umKs9=u{bd3j%HQcGRK52E zz=5_;@!1X_v?k~mDp`U0Z5qm4f+$aT@7O3O!lzoFR9WI!D(n~fHJ0A@0?`*!_k_sf zVTRq2wCWTy@W%V|^|u@*U?!^@eiLm65!=p7ASZZ(uB@$h1C=RoD4cey`J*+^9=or> z=%d3M*{6=kWiSCFW@7QndJzt34SBVJNCLIjmikVJbZJxR(9)Kj?!alV%wO#%10rpK zm*+0%0Li}6kp*u%II?_5-GX%l$kYG4c3Pzmy7%|WnP}6XO9iujj}jh$X4YZsA6C4#WxUthBKR~*&dc_nS2zStq`DNlD zu<5QozE8wAtAlIfn#>^U@8b;yTpuWYe8leECL0(WP@=Z?jt2D5oKQ)RAp!%%^saLh z5igkBfz+M@0A#N-UP`kAE+sYXe*-aavD-hu_@1k%-agLm7RQTZ%EI7ZYYjok*t7FJ zRYVx>W7sOp*Tq=@2v&)=$hW}sn5j&_17Q&iv21cw9} z0@Iw-|9Htb@aox#(vlw{+|UzSC3DpZ$eW+)k7VJ2`8&~T2P4_(2j0cdkZ%Mq@SA=Ul@;5xWDG&Ojs&Kxo?qIJw za-v|rh&Pj&N&oF&f?j|0x?4H}27mr>sTpwx3s{HjO_g*gu{rw0r6W3k{qJz-BVQ-b z5-l5>P(%l$UC~K~x*kw%ycF}{ybnz2Mr1Iryr6-{p_fJtL{N)9=$94~`3R9O^KI71 zz~@f)3Ux%jh2^1D$GGZrXn|R?k(=NS!OWnZGs_!-YjO({6N)0frna^@KpM=NdhY-Y zOeiJ(oiBBc0MA-3iK*Q2fU-Y(6b~7Se2dqEw_dA|fwh-E@6bjjSkS~i`moChJb6SO z!ro%Sp&)Mur$76k7)61Q@^CYJub_KypCJmdVvoT3!^ePf{hwzDp9*@7vv}d@Uck=i z(${l;tw2WQQ#l7V5$3Iy2&Ren--kb$a9PBsyJl^Q@giNb`R)H=5vWhJu7ma~szm!NcJ_HpywNL}0keCEHR- z#N*>HW>3br0Wqh>y-}Oqbp>z5eDhA(2augrPHMI)z`7?CeO5pbwO~n$aC=Mz6FZFD zKK303!M#T5yu171#^|2$lnMqEx=Q!_o%IIiObjh=#S!4N97!opmkJvS`<)a^Nnmzo z{LSoc25j=5w&%#YL9p4(GU1>y5Cw>&Ak_ni{}{elt7;7#ee?IW_~O9G;-O=q96e~7 z-}5G2myk3M_(SYuz!itOX_Jfagn4tg)f;(7q2$yVzQjKf?u*`jEw+yV zhx2c=NoE}dUv-q7%0&4@$1&}&@j4r5{?OrrVjUG&TrK51UhNKlsmEZeKD)r$ug6X8 z+^pfD9ZgjalRV*&`MG^G(Y%$cQ5G8RJq#LH&+mSzV#4S|yQmXYG$_=I-XE3g0M7k* z)#><_4&XljjHnEC*tGu1ns0b2G=3c0+ptqqH{#Y$=)3zsICjiqyMi%n_!4G8Icg27 z=U-kZ_$uOMBRPI+HK?NJBM_h7FY<-9{(NwHm<(c-K6c(_`+(NVpGMVY=-|%TZ2_j~ zR5;h_5|zq9pw3n!w;6F8aLY7A!oh_O%`^Yo&%Nym5;O0IMp;F}(!q<* z=2^vw`omJt{nXOXsh>xvEp0z?bSu$niUe}iB>3Zo_q2iBH|Z1AtbW3z8SO| zec({@)EnsRby#ytd7hFu)^CbQ$5hT7HV*4|H zSrjquZZP5FfJ?gHg-8z~7|HJbUVr@%fJa|H*FJ9y8E3^0I&_HYr16$bI->Wd`db)$ z#)}4m*Q@NgHQ)j5>dSw~i*z$FBXu=BUbh8!yCyO=VjI9e8 z|pEy9|dd*BA-y5VqQIEL%M1moCQPV4&`~7v}Oz$aB8>iJ|BP<{$aqc+r9b{=b{S z^&v#$=N`DQahNK~i>8I=y;Xp1piy?)Ly<22ZfSL~oi_|Vl-f`wuy9gq}}Ke?Ie_3DTjJiF}RPQO8f=Q_Kt4RC0nta|LnW=kiy zd|z6s>L(f2fNvYmZ1;xsht_R2Q9__4>SERwOM~;f*SszL!vH={FMUoOvxcK~3k%iB zR4CS@{B_U6=UpgTJG@P##~`$>VY6MPBA;J#?BYXDFr8sRjJrz#!;dZQr_Z_rmn`Ft zW*l9J=6IgCm+uA^yz;&*G}wdbzPSO)0dJ8nz-K2{9DtItnig7&qX3%popUyp2fXiA z52)X*fq%t=?%#MvAb-^;c{SY?j(cEdRbBPK4P)<}lD~0KMS2}sDrzU3cyM)WNzxP8 zPVFuW{lx@Bn*SXJb)V|} zm&Wj0$u;FmSxm5p(bVJfSma}`e(G@bC;=!E#n-Dq9GH7g{V!HQgs+lxW>1gEgXx@A z?_GYogXZPtzq{IffVOLbI+kV+x7<-2wb|zhL-re@+ussk*hQE3=xJr;r|iga`(<9|4K_Z{O7uH!)Y2Ywe&f6c#H{Ea+_=M zPJ2M`yB(_6E*V0r+O}An8UhEUD@Kh(^%Fi>@i=Y92QZ`}>F=z)A^Wyl45wEIYTk2i z)t;fk=dW9~^`Bw@e6S*Kal!@ERa0t{f8yb~D}9sIzulp%)+q=1BhEl<%WO~KvIVfC zd0hK7qywAN$s4An%z>-h@1nKQ1XxSNTpvAx0e0o@O-GdtU`@!THGii>zFO2>w{SADHy-crGqr#RR*SEB zaa!cx|K!S~Q_Mlwfrbuy`NNQRq4z?QNMD2pPwmMZ*bBteO z)zUQF3d#sjr=V6eZe#K7MFl&+MTVqywFt)y?|YRZS4M}#`LPu~KGvY`@(p+XIvogB zmraZYZUb$;6J#T2CQQyj*J@}ZP!f}_{osrj=&93OH*AA|7U_389%dc^K>f&@HO?!6 zxpYXb2@Qb=UQ2D4ygtA_BstEhl0e^$jb3os2uK{K*2$RI!)z&?#q`H+@Xs0Qr}97) zg6h;;neSuxGQvsjP_=VU>cO&cKf!>BU?D#=xjJKaRlmhEG=mCp>}5{?MBX?{N@cb|Hh{ zOoLOe#~u5lUE!eimt^~5CK&&Sr5s!XfXYbF!-Y;Y(7fl@&3+S0_{rG6Y;6t$wElS( zV72THjxTL^6R`mS5)-R?R(&;u%}a*roB}HF{kHKek4goF%OM_@nIb+`{d}9zh&5~* z@v2R~>-DQFg$sDiz zU84A95VF8gk>3(3o$=Ay0J0BJ=#TRb!1YEgzl!`AK;ChzREI07S9T@A@+=Qv^`FrE zsV^R0>Mt}6>=CV3xPL12%>oU^YkwF$5k-d^Ggcb0l&CN@+qo?3-Z8jXmSN*Xw1#G9 z2rXB;iBR*>MxGlu2GYh3wGE5vv_wi?$zx+05UaRNe{%3;*TwG1Gu^&qXi9Q)35&Oa za|G8jpHF$ zB5|7~-2K-lR9~dKj~ColIc`aLVswHo+RMLAb( zz}0_e&n-S=0=9#n*!_oO;L>jRX!lhHxIY%S{@NcGSeGWTXktf&$nr9{;AjPkbcV}H zeW+mZRD6PK76B;qCDksMGl7RrR9#AzJ;de*q{_vh0K7dp>*gpg(hGKRrmwq!;S=UB z_H1SXmqOgF(`CBw_0i|r``uTwmc0tW(v=59V=z|G&9j>~mWL9^^12g|)? zu9@M9(#a(F-aojKnCS)xQTCm68@zLRphGA(b_it0I#85k*wWl0w-ECB66iwJ)Zb=eeKjI_G!JGsejq zztI&IoG6jCTp^&o58NH2^71DcRCJ>+oIVy`ndSv)R-!@lsgKANzeq z_atA^g6yb$-v0^sK>WRd(vhJrF=e+cSH1R28sLLm|ZbX{RspF675nO#}nalz_;_JwX7=?NS$Y1A*%HE&gJ= zHb&L!f9<*u0Lq>&!z18^BS|~5YSMg=zs7X!n_t1$Z*Z{t*hVv8+&vOrETM;;>UD>! z4%m=4y|vc-D{upf?LVCz;)``H1_l@EC;g#~zD!H&?RukSx4+bN!+gG~zJ zd2eGjK*aC)Vq;?$=odG$T*KiGGXkxK-0!K_Qg^8BwXrSm8?W}gWbXm5H>Fvc-wDFE z(sM$+@5sKb^YxCrsUM7G9b;;xJAqN>#Ijo&{var`+qLa#Fe=6`bgIAf!E+ADGltS^ zoVe z`t>jqYyR!>-YszU^;wzi=RC1r)hC$Bht_o+^8H5`9KvFo#3Z4CC#z?6Nu5LFmbbWcG{~ z@iF$qjVAbErky|!|D+!bSZ-(vyc2+*Wmo={IPMA*E5BIhqik^Eld^jk#D=Y^eC4Z$ zeTjb2%-wR6LH3x{loHOly6a{t`Htd2_$|2MuN9SWD zRe}dbJf-Xn)zpN`j&Zb-940h*^(}Ch(os=}R%=y!cGoE^Ivxc&8-`xf7e>{D$UN8W; zwg#`2T(%o=knm?FCN>bKXwOe8vCa~$hc$|Wl3i0M^M=J+D@Ti&Jpm4uhP8l(DV zH62<{?cbqzl8Fjj{cmeI{Lpw$G_NECV{B>RaFMJN@RwWcznW-|v?_O8wTl6W+EZPIXD$Y*+}muhy?O zRAxbcVb_zlcZHzO{7`@Q3tP-ow(Fj*F~zy?Yt`FzZ1LvB#IScQen9CN@G0dp0tKmA z&H=&?u4|4}@jn=h`y>ioqSpIjn&aP}BfE)DwP2Su|I`QA%jZ)U#~smBy`CzdL3r%l zmF181)}iO{jV^@~RG<{hW$f(bgUigsMyK!)xVZkHr488!!?~?Lb*HesYC9F7cAO3I3So5Wm&`lpSc_e-nLq3He=yQx7Nmx&eLvB}P}f zD>$BV3F8Q_!* zV_`O2POiUf4SLz;$qk!5Aood+M(+!vH{Ms0dYUN+nDE!xvvLKjSBn1cR$(xpL3FH6 zvmBh)ygGexH{l{RZ_^H$5Z^Vjpts7w6cUS#uE}o;#z@DRkB7wA_|P?MaK&MJsHD8R zH?u4dL(6}8@O*SfmlJDb(z94_U2g3o6K$e%f9pLq{%0fFJyfzc%(MZ@Ns6@K(=~X& zRPTydk~8v)`zCgb65fpZNOm*PL#6Ed-W<2`1)<1yH$50+9vy~&-k<_jB7)K2`pDb9xbvAcnXT&FS&|&sC|v>P}p3JZ@m+iULc|8I{aYA-+PA z!`pYktW7}c)|VIl#3%9}j`4EAvMa+`kLtaEqFAqB@P0Lvw-;C3{j~*TTbJK1eoi>i zfQ;~xgJzJ{W3Y30AOIRRRk@uf`rS^w>*q7c@94r2epccY14iaw954T9jyr$s8jj&+ zqUDgMqEuxtw%#leFB&C0oN3GfiTlAgZMk>1)ILvS@6X*c%5RMf-NE59Iu(i^m^ia` z5IqelC23lVb#~j9Q0_m_#cyAF4f$mJQD}9@>;5`DFj~4szs%GW<3kU&tc~GodvNFW*WV`%BY`uNSdlf5UTE;}uRw zqmE4;Ze_#V&bmqNZa28;sCwm_sWbM9Pn{H)^~B6)yS=6^(Sg4Xn+oN>oHS&n1= z!ML*gmCTAbKcxJ4k*4%s5D9p&kn-CLT5TtydWkMqm@QFPwaN+9_t#l26AcEM$0Z?N zy&%Hj6lJ)t+kj3Xe(wr45dODh>-X~o0icZz5!U8*crQl!c>NvXr!;g&Rm=uJXfE^J zPzfDg_J2+K{h0Wg{)#KAA8fz`GgXzVCN8-1d)~`8j!Y0&Saaq4Hh=U=^q>ZWlNK$O zvY6;1*>l(N4{eH&MZd4n)~A99momOt@7%06%F9bA`ue-VyAKs^zWOvw-z~Z7@+v0` zF;&m3VZl-X>gvBT~I@{V~Gu%*Fx9&FErr-lUXVUwCbcN35VV zbd+4$(eZ-@1=8AUY)aV}RU61v=xhVe%0T7R_7H6DiRRBH+^EoIr=R6tJs~`|qGpWj z8{2|~f7^&LF#1e&V%0n1>#Y8qam~mIJ^fybx~m7`!{-Yx!x=uXYHrg-xf~jvj5>J7 z+R+wx=RKr*hIFwcVdvk^Tj^NytUFTnhAYxfzb;=zW5ELMEHI_oK#~2Ay1=6htnSiY zN;}I!BmPV0#YN4bZv}UyK{Xp@mjqp)O%Q$F)zas`ZN4xucz9X8=mxkpDJ>Nw9gIeb ze{9Z960WMVzT>W%Di+^(mbZR`9XU_^hvs=i(dyUCcw~PNDlEJCgFWPf@vj|yyEB&o zgXg%Z(v?-9y!-aW?{a{49Q&`ja@nGh9O*=e(u1@-u7CsYnNYmt$d0yo3-EFAEq4#5 zfyjSxuhrQ0*j$}|e8zKB(7 zQtJRjocp74k@#VmlSXOvLD=!~{r5yx2re{_SZ#Iihq)W1wZ$zMM9j~+_H1Lp_n|4h z#eG4b{<*m>F31wHX}9lXJoW{&sahJKuL&nLri35uF$aC!zU*gWgn#NQ;P$gu1fh?s zth`R^!pIkuomrfASf8lyhey{1CJvXRi9L2i%IR(Yy)*w@XE*vK+os10=vhbq`hWF9 zqg;p5Nphd`zt6j`#$^aEU(POFhzrKc16z6a_OX$%I2l=S@w``XVN;esaFrTt9MCa7c zCxjP?U-d1_D$5Moo9SN~?1C|6V18$gPyn8^p#A2W)q|+7+&_ea$eeUV@n6aYaftQL z?{J#8hJ3rqSVucql;du=lp$vd)JPed71>IJJKby7xq@(7@;~X^*}@p6l6~uQuqCFY zXezqKkUib>S9xXK7I?0$#LL&j1jW7to5yS1F|F2e!8pVR?sV$ptsHVEdpbjMfN-Yv zFGhvZ8|YXjX85Pambfw2)T(6?jC>!61{v^}_b;#%Z7 z2>TPmkh^XHc53T5+j;dtj+ z^@W>G<;Q&j^^va5ai!=Q4Jw>Z6v;^tzpAP|i}Nkv5p+I#YNrQ*`|B^sdiI1LVa1K- z84@n_qzPBxY9IJdQ@c4*is<(Bv)!JTrBK|?i?zX!N%XqwGE?Xa8$tqWX4f;YQ>^{{ z!a6oCT76m>LM7Kxw9LAaW1B$k#ZOPi7+)xpty&|MA_r473rnO{QE_t#S7v99KUx?U zOUaUXCQ6bctZEqxI{jk>zAf_sN;db1sLw=Q{KseNa^yLaFA$?{eZ&Ah@ZFQYdNmNw z=JKyqF`{GTpNH2cGRgiiEy?l7ND$kEEu7rT zg!tecCcW&rx>`4mre}ff81?>l-GLP1AJ1GmE|x?Gxm4fx(q6)l=8)O(jh~Ks`4N5x zEy;DI`b}JNDL=^7>b-8cFN^e}w^#2~W<#Df!zMnNfy>=%@4qMhfOe?CdVhN(L1#s{%90;{oKSlI!p+zW`6Duf`G%{8y?fVu~7H@9~tui7;_D4;>a`yF0+N^E?qki z$<}cVP4|ZLi9ha!^94b-^T8Xsk8Gj%Qnj`eR-|+HsF3G<<|6NZ2h8DQe5jdnIs<|VR;eVe3<5c+ z!k7(?nqYU9cmD}H7DO+(5f}9+80~o0y=H0ATXN3(92+3zfH`=1Fk>9*R_&s#(Kh0+x6SQ@reJ?{1F%;11P-h&;9K!;pZ}(jV)VPmw?@EOYn@@cq+|YnN4N zc${;6m7KFHX7i1etE;#}ME<(BODi;?q2caM)|wE^KXWszUQrJ}@eChZryYQo3zS#S zkCSuy%i@LQ@AUBbbfT5j_7M2RegB~O9TrrbeVXDxcyc)n{s9*wP1Liv^DOMTD_)UF zdd$8Sh(~$&)(I-OU}ld>7E8+r=XqGqVxIb7nYdG)!*V~!6i+C(+@%W-Yf{6e!^j-p za$LwKfCV-S&oyc^*vO^ku2WghgzBCZ#~sCqp3gozu|RSze&IZ-Qdt1peKXg8Eb+v6 zr*fsq*{Qmk@3E6b|2&cE@4~dM*@S~?nY_EM2f!BLu>;d3et1YrmEK4=uieYzFVf!m zAZUJ^3Rm+1$zL-w`XE@ttbU>`akLD@k=?K6uhy3IuAYWX*wqUw%}9npeO zruz6$XW7S38svKVm*8FMZVlI7b*rU5V`B7!m0M4pppre|=AhJWCG;`bs2yNVKA-mX zzsdH?(a-wi?NSZ0zsGf`Y#DJzQBRrW+u}Tl4F94@6dOGM$5Z_C3l^E*};f=28FLeyE12x-`Ful{BILv4-JN(`q<4yLO^~!j` ze2DSd73oY&wK=jOO`Yh-A73otG7MAvfd+>RKAw^-xCnf+@aaGHouWay;``IM^ zmgJu8SJj-PTGgx%rV@RG-Wa|su>&-!Dn5%G6^uRXmfOB=QQqgj`^jLE*iV{QF15H1Q; zey-g|I0yar$69D15Uwp)`L2eJX<8=sbO#2A_B2ar5Lz_8)@Jj&UxMghzlS@4lZL@7 z5^A>5sK~cf>4MC2(pwPs(8DcmBi38=^XDD?f1dAje*vEvM3kpFxYrw_9*gJlRU5*E znjfyyq}ZZ-ntij#5%Qd+L=GEX+FvJZ|MqPj8SYdnuS->gCQ z*XB!R^_<&SxHCCa&M$~?#73zPb{hwidFbf+vA0ebPjQaD_}m&b(%oKi$`QV!AlcvQ zl?#UIDq8I8CO+ZzlgEvPO`vV+Y3?nf&0uk3Vq@g6D+nL-?9K3IW7vu4JPsva!aaAn z_KgSQJ^n9}1(HlqTD!C1KG|c?g_fHVM+dHLU7tU9c!NTF`IT65&KQ0AwBD3KMXul> zUh^SG+#stOT(Cez%}Br7aK{(v2l8Zm$F~56+r0Uwx(;U~_9q6$dcl;;r@^h{`4UjP zm)r=XC!sBRI#P)SM%hPGMYV{Z@ce$sw~9cRW-n?Pto6Vq4t-I9XcLH^iV)#@C=Apa zr_IlzCA65l87`0{d-F{F(<1>}(C!0M z^lxRyr3J&fP<8*dZlpFHl~Wi}T=UjQc@Sdu%v%JN$21-k&AGwkgh$tJmk@l|t1uW4 zZjC@*3@lS&hsN=%ciH&UA?M-XQvZ9tIJzz|{L-Qq`t5JnT-ad&HjYDHoUSaS4i`G7 z9x%u5bLq#+LbUPFkLB-$mXm!uR443vhz-zxUjL1L`WP{zpqP3z7>c_-R=j@c4y>Q= zV{?x&@Wi>@YNy(0IO0yZy@t~UW&(HZnmg(UpX0w)&{)LJ=({F6zt$Fo3a525&RasJ z_8YONFTU9Fc6^k#-vu}nXE|2(GB97FkZ!ix4dQ*E`J@Wbjf;={I!pX3#VC~@%Zl6} zanNkK%z=#@RlzAwZQRg(Q2Tu{-4YW&jlUKX^TcmE9}TAH`e57UP3+}f@?g}WVLHv> zf#SPgxi$5WbMWwLuHiGfkZIa-`Lwtj80_G9{?UgDo?j>T?k0NVH@mQm;9su5mD#dA zK7)xjJ9cNb_}Bux__$7#XBiB}q+6|dY>9B9l39Aw7h@B*M`z{|9f5l0%BQZS06`fh z9wb&49`uKM-`{6EIYsza9`R-6PY6d|pE0~}kBN)d)dIv;u^_*w zM4~Cd84DjKZn|Oa2KAyToGv^zD5KK0N=7>fv)^jkzmM~RF?#Im4lM?Zea_%LeTxdz zI8zT7K25Yq(G8#+Cmd8wzk#8WC4ASGU)t$F^gbs$TDO8NtPC!^T>G7d*`<`P9-rM1 zm*s9<_SGBm*Pgq1;_o_`(Yur5e4Fs8AHO9~9}?fO?VU=PGY#lRkIWs6Gk|^R(@%DN zVxXVEQ0pg>BjEf-nT%DVy4?nE?2oRU-(?*P421_@^cyU&JHdu} z#6%B9_AwXZMaZ7lP~hu+LLN0o;v^GvnF!Up>m{Uxp={2zGvxJVfSbK}&Qm-jXS00y ztD9`>Uv{YAl`aj_G}0U|P7z-qY0_t;!~}vL!}q(5Y&5xIY+o_pivt#t4il{;rzE`e z$^8vvZgx%WQ6qdo-haJIgp)`PNHQiC*d&AT3~_@mz6eC!FsOLvWAEp^0A(>tCh4S3;u#@#E!Qf$mUW%Tuz z3)#0+RGX}YX3V^zKJ5Qv+=?@x4e0})nlbZ+hso09z@OS~kNO*xa%?s}K zs1}voW#i**A9^z3jM~1ff0wOwfp0f1nHudOI_;06+`rjY z_|Pl4ag)6>ETP){2!GGU?rko;Z^}r&gz1_1mD6m1Lq7^^l{O>)z-oGxIN=lz_oTe< zQ-+9lS~o9kvPLIrXXaTcbC?h@(@f`50BYrx+XrShV_5XE%A8~tthZLZU+WYCh2vV@ z8(vvJroz(rRh1i|dJm0TZkZLfEh=bmzVJZ2l(f3Q&;*`_b1z6Hu;AAHR;x@;vM-+c z3UebQ$Gi1=iNl#7r2JRbe?-_GPW}{XX?&uGlv~jOe-#x#dl$D{257@`mr?(tB~+Nu zt+~)`8VCdToU2nr*=RH!ANBaW3o6^@2~;I|14XBH@j>7wjOQJC_@rz(_7A@=>T45+ z(BTC(&7A>$s!@MY(-!1zY~F8wh3rd5>bxqG+;BqmnZ8DrA{M?hUau->2alI|Z+FQh zxxa~f__c8(eix}XzVe3XZ|4+6G1d>-wwr8Cxk3DI(}2ATi~6vJy7@M1wOXxgikCm?rPVgmVD$Sffu|pXNq@@s zwDeLxFwEAQ`(+S_tDAk*E-nqkkqG1GtJ-~my32NOi}qT=g}W(G8B!0M6dH!@K=h&l5o)(jDjuds37IAtTwoc0-r+eY*v*5QoUd)`n$y(>DwO#_eG ziqolNKRU=SHrYtHXOI=IyOwSbEvtoR3Ula?_TE**tC9u}uXcYrU*-mbwwhi1bwM~V zS*vQL;E(jf;q=OL9uV7eqJT+y3Mf2_XIrh-pi%HPu^5U2)Yt6kX^;tl>r1%p&z3U) z6g0{Wx0oa6Zk0r@n`GX4wM?zOzz#asaP5Af}xl}H}K3oAo#G_ z50#?29-o~cdQEF%kjEO5SKqp4VoH+i^A|-fr)E1qb8g)7&yU@aV%^nv|GXMlNiB|# z>v@9mQ+LyzDj0~B+`7|6rNi7iXnNfw{>}j96ezI z)%xk?>qt)FO|pmcpE-9N)aWc8ctGYeo7tCH9u(B;Tm0MSUE^ zO0XIgV&!}wdAqql+8~AK#A*+n6Zzz!+UE4!Y~Dr)S=AhQJl+$1V$Wr4HmAYKxev2_ zp+0aSKyk|NuRAIo*NA3Md*Pt=&a(|DMO1dXhJc&-KnkX_s_>J`B&owEc z4D9ERU-2<@HGIf<6Z++RFxo!qjUB6EA=|QJMKd4a1m#mXB*7fxZ7aHR@0wsPtsu^f zh z>nCH-o>l$!BvlT|wul~@s4~QiJ4ZK8^V0Ct@~aC4Bwx$_+${FSJQKAymIw>9`an>u zRn}!m76{dL@u@YiAkZ_=QpKB&p&f}ee~7NZ2-?nRLi!iFu})fW(F#vW?Q(sm?*)U8 z_Vmt^c}S6BeDbY#5cEeb@RaV5f+ZJ5ig{a`kh5{MVTg5Y3 z(0cKW$j%~nq`wP|89e0!jDVjMiE0_7C>}grn$%jCruJR!-BFU?3OSf>``!b7<<2|t z9Hl}KSA)f;f*_Dc)bMj&z8Smrt~k7MlQ&L$ug_4BQNg(LNexnjTRyUR>!XYFG?Xig z>0VC!nRwnk{*P_VkUD*D)^WcH-aH`fz+>hEep?kkM~>5BWG13ei{#z3J1RxQJ#2yE z7#I6=i#WPxcDD;hk-mtD#rCdzHr!ml`_!Yo0T9sgr#1E~6Xkqmm1C9%L1=c>)#FA+ zK=CddF7a7~U~^3{@GRk~Tn_vvRp5!G;@c{l{sbaJ!{~W&3E^UU?mFyyNP5)#1LY^h z17N@BgPy_OVCep0E`Mj81+EV@Rx0`(h+oRJ<`v1_`LLR+Yd7I-<;wK_80#70fd|V{ z9Lt?>Po7EE+1_}&!y+d%qxGCvBucp3y@w$Ig492h8Qs{JG?o=);I zUsmu|c_9IaGem1Qlm4mNuhD&kuf1?&$iZaLjDm?bLN{DA3-kKVF@KIu`sD)bKkQ;7 z?z6}lQWXG!n}^;7Dmx%ty>)uK65w#EfzWwZ8hK~))6-jwJz>mWm1BhL{{qJqOm3WH z0vET)%cZ-m;kQl`*Q))b?`YFs8-Z~de7v~&h`81kluEXn?)_{9BU(otLZ}W%5jB$k zSt){wzmEpIX{Ezyss9+4NUoRhDz8adod%kTo|_{&9WhmC^(Cu04Z^AaQ@)#Sg^!*% zO)OZEeA%2-NNThaQk*U<vM?~bw7kd6-?c)y1wcgr5* zl?DF{<%olboDbKzfnY4USbB=*2Jyc)Q0^CbkX{bzCx<>CSA2S)DO9+D4MBqSpI@8{ z20iTwp+T~*g+0%+$?7CI0|ym_A2sHnC#=TzcFGn)4-MIkmoc!Ta(s%h#}oNal`IwE zv4Qa8#5PLxz>|~G@yCw_!h(zQxwQgJtd{Y%6PndS4N6VwET=D$_i^WLf2IzKZZ6&S zUJRsajoGPjTL5MDt!Vl7<-k9vI$rMYg#!D(7+=W^!n!9SeYcRyew_<2=p!Z9y+5VBx%E|dw9PXa48$NS*6CY`gdxQVWHRGsF1!4V|% zZvCbEZNUdrS*OkSy)oD_X5w5e6+IriuWURKgvK$4#QF|V;lX9SbZ_?%NV!(@*YR!u z_HSBwY2O7oP;F5$+WC(KO}iJTi-(!e8L>%K-^v9f{!(|y_;`_*+B}vB9U?x}=ZTT5 zNE`U>S?u#Ip9y{~@~=M-;kHBO@pghz85lmmc~?=HKBL-ECR(W_`ae z>{we!a)^mNrQwfR*v}ndY-&ULY$mvqL%(i;?xQ#NOcCEUb9q*H8PVgCVp6F=t6A8R za;^Y-Z9(KG%{|(gaD}PS8+Hgfpj$6BG>>fm9b$XJ3yX=y>JA&l)&dhL^Tuilw2k)=1txRQuj_J3|15GT~ig1?6%g+Q*6S-vdW4fyUp9v|=yMo|8+;jX(1=^Jz}H)|z)!w9W&;TWCt-{_W% z*2v?&mR?Pp-5!{hAK740Kn#(Xy+@x9NJ4r`wPB1j6GoOOS8UHF`L##%4O?y-;{GII z8r#DeW7VUp))!l$+{$H1lhZ<2s5oJLDug^2&OL4_F*k?v@89R%H6=X6K42mGW^z?E*(d*d@ZxU|6^xUP#R{(U1kuhPOzJe9!Y5ef#|hced~59)l3bncMe-zcCoYIP0D)F?I&_#<}I* zmjF-7AKAHv%y+_XC3Q9P3D;y{cv5x13F>#*2pyjb0*$^r8I7R`C5 z>B(bsbVm}mHJNu#WCU`b4usQVXSfd)Fp%R#`$CdzFeK+@CLbjFa)L{1e%hr#;JqvP z`iYSfhCbA0@8StYQ$ah!kNPBMmvlmBg0c+gN%^;*kaOVqVJo)aId3eTuUw`0#0ljJ zZn&A$f7@2izz=Vhwo+$&khbGnL!y@_3Qb#W%~c4%w24mFDi;Ps zoNk%+;L?J4`T}=1w+{r%9A9c95{!KV+_$nG(=kfhwD0#S1}3y&~JC{G9$llq?&!HsRcYgl5|Rf^a6}u5?dW! z5`_OfEP4LmzaUI~RKCyb?CS@mPP0d}tH8GkRR zJiqU3upJm&ikqO9Fu_Xq*cHAK1}^+0Z`u*_g3#?DyL`I&FhN@tb``)}{|EO3avo?q1Lpp9fzJJXQ`Z@A?M{9^rBL8FSg0TXyql-Gn z==P`w2g*8CAJUs({&y;<{O>6|Z=QhNeV9;9RX zDUL6P)Pf*S(drDqn6Faaq=@Y1lz%VJS>W$Wu|az>>8RbelP90t z_n%GjJTkYD+||AkS)MQkJp52pd)&?wLpqfPlUn@ogXtBC;yJ>TT@SCY_3=V@{mY+g zuP4qp(bc9Xme{x?L;6f|0OB#N|AakR(DI^FUZd3l*+Qygp_#T|e)jMe`v4a1&pmhC zejpf9C}aJTO>~ry+*zFU!UVV2{f$1R=!)?f-Z`AmCLH z1Et0gK2vMQOI%o$bo2Y z_+7d7`xQw#j@%OWWfzhj*2oWCoaEeyX?v2fA%*Z+DN%lQ#lOs~j}$-R*%5^87awmw zLh=&Y8K+GS&e6fie0b#=Cl`QGccW3_)5_Tn%e_XxLi4OpB@TNGJTbD2ycCu2DVF;D zLpr!IFm?VXVbR$qqO*TraeyCqwe#-pjqq$um(+h;RNTGtTeHBAAY78v z7=1g^2eTWSUR7OU!|g9dQx6x({vr1tCE&h2JTP8Vcm5QJ@dlOVU#rQU#Jk-1Rtw-` zmpwZKPmzAhLt^{J^hr+q^3U6E&KeQEYUnLWFfyYaIADBB(;+(Z71Ue}WN*8xpqR!VKseYmZnl~vP?%y6v&O3AO z%PAgUwd?20T4~bP+1fQXw!#bgZw=^6U$ckXJHGw!Ab!vKxz$Z3Q6X4BdBYT860X2d zC_#(Q3om38DT^F*MXP5cn$rC=ICfyTaB`C5m^5CjOWURgT=k=aEf>77<%4E^)NVF3 z3fws{-tL2~c_+_`lKvODr(d~OM~IP}@4&|ZIl^JiTu<0aag2dA~j^I?;S<$%(` zU>F-XGpY80=vsn8PcC=3BlUi?SLP2n5X-%lD@c69v#J*j-t)R+zR5nn(Q-L5PY=27 zb_gN;cv`GEu^=d|h(@uCY+xjHc8#x;LiNg1nQkhuRCCO>#src+ zwZxZaZddsurBx-TkfjFSsZ0awJye)mFI<0H&>tr9eYZM@ZotLUYB!k~u0Zn&J$rYQ z_z1UMI^UeOf}~jK-b%uGOb!l9s+-X9`%an82JphRz8>1SJS$MPx%|F?(-$7-j~#+@ z0oeKXppgv8<4kkNd1-d{4&>#fjKF+U}Jiz`Iz z*gUzbUkB@#m$7$~Tt@8pZ1#VGRG5icR{s1R9Z=c5fEh0gT%%3f_Q$wjXY&eieyadz zs^YYkEp7J+5^9VO^;B*zp@bku-|JjtRw6(-~&e5Am6@bNZ_w3m`E+B{g^Cg@s zvEx4~9~$_f z2&XK!hlCTooW3|Gl^^mEjO7DI>SK$sew!wxKWS;pPc7j{O zh7GjSy<#_Zg@7EJ|3xRKF!;^hjZ+^qheGe{a?aP@kjhi_NvvNVD3y`z*0De9_J(@R zh%eGHWBzHx-Un>t7n=(c9Jj&DTk6ct2mmhso;&}gxk9J=Cn?vBfuMZQbL?NBJ(kJ- zj$;z8X{3}f=y_ik&ihQP{&9(g2BWP!rH36bdpwI+??(@4{MhP+4 zvGcH-9i)$&DSli-=3c``id+nTkR2{t_Tv@S#v<1uEQSa@I=4XMfU{tI5`0FqmYTpEgeI)#f zp4ZxsqB{sDEWY>SwKLu@QyACuQ_2IB1CqYj{IP{J!%tFmd^T7fV13>+&zj_(jw~52 zQblU>fJp^K8z-Ot{O9(`2NT)3FBhkLkUqt*I*_jo>J#2#;yz?=o897Q=j{*a;YXG7 zYKZ^F*)uuTXpIhI+Ot;u`g1?tY4Yu-(JgWNsJWOM`z;#*d3z#{^LQ|0`t zMfApU5C5$J`vIl4EQZeP@xugC5)sr+hwHv03J>)ikmtvkS$S=rR$DMi&|j)syQ9I@3z||dH;S(b!0n;4&4;g&`8TBe zvVnOJ2<^Jq!8-2`w`(&a_tlX1h#cQp@ceKP#*WzhQ~bIa&wrsjvfn}a3$q@H?(k$l z@j$QmgFSB0;r``(3h}2|i_J&gZuG>6EnXT0KseE3e0_btbYc6XZBCdo>D!!cSP&(1 z7K3uz{n0a1h;Qs&uq-!$g^s&@R<2}z&HvedA|e2;-_P0G?#2Y4=!MtSWWK&WNV)xS z9UC%O{JlF&8MtYs($Kv-RFrtTGeW#c7q!JLZoImxjfF?&xYYK!g3!=e9$rIt$SkkT z?y>O3OiFKkixm^jR6O)_TSoSA^8-rBf#kZKr!3#b*aVbk#!t&-B_QT@^!K$SZ)LKb)o+bf=QOMcifOEl=jA=RuKy(@G>K zHM`-%36kd=uQboyFYAL5ImSFW5)7cPy7sZI!3K;i<^C>D3dRxMv3G8T255d`!`Thb zTyf!2cggu?bE2#M;PBgEkA<)P=*XV-a=aiZT;ueaSU=nK++P4C)dd85z^5!*}Qdc+56GGR&k!}8-JD|_c@aL{e2 zL+&d@F2egc8Kp8p~MpYyrNL8=u`k)j#E&U@n=B% zLeQ*U=n{+{BrX0n-ryJbS{RoHk$i<~p7TK_%AAaHIPuOE#ze~f=KO4+y8Q9Dh6ec_ z4=DURTx*R5fthtO6|V3BqoU5PBRqVB1=n|ye-S!WnjWre1_`!*!o4WuJs$Fcw8yr3 zP@g)n-(s8z`0$R;75%MGl}`(Qv8^?69xCD_7>M z2MB4d87feHFe(g&)L(>15&3Y?uSj~zeLaQ#P- zBKZ_5-Z{E$emt0sB1ReyH8OnQ+@f3aper4}EpL7-NzMz1k>9<)Tpuq)O&N!Xuu(g< zBU(?^7(NdyWHbqo=Y9j_1C7kb2G6vOJIQ z$+lx|IURxUJVw8F$lMVngg)&moecyhJT;(nk#J-G7@FI~iJsnf_QbL#D!dYtEZ)D$ z2Vba0x{F7U9Qo?P))`1vj`Sn%k)BVs{xov8G6 zhaNZsV#xZBIRBGaaW z<=hj1Z9l1|XNbN}DS6Sa*s~HCXM8?C{Y=ga5B~%9-%0P`l%4Mt2WLp##x?umkPU`B zxBHloLB2mNBj2Nh&pTsmY?c#1hZg&I-!8&ERa*-WW-MXiqsA|}c{N1uc;6t#QOhR# zx`tlaQcI*qX)8$Fvc;K9iDIwMOb~kX`>^X6>1THwmJ+rO!b^6CpPX{0qx`c~n-A^_ zhWzlm*YpLYVdV{;FOCPv9A}c;5&K>S4oF=VcrqUVywbTOL28Z4q6JT{`8$zwbp6?1 zZVE8vZLf1mig0H=Kj&Ku?Xddt*+&P6epqTa)S5CCh$0DI;rB_OYvy8<$nZON@Jy`n z(|Qz$6z=tQk1SPD??o!@)}k-$k8BDVC)a1UtJwKsHDBB&O8auCk?4cJR%;wnVq%gb zEpnGO8$TG$^Zlc-!EoEd?RUJWC~x;A!0rrn?ko;NmX$m{S=LF9=>qI31fHucLF5;)lgNH=_W^>6Gmr@FR(EnNT{KR z@LlxcN5_}!Ci7a-;Htg5G?BIIMm01)n5pECb{MbesQOOD3b1s#nO13! zLft0kTUxXcWx3byGM9plFOKc`bN25xN+Co;C_=_JNl11bgtiuyLJ>mR z3*mQu|MC#-{fzT|o!9I6a+COJh(fAo%j91KKw*%jOK>ak(=WUaO4~>rv$As}02A+Y zD%tE`ga@!LSgiK=<%5e(wU~8NznRE?s&Bb(*@CFY*Lz;Hml=F_^4wlkHWt|>?O2P% zkLBp;dh(P3>VsdE-)rdLT_HV(xr1IXqq2!{v!4NF8EJwQmu(TnJNDada)U3u*9MYB zLa{gNUzT>XHi{)Z-5~v+8)SRRFFoMpjB1gqyFL#S-y5{V3k-*e+Y&U7JYSMp z#j%sFv+;}vW)cDD7ErO8KiwB@F6lN*5~QA*GyB_?93xKS<+;1K`Y2!Z!BBMQmp%L` z=GqdW9*7UD6TL4qxgw)@^m62O6AFHw`yIV&5mehPdj7r58h;+R$*R0e@9#ygb({m~ zyt1mVr7hYUc4)htFuTD*r^B_bd?IX=VqbsV7-x=6k|$bQ*&&z|{`6FAiVsS7+_?UozCO8u^cR z92w^Uj5$;MrLm0x=AqTX3+Xu&lJ@V$vn3%onD;i4`?eJ%pS)x;mT8T-b-$lWj51N- zNPSeDP%v@&4IU~9xI(gG7XQ|C9qQL}A6-WIjEO5B)T8aUL3!4F;i!$(P1>MTF?Bum}~YSvn-DBh*-Z)r{VuMi;&^ID%4zR??NXnO5* zF#-qSz!_&j(ieuGzS&=Hj{9r9m;GsGfOtduuamTADBJfjc*JED9vL|FO6e{eYG#eneeP51Z6+G z*Sa_que{$Jbx|u6QZJb3*3%v|PuYRf=Y>BobjEcJUJ++CXSI5>#X5LxHh*Oo&1+>6 zY)#IqUZ5{*q;9>2^qB^S`2PRr_+SC+Lzpe_Uu1}+1u%f4&qYT|LYMUZ?+hW0y8u6=y@?;;?)5e}h4i zb9bT9m0;{&{Og=!1Pge5B`++d+|ShFgv}RT1%dGO!t>`;LecQyCVv+Z6U_P|nY2VD z7|T<;w|A3XSpI(PyG69eg*1=%iE@DmF^UatFQviC{dL4+HR7A6$R|t%n}8cHN1knU zAS^D6;Vg-xefNWTJ`NF%D1UVBfghGMPnGbyy581?lGEdBuU30Qjj_avx*qDS*qX(f zB%ZFE|J!g)yCA%_r?!9Fa}NwJXyeTyeLr1$Tfm+<3>XNxb#o|K6U8bY?6s<8gV^vR ziFvCjk5*PMDD+zq_=BEp3cjX~cf%Hc%?Q?o{+*+P<>J8*S<%(;b1VoO7Y1$JK<6QN zbjz-VlN0X#mwzg+&=ksfWE`Iv*+JrU#YWNf*2HuAvc=lL9keU<9ZajIoIsw_uwq#t zX1scTOr3bg%X>b^w5T(neWQfxTQy%$dtZG*$$;`>ZGY=7b<%q*R(Tg&d_5G|D2UlH zodIm_UiojK3i{b!9^F8>%!xxOQ8|Z#K!1mAUg8EOHe5E)yKid>QfYQ7!CnA=@BiF0 zaK!@)JA?DiMlZtrDF@MM#{jr1ddz=+F`W~FGpANlzN+=Vct^n_#2@XU_46U}&#<-b zTzoTF}aUvb-Ep>wdI#0(dYMik;Pf#^qT;tN zmOG?Ae@XkoT*Zsty%rAGa3+(&Z*9iRw7N4hH7RaPvj8>o8ZlptWQl8)PN8 zUu}uj$NlzisS<+r1Adk96L(l3q~**zebyFq1D%b}HM3#ivcJMgLe7{uE;di@9TV4Q z=sgr-xPal6TN!^wiT^sjf@8rQTkvaS>}pXZ{@b&};fyAIM8DV5LdTb)rR|$TTj}#F zF?qA@Kxzo6KfIf_WPbo^N7x#&wa5>#QdvcQju!m8>74A20Vvcc_~GlPKs0v{o$t&f z-tegnmv!j;xJ~4wbw`~Kc3e`eYk5iiq4EMzp6>x4}7;@ zMf;>H!JH!AIw+DcapBB=Yr)XXc{(o41fD$j_bgi@5Wf!Z*8Vv^1ampXj+(ay5V}F$Ue8%Cs@>nJDpbcVIi~dJ`j?j_X@WZO;Rq(iVSDh)AthKnm zT9NdmyO)$yCxU@)(Jcpwvzh#6W#7*wLO9lL*PA@bLN%^mXSP!R>)nZ3E(=BKhw3xG z@le$aE;Qa*zLI=)uX&}d`YwgQLxV!Tw5PQ95!n@>4|;DWOK7mdVUNCNZ1y((Uo8qUmGWxF|3kH^GbxX{gp z%EbaZ@2it;yUMndyV0NgS1YT^hRJ6ne{AP>I+rx1ZVS4Z9SFLzUfag>1}L<=rKADG&-(udkI5SB2wdNpof`nP}e6 zD|b79p5LMq6`tZDICU~~o5nvclp0&j8DbI)>sYlvQm?wAvx9!}92erRq<&KHJY|S# zAE%GH%6Pz5nFnoi?l>Ss*vxYaR>w5&wa>H_n0Q;V1=?EOaoNP~FAW+YsC~6rqO9E+ z;>U8jjcHFiq7m7&AX5peb{beLqkYeSeHI2?qxbdAC0~zt*a0-LUvB-#i+O)HKJ8gg z9KEBQf^Iy)01_jTTJMZ{>OaqKd*%Up9eN!D-80~KeFWi#Pd+T9;kluPix za`sqfH}R?UOf3!4h_AIhV(f~I9o#jtlwO`gd!>ZrB6ClDv}a3iF5PE`c<1W-7KK&7 z-r!^)UgZV~CEt3^)sx=WuxDUP3;DV>@M(o7+5*QO{evJ80+ltX_XQbrJ(;kp3#wVz zxvBiV$7c&zXt!yX$89Dmsdr$)IqG5A#rf~(mKefF)=vl>xIWguDId%N3-RWJK|t@Jid8a zkLFYBZ_6|(zp)|8<1_h9-?g63+?PncsNV++MEaD7@z5&vXdwfd6{nYtC#{DR-GnIF z0Ux-(_@$r@}(a_S?_5xTM?ZHwHked0ykR{$}uW~^3yDZAT?S9I6`vZM0LHj&zw9ms^3p`BYoG2H#=X<<; z8*!`e3zlB+^Fooou|hWnXs?sS^PS_667@QMzNX0+1ZS=WRv2;z;IVnimX-F-c)<5~ zjaWbNPc`^6-%%cYhR^zk*LqEesf&+Z>7<6kF@XacRoK{TmciFOr~sKmoxXFb6d+GH zNNun^2w=kmb6eDEEDk$&%_f))cLg19w2SNF`Lux9y$0m3d~xIZ&F|zxo6;i70UJzT zTbS|B*r9b_!Uvh}ArNrwjOUj9ln2YYRCSknq-Ot`CMfSDy@W^Oy>$-*drfa?jB_r5 z{tdF;YIBLpdVJ4-sy4mPtD89-X@At4DBJk3PK12BKZeg{m{Kpx=DwO1Mfg*F(QEq| zS9<@>KB}Aa#1_xTF4ddJcW}S9WM7;W^2eAiSh6?}=47vGky#apQTaO+`Fwn#Q+$>9 zVNrM7n#<{+b&h<5FD0#8D3@289&)5RJQ$Z*jy_EI&j)n_eSb>olRs0hZ|?JNRO|U~ zxAtl`SCkTd>w3R}`YmO*7WPYfz&6iE`k&J1`>pUlt4w~Hhn3sL4dJa38teUo;IVz) zw$xQ@6fUZ4=3GrZdCh6ut7eJw{4T_lpK?z<7QNT2pU`={M&MoTpem}lW?%JV(DUs~ zEbGH*Iba-bpD`@9!bkCzKkx2gV+lXclBhS-gQ3&ant6!{QKkRtR?zjzF&kQV{L4RM zsqy7|?^O6e;RnuDjSNxZEA+4ACoWybT9q~(ZsLtB`MXS&-xuwpgfDIcf8a0R-JZ!6 zitS4#0vT(GKdiPod3!<#*gfK$$KCHq{dPtZyg&4TcYkpE!%Hlz9%ffxpxjjd^!j50 zU#(#Vp4<&P$by|OKZ-pXqaJsW$e?;-YqZ~Ux+so%GV-e$R&ToP4Fk6e${9!Wv3yw& zcme|w%^T_z7MNjrY`DftF5*TUQs}d%?>BdU_lv9dJ@Dq1A@u7Gg654$Ri#s2P?&1G z<+P7G<%AZ?8lg9&Tc1z9VMu+I!neAvJfr?7Vc7-7*NJ=DSRQ@p3iUMXm?LJpAsFSC zzC2wvN_pk_+n?Uk{m-8#UfFIJigt(Ir2o_*Km62^h`l>W@ONge)m&>`@aBNIAt)LQJ_S+P)1Ld z(LXIvfAjY9dP`;WvS7_q(lG(9v|Yt-$S;<0{mkp<)@IOZo%JjW9B`oG-tm7HRw&_j zQBP(E?Z0f?)VWnOuw>xF^-ILHcvSiBtiq-cj6We=CFE!iOT~Ugx@&}@?7}MrGXGpb z&?bOgE znirxDxMz{yNVZ^?x*8)CGIrLMy-oAMn;*0V0-gs$x?OXF;${FwsgWsvaR8KUR}qn@ zqP?)R+KbOQ^jyQyXV^~febd#P_3|7LD*kHKv4|kxD9OCHUum=|`DNgXLzT;6;DGnb zmWM3R=hj%%kibSp^j43G32Ts8qgn31Rvj+oxPS0`&H_F5?4}oo++k+nrER8vyr5U& z+0+hq8JL_rlz8M96S?#ETRkPe18?}*!q}^9h;sEQ-}li9LGozpojvQYDZX|8^oT1e zop)CFjr|S(L+?D0nfT#piVATE51yPO@|@1!%9#a3t{+3x3pF#X1slW4u4%^{C?WzrI z89Tr*CYX`G>zBIzym$7&u;4;`!(95l&agMm1L6KXbp z#P=zT^2mF)AH23L1ZQQ!4KAfpAML=tGq0xU=WpE-TD4l8%KDt0+AO@Vb76|$Vh@`8 zg>B@O2Npw7^|E57n+^m|4~%zS_ikj=4vggHbuWvh_sb#P&qs?bAk%K!s%76DfpOma zZJ@3*@dTUvFR$@{hjKGrZ`P8(y~4@dM_(U$&3a?ATn#{vRa{=2N%QRKZ*TeMJL2z& z)ZoNP(t(zie%t%m7Z#No{!QZug5*8#QofUaSiN$z_&?GW%6R6#KhgvkIrTpJ_HjSF z(wqILb%PZo7ls=j&|HK)+764__XR@o3it6gwjo9qxUH+UWP{sp=RK}}O|hlA)l3^>wro#ZON%M0zu%iaXc+_vlRjJf#vDnt`M+AmCotjqhEKw z^)&u3KfJkX5YcX3p(` zHADXU?6kFZl-Th9vpOKqQx*x?&agJE7-pOfSbTBr5lAq{_8SQmxC5@ICBJ{I$P|zJ!2aO^n zWmp)f)wsxG1@U`rBC=Rh!Dv_=Q1PsUa_f7SR3`njfU>6>3RRbSLxSV|jj!ixA>)iw zyWO}AvPYlplRrp4u^)40tQ5T=z$ogCyqqIWY<1(EOaE`#6)Wq?GyUNSyW`+CsZdB4 z3$6O2;|?y$yHq&~>~WA!>*IQFXQ)%~$y`O}g4Zkm)#}H1qV=i7@O%FSz?6dgNVFI6 z1P9B1gue2}WzBNO&t_8&b=}Of`<+hk&+|}H$u;sN_daVh*y%=vY^g7;W`hv?kB@%7 zxfHH!c|A%@I?Q_&nLn<(9uG^OK1sPcWMtR-L~itlls*thTS9!Jo#mQ)DR(aNnB!X( z=?o$pPn4RwX`_n%LHi@eXfC+S(|hQr25O$WI^>t{kJsxlb2<4gx?osCK#BV8b~N$- z$_|0*u8ki@Cw-6|A=tXB(H&%EKOPTRy#Q;hzV?kD^@V}`319!t^@B6}onMw(*A>v(n2#4`fC6CR~dXg@(L{iSOiN^GisJ?c3%B zb1JAJb6PyeYxbf1N)F<@Pmdb6VnegSlx3wsxMd?0!PCqV$R~bCE%s_~2*>h2m6$Apg z#Yb|_u_1i7Zu?h$O^|xVbLC2cDO~Y-p3t3U0NJ~OKVM66qW&3H-mv>>ED~D2@cIDt z0ViJADS3|c#6CC6)Ti!fIMn6O+#L*J=Qq76iey3mGT(avBQ`XDZ0cT~XNsYvLI2{d zf??IgClS0{!PvB5pTUL?tzoI`F317y*(HXt2(Dw)AM8d-HhN0X;(aw%RjMA)CGGz%DlyzEnv^UFQEZFUz8s_ z8JIM{MyQHf!O`H1rLV7UU=S}LD9*i6vH)T5d$rn(XH264A09DimN9qVZSa_uCdb5~6 z;B65*j!i1~PIoV7YEuX*t-B#obHN78cLy!j)$)Vi^~!enRz9KKWsAX z+_;~9?z=#fG1)=NVcmfHt1J za*)i=V}l=GMNHf%^<@eDUU-r?2Bk&N9k_}7K&m!dYCsk8He@Ybo9T+Bd*sz`pLfKw zZ8{sCP+w89Z6)%Q-m@V=o z-uo$akAivL@I%Za$Lwk_dR*-=)Epx{EKKoIT!lN16F^sG>&>cUOl z&6F>ROZv2HfX>6<(jIg{A7(0L-G6BV++%%me)x!uyBrQ4f4Ra1eXE|E{ygW3+>V1q z6$|{Jc>9~MbkD)E15t(0!SJJiOP{~a%==4u+yc25=UdtBN6dHs8_kHW#GG>%_ zej-0ALjIlJopRhq2Srdu$HYu|gnFP9m)`KXNxqc$dofC#)cdV(9>yC0;L#8gGNl;` zZ_PXM`kkn6Gd({oYLHpC$hwL(^gVMn4cROFb51?S+A}{Hp}peDKWU9FlLz z_ucjP;5za>idbd>g<#?gPog?bF0MopuE|ezV!mco7j3p?~(ip_CDfA>F}0@slp8qlNJ*vmyJ6HC|N42P!{wFb zZRdSpiSO;^?}oQb{H3Up_f0wg?ah{SFkReWrcX|8d#n)zUfFEu{*3tAKee}ai@1U9 zL{MjXLonDk@2!cZIp}8`Yxr1I2!apybWXh?mi!#pwdbfM$Y^bQvhBGG$bh)W;=4?Y zS@M+cnW`#|%u|^9NqNxxR%ZW;D0k}5Y5$#Tp+wx-ionxf<={hLb-;1r`RGb54LzYm z^Y7=ap_jA_srT>R%}4u~pz=&Lx9xKX%r&&B5q$-WbD zZ<>}PGunK&!7XW6daGvVD}OpaS}Qfa<7Hu!)L8Lypx%*H_Zn67zTt^0$;kFsF- z<(01+;wksI)k5G}lM_t#y;-N)9*imF`$PYo^?@BdRwY)O{ZObp(7m<70p?1zmi@Zz zi4yyN|GnpH0h$VXa#t*KMK8i{-MC>!T&+39=W~4_QtjBP=WB_N`(<)^?h_Bn*_mIQ z_(pn0bAu_bJMlysy#zZr$^Shir7YFA9(wfu#;HDI;hRaphrh@_1uNCR2y0kC%+GB% z7Z+%vuHo->Arh2768890BSn6?f0+$4T=pQ?wkOZ=fiLFWwlCSi@$%h%h zTP8H1gVyS`N@d-&7h9n4+_aqyy$)eQov)?wOhkQ(-Z3W1N;N){7W2p6;H=X4Nb=2u za~xbXZVQE|2d2*zx-GExl5o>NMj&*SG`)9^ zGzN(a3yKxbsAK5|pdpQgS+mn}v0gr)ouzhC~xRpSpv zRejwRf9QK@;gwC8ATH96v>fRvJ2-h@dU|Dw3o5;47Y!b^gS4toOT|;YQP}xFxao89 z10V2^SAh8_)|_R~nBodetw&Av?x+24B-<-7a|L{}RexGLO>&yk||IL`I!H0P?;2$XJPOaRbKTpO2OtrT4}0vmT#Q zs83U4CewxH)I$6NmU4`pPwVBXiD0f8HV5ToltJQLaj&-f*IIcTBVsr7GLZ(Cw= z!&~pC3#W{^Z7yuIs0c#gF=pLKUKc3)`uy0<-Tq)3XPvP6XE2m}dCO1>r|)s#>Y=!_ zwd9vf{H$DHjl6vE9&KkSuV>Jko%GiTjLy!ElnlEe^UTgI%{u-NuA8g1Y&r<4R(tF_ z_lo>k>fQ$pPgw)|mdo19-lRj0EfY0~rhj(=&yV}3+`+@6bG@9fA7V4Vl)khPIR8l) zt?Ht@$!Mqk0sT;PjCXyd@Se^&`(JtROB!MLu+HHPjseKz6kt3L5{J&Q^pAzUws>18 zDq|1L%?cyWta^)surT0*=&L8oXzy^s$ft`qT+jKq7hPu|&vo}dnL9$T#%FJlqPQgt zPV&lb`RodXFBZ2;=V~Fxw`UP2o^=}UJkl*>xg{7a{XQ#3iTIUSnJ}y05dSpX8^W*M{FdaZgNkWUHpNF3|$~zSowjf0sVc`lE3^F4N#3Ug;O=x5oTTq#XiadXs(BorLyu6AG_OZ zQzi8m77y^KAN7PsP4~E;EDOQ`8Q6ZjYAwiLYM!hiF0Js#0h!Q6q|5x8Dx1!=1>s|j z5AJZeWBHv!sa38HIQ%QZ$w-y_oo9bNv#`~|fdqk;Xu9sR|8>9IvLOUQWnPKDy3PRZ zFC5>sZqhkg>;45}%^>8D>x8|WW{|Y?g!k5Mp6GON`PRjE$@ix?_{&$(%q@zBIfl#N{L(Ii>5T!HkX7yIN&i1wbt*Aog&e(qyO*3)pq}Xo)&`GldY zVShkjG4(?OIny%qZTo|TOtUHEal4{H+e(H#)ZLG z9}-7_qiSy+*kWhb#^;Bvv{5#D$-prcIrw`t_S%pb_0XO=>f|oTM6Lp7$K{%&3kKfR zJx@B?e*Hkr^^6c$D;<1cp)0-D9{X`7X1iixVPSE`*7fj7b0Z^XJ_Bdx*c&f@Z-HXo zA;(?iozU>-Mg81|wC}4p*L6#hbm-I=F(-tl0Y1lkmH0lEqujJaZw$mKHMfN(c z7WD`7@ICevQiWE9i@}>1epulIwHo4d?#ZaT)%~0Dzd|QsCFVJS-t_|^z6$hw&m6tE zr-P6BJ7Wv@Q-VoPiZlODz#EwoUPF)nc|)1i+5OyqZJ@f--~M7}2%Pz}XL-@*0L+Wr zVi-Fbh~n37<_ZPTKIXmD6SZ|A7{pvHGy9LuuRFhZw{7}V#s6vP#hQF=NO)SkY59O5 z>89P=u+kInD(pP1{ZAkIFYUCjZ+8WG<=L#xL)5S7J?X*lG=R>l<7xTJ&Ee7)UA{j~ zq|5(l-&FrK2x3wME!GI>66a)8&HtPO#JpT#|47*jPwqEA{~{v2`~~Sde{(cgcA(2AG|Ey6a9@5N1Yrp2!r} zrFqY+azm^y_8#?U6j&N?Ic?}1qJBD-s+ zH*u^MZd@a-=mh0$Ib1bJ{4Ke>h5k}O$Uu!rhvVz9_v={WZH~22CTZDdU8o38EOoC$ zzIVnGu}3`iksl|w;W4LOID_&bQBUX0RfP5JqRZs|h5&Zx^eeiC;JnilhMETM_*ZcM z1P9v=uP@)sG0hW-MMGWI_7}+~bxGR4tIik=Gac)126}*}Sj>a=bHvww(_}kNe%Y%N zqlpuGv)-rf^r&f3-g5eq^;@fUq-9#CIPWYy=|t`KN*d}KWQAOwHK>G}CybB6T}lUrQk zLgB!pv5+X1GgeRPXzg8OMm<7txs@v<(XuGy$ER;h%46QHz5Uq;qH=;Pf7KEPIeO6c zAwwVJ113I1R8X&M?B|YgSslpZIBs^@hzZh?cL%2iLu#_4QuS_t?z8@xSgRYS5FZF7T?v z0d!j?>SFIvPt{2s>5b&45%W1U;Bt!kU3O)2N+|=fV~;9bJ;ws;{AHP~mO=Rb=Fzs> z9&$&^>GJmYxpv|sYLVm+nZ|=O$vqk^o zmr&Hy8|!WuUpq^4Qn+8yn)p|aAk%WT%|s-W`T`dwN}Nzbj%I&>%r8a8SDf8XKR)3L zV$Ib&M_B|P`^e(Kk4pfZNKeO++gxhsl;hjZ|rsb!yB1nfo1L05w#-Z zzwccAWOr^5=|CbS+*wvw`B8|!dNc?Fuls81FJR(ZlW|R@g_L6$IAyqZ2_Gh;2>s!r zTs8+QYw3P&bx7oGiaSnvEHlASZP#xf09#S%c~4XT8(n=@E?$bAJf^(|^44PESc0Mn zV*yAVN4cZv8q z@s2zGy0I{iR~3F948RP5UyEc$n0QwrD@d8naem94>VJM`f#l{-hlbAtQJ-_2Xf&5M ztg|?_*;<+UyOxUH{=1(1GlI@*x3v19+5Z`SSQrdMgfO;V|bIVM5Lgj(t zW2v4@^r#D0amyzTCtr`d!~-ih;w{_3+eZ7qE~$8@@4Apuqg`y(&j!QwOV!)0E#SP4 z{*P)c;$ZimxxU6O1Psn?9~6`c#=z$Rp-1T)l(&9wO?IglWDfR7b{vs~?8_=o3%+_l zL(1Be@p~pvdEsDDqb*%WiXB#FFL-cJV95)SW6qe6Ex+*XMs?ER^$u%pr@cF-U!B`1 zPB}J-@+Dj9jhhRkMvmJ1!_peblt2$J{AxT$f$2~AIIcG*SIAL+&n5x(nRzr9T1XFL zs5`hnVQAKd5vN--Ww8yf8Q$GL$kG90RMIRB zEG3`B!*exX^kh9@vw9Mbp8=g)E%H(dnd>oX&f2-tTb!wnFnU4H-VkI9)%5drdB9TF zah1s;CLTO@&444o4*%-n?ss&*c^$6W>2_Ze%lpKacLaq%|CU1muM-0xfB%=w+cYe& zVQK%pYoCZ$8GB{N_sgPKp;vrwKrNU!qviq|*HV6aXk>>X@e{a=7w0Ou%hr@tU1G#d=1dS?^Wbxpn$ixzkdN=lwC8buNf8yp8L*RykkuFU3so_hZW$!;maelT-FV&2q>0-Nlwa3!uI5d5g~X{2XKR0Jl-(me_D07R1_safLyswVMRUjg z&hP`caOClY!C>MIxyl~vr}xE|UovOYMe)m9KA5WRJlbC_jG+}(Z`pA%N*--m{0eK z!K+ss-PC{GBq*b-X9OJ4O2vP4_|RTEX_XqXAlXpAX3r$$%ZwUx{02O+)c#^c!#MeL zExQW|b2(!23aPbRTUl5ZcQ9aWjxtXCV2u_CdZBfD&!Z>Q zOTNDSPE-LspCkT*mQDY;p|SS!hby=FptkGWmkQdn7k9pFa-q%>lz#V%1678Ka(Yp_cRF`k|1JZt?vdlfGB|_!7l;ZWyE5pH%z61;8ia z=}u`K@YjuT|L?6ca$InGoiN8ABX7GMc`6@@HGDDq^S_$FHZPa)$n8P6Fi3J^i&r2{ zJZ(;tOE*Q2zUR@(fv(sQuV&7$^u%*{;naP~MA1Evyk?T-&GB>JTR;1w3OE<+Zlde$ z&4;idP9rG4G!+;$%7z9t4Uw!>j>rM$Z8wJ8sM-{EF!KBq`SWa>|0({ad{Mwa;e!1v z=v_IUYx;i<&hLuXxBskw*EQ>7cL%s3hggKx+`;Oqo0H1dvsV(YIA-pS#sds6ttX2}m;|H%hZS^4?tAG2)x6uZW*{ss`aj`dM)>Xw^z3c$J z2hK+GZTAF@DL>N}%f_mDHQk2Cb-dyAX;rTy|7ai5Y9oHwk@U#{tM3*P8rYQY%#4|` z#rC)Q&i{6sLFzui@FQ9@=kQ8u+CMNuoSAca0p&3=j}Kl;y=DgtgICAXN`fHgkV4JT z^DcN(ra?_CnZCbgrIjn)w5Si@lFkQn18=p!#ZgOlI#X;$y=DOmZ>DM>1TH(*JQu2|N zMza>05C^x2>k=2~W69U`hnu?p8oxBD$~-_^)8>oCI`VCDsBRoy%-cr&k>mQC=PacA z;q;yX17UqkU~Df|RxtvFv$kClhsb}lL$+Z2ksb)?+*dyL))53tVlM}_kZi+8<^Dvmu8oU$3Cvx0@1;>r;v6XWo`( zqDVYB?P)JLJ~p~s8lBY4A)esyR}Rg~!621-o&R+P^?Idlw9=-%b;I8eFAH}1!HUYg zO^hx-loyKAiAko?^(?}o`=T5#WIUw{c-$;v5;vNE6^D8U8N zw^-C0bxm7hDdnR#av!`^U;xEwN^z#2gCJb5du*K>6YSgMB;-Ub@ydtXwhNzJAYgXS zzdV{BM8?(Y2KK5#`^&Tovuroe_Y^%l=tcb+Th|9aqrF5~>+nL3S0PZORI)aRa`Jck z72ahxY5~7lxW7Z|THI<-d-POK2v*3uWGo|p)x%{=X1#svfp>An*FW@J^y(|{ZnN{m zn5iJ`5*|0G9JxBgAI-p#^0`SGRu1^S^QEx66$@K;F1g@(AqeISDKOj=|Igc;WzB6R zJ!L#&$Zyq36nX!Vm2*l3Hz#~;^r~WG*^?W#e8~aOd^qT}1V0N>{zkv>AU(!QCiS@> z`H&#B;ER#<3S`ge$$hcL1v}4dTjM(I40sYHrmxNiX4s;r)8`a`gK4rg@~{C!ec!z* z%-kKW92}cjUrgto?l=RhaC;~VEV8vZ$b{94f8CP#%*2NdC;F3KIYGZd^n(#&ALO?7 z^K|F&hREQ>Ew8niNCl#LDSLIWgee@Cn`jNUW|MEH$9Z8-I%n+DC||TZSY`X5GyoN| z9vEICpU&XF)did{tl(B?j6lM556r&uV)QqsDfvqN2*>=MHm)4uJ)A?_C06rS%fGAu z+Bou_+~*@&7G7W*tu`(U%vyc$lQ6${a=p=@m~raoID1QT%^n;Kyz--^C(vX8&BvM zYpmUN&YkrB)>nV5*1<>Pdm&3Ee9?_t_G`u-JHT$03vntwaJXxw&r6y!!?|Q$w~&qn zg9j`YvzCB;@0P3gNl^>k2P)9;pd)j4~5 zFRoL6f8S|2rmHn-%t@hs>racErbAqzBz%}lrIY+wb)n6(d~SHR=AR{hw;o6onlIyG zIH4@x+->|!P2_m4-~YbtO_k)1U0*NlcET%9^mjV+tCPQbkLoT{5ePSUTYk}06MbIY zc;7ck9RGce`Oc+!u*kHu;*L--=wF=cxkWk%_cC45ioQ@E$i4C-)nlF@du8MBB|~wj zd!f)@b>9hV8p0TdiKo@7;w`qRJ_y+7t985nurTFl$!z_TAh>o*eQSk!C{`y6Uiqv? z{I$VZQ_Uy_oZ*T2e)0c$Tk5EA;9PTz=oT@av(yXg$_0hyC{X@z#9k}1kNBwjwA4p$ zdE>jnconp=PNE@Lws@TJ7Ql+8a4eghblIb+wi= zk4@P4^2gs7d*69tcGQeq`Fqm!=4qRMB|SASs?VfoAsYmYo;$Bx7=SK!_P2!jFyYLE z_`TDOw&-$p>_FKn2b8$H{M?G!ATX2L;kMh%A4^VMIO`MOg&7_FNm);bXaBWn(qJN0z{(Hq}>}KKt#jeQd?j&E}mg4=)DgSaBgf|X8^>C|8lS(fa=T`T1tBgLGp)oYdl-DbO`^dMQZ+Ld zKJ+DSg=FNtV=`9gW^|#`tjGae8XTf0COlB;v%38Z(*`Z~U5j=(O1ivsvS0k1+)zb|jpaj9R0n>hM#at^ju?sUPqh+H+}Mf zexBAfp&yuNZx@|#o#6;-$KHp$D+z>#`_e}*KDR)|SpB1`3s|6iy{u0`-4DtS=?`@n zvB3O>zkmzF6u#--czb4m4S0*gJ2Z~?;-=8%Q*84smoozwm;zdWE8u~tdI2tp~6SqqETCSM45R$P`vw+ z)=i-ROp%#UU8!zHeHF_DG>#i$^Vd}(g<9@3XV|Y4S`>ih3ch{4-hqHKIUXkr{UP~% z*14|LOQE@}V5Nn7D{Pr!V+L^>h}ZU2b(KD@?6W)1W8%xN$5 z@4kIOch_3F(MR#XDuq!&Xxw@Gj}*t;YWVHsJ!z*aS{MC+a3At`+*ThuJJ5` ziPq_MlAGw~4sdU{??FC``1qW9$(;d^Ec$WsgZOIb-C)QuuUHa#d3)>MyjTc@A!pO9 z?k~i+2YTMm^L?@Z=%$?CMV829&Nnd0UyDVKFI>!GOkr<-$GKl&#NGFN{oiLnU2Hzi zFMIT>2Ixe6_L3TBV3FjssjP)7g#RlZ;r6scvBFJe=~aIC;WuN=N8J$8_g&gL+FkLI zQP5!)Hv>8qoU^xmqVrFkQQ$b`Ubn7hKg_xtjFJ)B?$`B$(8KqA>TGWawg+>TZIoib zBZu2p>WMEGv!iPF4OwIApDP*W=kbI4i8{6OjuU_6n@nnfIUB>pAD0CS8W3N+VBaX8 zCnj&|85K%Xg3NZF=bc>i{#2{B)cs67a9SUFoHh|(y<|ZhPqrCYo3D7dn=N%uI~K- za7iH5`cXau+iV5b&p2X1 zOV9eTfVDP$`TPEfAktbcpvIeNn1qJ0hz z4!U8-Z-IFeZy3O{x9>%npBfCXo+QNaxq@o5LECZ#XW*-I`IA#baSC#ezMeOx@26Gy zSAg~!z>?mE)(onkX2YV;sw`;q)+np-y4 zzvUST4h9|VgPWy?xG13&c-)}P03~-+>aV;_JoUV^`6oRLaEG0NBHvCw(%!Lxg|3j>bXp}h!v|hZjrac0 z2*jNHgN%>l>s9qFNhvySh7&(daSXI*PN3PcwZCBv_GUC);$J3$;-8k?EnQ3;m>e74 z;XTBik-1bKTaFu&efRy@u0ns zLCHP41`b+tH(=m;eex}=xU#Cw8b>coXc@FI@!G@P!O`p2pqj9h*IVji3;Qk}5y`N? zmZc%--IrFwAK!!r5;Ui66K}6BOkjdm;^~y#X3oeOT*$xJ)fWC75-79|41ig&?)Q_N zZBR4pAn(mK(!1z=XmN5Sy|~-9w$aBbczJ#9___5gJRh#TVms-Jw%cmz&dBA0s&lp3 zsd;q%mj2jQTfYuQf2<9g9V!EpXI9J*THyeES&N#svxsxyD5zAD>JPe~=Lc5!TJJz6c(Wr*^8be(3d4;EF9tZDy>TV8W)7&I%{YL{S@@=KqZwma}kY1G^?@Wy0lKRf>@&_!0p$|JM#C*YVZpRnBY)8zT7Mi-P z%>@~sah5F2-*n$9$Is=Mp=58w=G5af-#Pc4dAp6C^T1=4c?T9__SqJ}r+aO2`1i_N zF>G@**{*eO@f~|?&N(Le$&UJsuIocF4=Ep2y34COH3;%9PtQxD+#S_{_UXAEX7E_^ z_jYbD@iats_a84M9^`KkKcV7v@SrcCsjkKk7q#4N)mcdXjHhuv7Y`Xi;w{TVT1uW+ zr@d!l%Su-)obSozZT5z;D_=w(bWpDP^xE(1C>M9>#c#T{ifqhiFO5{3(8e`|)l2#e zTrl)sypsd%tBhtV4a~47U45_V*9UhDaMaH6tZkAmOy55)ae#8GAp6q+QFlFfb-A8r zwI1z5!vAhaccOWKf9A}c$txfv;7_O8lrEa|rQe#jIS3fOtAi|z0%0KDap9Fr%E8{? zIspn)&q?2m)}CvFCNf>^Lo_cKj+-+uLV3up-`;IiCjYWfW?;;4tq&+}n=K-5NV?ON zHJ(5C*l>@pSNppf2Lx3A*cla2-#M>M@2ob>6(fp;%*}lf;;yE;Z7{^Hzxy+T13l2& zddRlx2=#TY@e8gLXk)53QbjUci+mvr+m((8~22^j3ZJ93}h(kl3*Z&&Z zV}Ik?Ky7{xsv8upSd*V%c3_QZ=L0tCPv*T<`)2~1^&b3MVZgD*{}*Q&C;+y*w@ zc-Yyv&>O>J_MOps!@$vZ5s&xltAe@UqvJk{X#RA-_3)eO;YPVRE(zl_Cogx?3A*-- zjr~mzMSeZ=N5jze8%_B^`0?S6Nt*xB<;78gb?-jDSx+*BzeK@yBXu& zD_PjoW72 z{tbFV=);QM^-qY~)H^zy&ld=qb4N5@ZnekB|Hf_$wtL{xM1h6nV{E{q9ewdt;#hQ@ zvFp4n2S8$F^xqX~2)k|yr^N-rU(xF)vb%`S{)4;ZF4rEmDXn;QXz^T4ag5pr!pG>_DGTJ-pvBlOUjD6=CNj!k=YdAz3k@?)LWGaqYQ zz>}Vqr}cmCT9tJ5B`1h5dHI6>us13X>YY-}C7t21t5;QQHK94V{N$Y*_P|IzG^=oq z8(3c~=-G5_J;ur$GkQ=O03pBK3-~lB=lHzJ4YN0vP-u7T@Hn3siYBe8ns6n($(ZCFLA%NFU<{iX8mK{B3@Lo{>Plc%72Y|&qB*}W>Efwb8t;-s}<<2koF&-{emms zGvPj}J5D%%K7ZRcnEdo{LL5*e{Yasj#IbtH8S~iROn>lOBg3@kV2CN{NL^b-UXk;KL6FNYbl zUpd%w<-QgQIUFzBQmudzJz?Bq<(BBoacldlN%<^}H$Oe#a-lg@=gsQHPH264g{&N! z;PM%pOj2su5PWaot9}N}d7{0|R(v!^+BwI#uVYZ&P{$nG#3q@lo9oTv1B3X z4q;myi{eC$BeLQNQso$jC00+4Si zQBwEH9lGuf%&({rOk8=Y)*i!%F{P*pkq7dng z`YeC1pijTZPhpjwnwtWa8;NpRV+MbH~X$FJR_v zOKc02c~zxI^C)MtCrfnI;hL-VA>{+6_>C+s98#azNc@aJJQE%3pk} zo4@m=7bLSArW#dcpd>CnY4air7++m9e1$mC$zgnp&(eKRuwr}gEM_1aef;Cv!D0G& z=@)}uKbCoPg{WMYoXR7OOWAqM*3S9?X`Aji`ts6qJ7g3V|8y` z6ycB3bqb{Xb&+Fv>e+$BtJyj82zLpnH=MPe`y>F5j#TO-H3h+qZ62$ed$^dPSU#iB z&w{?s^$g8w@{u1b{u-)e1U@!nP2o*6e-C(cdBHRK_oa%psmmKt?~!^Ntf{7%h_*8hr~J-GLm1m9=boeWuIZ{0^^obcjw7K$Spi_ z_{P&fFj3*Qcgt|m^3tjWclI(c<8^&qch(x{mVBMFyVxCAb<0$%H#^~e^F{Zr*#_hB z(jCw2%Y1O_^O;MZD*52JbVPjiGHVDM^1CA*8$2qpnh?_5XL>oxq@OPKbvyZdTMdk0SNBe?s^4Ol(wTn$bWtooYsSet!iYis@ z+@cPhm6{e$v*^$3!9^gX1htaJ}@l&)AzT#!#+L_zju;slu>y;YZLK0E(+hZ z_Bzf+`HRu3ZI-$L5BsyiS$i{Jk*~A%vKUs|xv0D!k;OdWIe)))+e6^0y+b|3KeGI~ zAfnib=C)7kME?Ka!jL?Et#x&3VH&^y*-8$mj zZ~3}ujRzdA9c?<`8-Vb#;mgQl(tYIo+L*e@2p5M&?aqqy!W-eYEgd3=qc`uBqC!Og z@LTXLe!rLQ_d=CTo^)=#s<3{2-6oK@sZ375D@TxX*u{9VoAP7EU#+;q_MtlEc6mS* z=@1HDPk(8l+}ec&W{tt5gS!z|mqv4}unRx+Z*R7M)JvwbJ!F{>d3^I%b2$z|{@rsC zPHgCXm1%K^xcPU>Qj7kL`Jmc^W}gPqnKEj_6YSkdkJ4+=s2)T9gjsuK8w4fcZKHXT zQmYf*-uLUV-51I!&5T+xr^*7HnKFKX!!lsFc2--P1l@n^xnF@4gOcfT5g_;JGSq zoceft?5`X7V0E?o8dwb2`piXa(;MP{jE-81eDOn>KS#K&&MG+YJoDGQ!ycgEw=4ZW zu^<##+GS4~B8+bzI}+{YiIxwA+K*S;!JCwT9W#Ag@%W?{iJ@u&{U3%tAk$CA&Y*y3h$1wmQpn(|%T={=pR~(qDTg zI9)9l{(tUd+e@7nR1c=4udQSUfQAaIDTw%@ilEZb&#(nallRQxAv@?zx;cDZp5`rg z6b9cY`NA9_tw+9bZ17pM_2?FxAdFc(bL9z|^R>h;Vl3q7K_E|9ua|#+{*;ZY-gLh^<{k)t_K9l*yZJz7p&)D4U79ofoDQ0? z+z~R`n&Qkpy)E2F_fm6PQn)6Rm4vmzmJ%hLtYcDzL zJJb?qtvGS$g=qkKvkG>}JvJkKK=_t3RR1Z9%gaAlM)yRr<;Q_^Luk|cGp%p!2ep1> zKlEDz;IuR4Oggxt)fT%mCG@#?-V5#Y(;qVB?XmxGfO3es9gZ|4i)w?Bpxv~l?|Otc z#$AVHj37|^)><_S29~SL;p0*sh^G5v(^S0GH>EifRc6a3r z4mh7Y|D0WCf&*Q`zl!3_AakwAv-7-;FqQc#m477%3*v$_le;KaAnAemv6djHX4f00 z9<{=u_5-C_>s^ss@M&OFjE&;YORj%)2n31tY~kp|Cb&-X`+vQr#4jC7p7r;SrQ1f8Xsbhj<2WO z;KD@X5<6(Fw0S8av>u#wZV$}dX$vR3i$Zc-4{pX8Z=98I*+8!92q_^FY_I{Fl{{#yb+5xc2&L}7; z!>Gu;4sW@&P+iN~BUivdg~Q>eT!^DETKy%=PtkK=zq^=3n4F7 zZ{&-j`)FRzfQ!Em6dJ!=!OWD#7QvzSGpz%#`^F;C?O&~6R91CE^D$3gn7_LsaFlXW zLwPHBx0CNOIbh%HuWRw!&T3})X%-mSG^lj9nS<3-_{)nIym3cjwO3S*JBCI|kEIs7 zVgE=+_=8X;jK+<3$k5!NccIww9gbFT!9e`Ujjs%h68<*4VO)vk>+6QU(%!l3z=54D zI%@&s8x8s%+vBm&F&n9GzOoAGK`S1Klx~^spv0LXKpG!V+lcufw zOwuO|Jb2t%`_&S!-F|$j|Emck-rmMI8R>_G>9-Eeiv`e>{H-V3%Yo@r<&DMTLD2QM zDp?|&>gwRRdjq@I;*v+N@*gkv!vwzCksk818zpQ>bZ{bGQ~J#aRpOYa9wmwSUdlVW zZXa3kko-6S-ye(_nh|I2@}9+otFifWV%Mc~@^{YS3_ZL;bKn4bIkCCg(Eo8r=1hDb zls$iXrnPM~Uee&xKU_(Dj8V1FPcsqN@<{%a_Ei^*4eIA?b|Jql4?E*WBKf(#o16-W z@rCSPe3zGd$zbwj&@|et2g_?8Y{vk~*R1o{W415|T}ODX{C-Ar;fWaw#IKOQeZP2z zW(fz4_ln7BGTgBDxo}?RU-AQUJ=Ze+(EgY4Bz-ZulAnxuVwIgKY`I-zB1F1rR^<1F zP&Fywi_C93vx19BkL!(Ic9VXw^5_k%4l69o@O2q1vBZwnc!PtDx-=)YxO4uz97I_! z!tT?SnD5O^57_Sj-WqQEuF(Fd<=j_#e270Oye4+Oj~;e=WE41h=@BnD${c|`SS zmi}e!75-4rsg-wX4Hvc=g)TAkr2SUHrN+^@RE$!7I>W0Wj<~jZbZXv!9JH2vCcJ5EWw`*%_^xT zK@*hgBkNxEdWAO1*zP}}DCPxr8$ENM+~%OTjGFVIBY_|ja9X6vMjv{TO6!Xk*y7eb zLX76AAXIk0`fODS=_cxw*8Eu!2>TkAhpO))oks7B69ODhnD=RWPNYN-UgJHoCHbHm zNJ>p_T*~ha%W^v}=0EX3o^4}ULs@<>jMw9J579hfV(1%dpJy|u@ zfuxK7YB?mXgUMW9=BK!c#*IDdLMf!*(I3%yk!HLWdhBj)i=G(_Cx6N{7B%{zul3W8 z%n%ol*XY^i(#^yZafv&Z*aiW<*>vHhtsdpG_DofiZ&pS-V%9HyRY+8vyXepX4ww%v zvbLo@x83*z^Jos`#i`C3H>j3F0r$gJ;zeBK+5TT*UXcg}{+w7^|C91K-<}I;t+s^B z(~AwZwiEB=qxQp?dOvtPpIvY5Oq|gV1(#NN+Y;x(d2ekWar|HYm!eVW3*L)wy}a<+ zf$}hYY}_wdgQ!62!u%{JD60(9eG*7qcCltTd(tWJjBJ>lm)YNl!IfJ}#l(QQ?e(^a zTn^5e8vVWKizBlBxXP)AIl!;UbTmcyS_T&EZvaa z&@Swaa}+IREU)2!{Z02TnT?d+?$;wXyV?RO8r4}EeAdvF8?xreXWDDmFB^W{M|wW6x* zYeRb9^Ru2DpR|Gbrp9}v{DVP5qQYNiX8?v=%IZwEuf&qZTW?bgX&%nr zwnBNYDpntl+u5;=`kh1SE-!SwL2j)|%!Vt(=jQ#;^P^QC%30?31}R^5X?WvS^GpWj zmsfgd{j&gum{dx&HXF+7XL{|qPdQSLnu-C>3R(l|e zLBAh5@9TMGqM{DzF?>yDsJ`cW+FF+F=>wBne@UEPK>n=sWjBu{QT~a#tEcO0ZJc#I z_-4Kf7bZLZ?K$A*j)`ISUKLaY!Fk)-Wy3`tC~3ZIPPC~zB!|w*^*K&?*qr=lTPVl1 z;zeIfaKAa~FLMho`Ai%LF$bPKnOq24ns@%*AnkMKc8Vvg^M%z#l5)%%>W52y8tR|$ z1l~e()=M`Iy!JcZoU6h{bZ$E&?N0vrMJ5KjmRk|$N8I$=ml^2Zax)tOt2A5)E!(`;J)TW^?5@7+kGDvHgQb4W z@+XwjP!aN`P|6=~Utc1sGlL7o)vF^P*)vf8{z87fbuU6GFwnxYVbFW>I2VYp8A#u2fPf`ITDX){8Nv9&IX2wY~fNzdD=bTRlpt5ZI{tu4MkPx!w zyr7*8HZT)(RSL#P3U5evoZ}5a?);oRf2iYLLJ-pUI(HiznF1sv2_tyA9 zyM9w%;?Z)pt%xAMdETxk|IO?+2E`pCX>W6ELDx~$EJ@f8e5FH^a53dTYlwf{An5{r zN>Ap_ruVL6n|EW#Hcc$|`}*%a&FeLVBWT2Hi)OZ8vNneWVEnEhZd%Ko5$>=5dU`o= z{bB8c>8Cb;;x5v~>m)E=UE)XfLm$`^!7X>PAs)!zCBF`JGO=Q^Y^8Rq8+^UKPp!7E*iM=e}&>WG=vZ^{D`<&=$$>iEO?%3euV z(mmgVAme;?5O%m`MY>;I1zY?0WyYvawt92y+Px1f9Cg2_A1R`aua7>N$m#XMHM{os zgg9`qGCI28umpp+Ji`U$jZ(PNTjkkSnh!^=44GAWkb{xLYDgYCsOCf%Ggl2Pa?j{LkDlY zivP3kiXHKNjWTN6g0L|Dci7QnH|P*7{ik%-24(i>WW6&|L8q%7ANL4QuDgQ1xw{_& zq{STdYxshZ^{k@jYNP=ao-cJ^KO&vt(86}kRt*G|6UL{R9O#}Z_bFgmg8s>KOrc@= zUggSl8y=g3^WG4Z{7MH<#PT2Y@2H;L`29otWFRhBs8IQv3~V%W=RD&CI~w~K!f1H z^@YBeHzE4Oc1ti$doPn%sPBYRGhUcY%%<<-$g9^f$!4fma!PRHEnEE6dn4`GaSryH z+P(iH&jh#r=VDpJorzUmUaNc62Kek7N5X3~X@6=usZ__%^d1s8*8I`~oL&9(2Yb2jNcJ*9>PUOaAa#>cAR+GZLQTT?%M>KDjc*&KXSRU9a<6N_+s`nu@v=0g!Md zLPu|&Ey`rwzFwB%NPgwm=R3;QVatQh!3)3ILq_4cr?sox!Fo-aj!T~##78H!*A~z| zlRYo-_~;T4D*AoIUC#h_UwmRXamfy4LdWHABoRl|p{gMM3>Sj?xX-1$XzoxKn5=Zc z8g4u}VVSj>3lb)?`u(?2-8T1lj=eAa9-ozZoJIq1!1HM9lN)vzQnlVynd<-1`%ji% zJm?7q-N7#VXs)-pD6L*Gh=V*Zi(iT(qcMKBE~PW z$~v84ikqbqW6Z$eHtA!DZ#_XS?wvxGWH4;HlR)!eHfHv27IWX`0?p+)ci+z@9#!(E zPu5ZlFi+Usv77YoleH(5e`*Jz#GTevP3{c1G)FxBr4@16^``;_DuZy`i|={%Y1-E} zO8)qzrHt-@@z?UVvQYB<*itbuI;RUhGw04_1ONSu_5<$D#9ib0?z)Zgr?ek0?!Uu? zmbr#I#;0gLSMk*)td?@Dl>fW(xt8**_gy?;L3M1m)o`l1d@u|vd9+`mpW}SujACb! zCtTivn+B;)H&nen^d^l<=jp^XyC!8&sJQ*UV=n-??V4BcyiXXLfuLu8b7C>-LWP_QM|Y&2M;MQqtME=Vg3wTd|;< zoF~mO<>w#r$#w(9p{RuXaeaswkgxA=ae=$5j>Ybv`KZLHxW5qi|)JH0AM#aEi4I zDMrfi=#?YN!I)hcbL`J;Z&dvD>5yBf2}~~YoNf>bgi$d?R+zQ{G&rx0Q$_Oi)>zl& zog?1fE}Z}wb8A@UUV5rDgM6h6cDjg!1j3?J5A(p4o~ZbtF8nz9Kj6D*NST$~&5<)_3E`L8Qs1 zg_UE@Sjgj`|3uFNG(bLoiD__WJAmEpj`uXdRV4gYtfUc3CGqjEk%V~ zN&l4pyVAoLWjep^*={KXvtFg1xu0ndLT1y0p$bgUmEL>(%yVC;K4jXv-*Gjd;2q(W zt4YWC^I_8yapJkidbS7Y+km$nrzbxy7&?4Q=ao?%%VJhtQ;Asyp)K~gk0}2=q?|9h z_NyBhJ-x)&t?ds_w07{8#JgdpP^5_WA<|8r^&?*_cQ1j>->&sP)p3Pych!gA^*CX2 zuKzj}%B5|y&U-aM`*T5ELuQ3TFdjbh;N+cYnorCOZfoGP!Ma^0*RGOYB3sS>>?46S z*lH457-wROj8AR{I-1O}&En3-lM;ea@1;n2+8#T2gZ8 zZ60wSJ<=Dvy6J<4Nnz9P=($JU&%ge>u|!qv-p;4P0l1`l$$wE>Ju$&k@PwDP71o9t zh%?b1Y*lV;jF`uQCjK;GrGy~NFdxzlm+=Pol9aH9%WPnKA!ab5F7=*?R zU*0Va0GYENG-uDShhE1$*@{LwP!e5c=6n~Z{~5on=uP_VKC7h9)i#(cP!gEjg)2NSDawdum6|da{Z+a7NkA*J80>Q^-s(<2guRh z^NQTPXe%>_TI#RnQfQ89`tD=gRl%65GWgJnIJNp(^TR9Yd&%Wh*6bg1$7czw$@2Jh z;4HjIe8|t<`^ISg2OTZY72oox zWft}EK9yO%eB-7^-fewuefIylcbq&K;}7a=2O)Mm7Z{Ek6aA@Qno#>!`oxz7fnN{X zsfq?r9?-nez&Cb~fBxIHOV3!)R;tj&7$vTLl)=GlHtF0>T=vyyWkU8tEj#%TRV-ip z$+RY%4LppWmn%MWGoAimiae3ffd?;4Ws>s!F~Bb5I; zd^t0JhleH1zv`t|eTj)ic)4n1(i^JJ_q;NEPWd4UviBAe$H=<#!-C%-R5w4H_|x&; z1Jab7R~_{U2FH`DcOMB0f@?MB_QeR=<{vP z-lc*d`S8Hvxm1_4hZ44Hn-LF=M?Q3Ig%R?EH7L&1lEU-*LV5P}d*h#7Q-xV&K~S~o z%^%@Q9E?42t4`#kALOfCC`_(3hvwpJ$x~F9dhYEg9f%2r@z<8NEq}PM;P@Q9{$V;t z+IBw`T}Sh;5WQD7X|61{{Yq#v^%2RZHnfQRHp4Vo(~VW~Y}_2Pbz-tK2xbU``>Gz| z;uq-`_02mO7-96`}3Om+MI-wZe8cIdOTTRZQR57G1nd&mc|t|u60JEE6fi`Cc)6w@pprW8y8q= zJF5pyt3%@JUiWbp7j~^)@~J(6-uLM}oTbGpF(ZDJLaDh83~g^o+~!REJL>E@>PNok z2=g1|F0@z5pKa|{qlc9VQXlgK1Ch1XXv6szrXYIp2#Nk^j~H}>^&!R-y#prpS4cC! zS2Wnl@(%fJEq{3aJZ^>0>dl0e#e>15HR(=y@c(lszD<3+OgiXQXS*uj%L9+PdsEiG zr>1ZA7wTa3z-ThYMqg)LW>?!Ffth|id!MMBMfcz8zwWkM65!*ck& zg@*$5W#{Y#{w>A+7SiQ9xI=Zfy|{?|YHZ!++UU~GMvt;DOB1=l_-TLeOwJ58)*dPN z@q+ptJTdb|SdS%U7QAh@ zTj_b3HrN{9NBJWU3kDQ@EwLvk@p5h@3$iU*6!cg5!00!#VQ-`Lz_*7t@~I~m%1-jW z+JDdtS(lgCNVqtHZ~ptO0yNLD`M~CO5kIZ=OZA`fpFyx$`pX+W%JbFuZ%X(+?GqID zC*9>MUV}1)vy2&!{o&eIKmGm-HV~`2gZJ0QV3^ltv#~>%jltcvi+kn-g0t2KsSEWw zAd>1B`J3w8{NK+xWnH9~y0B%15^-jEWkKvoF6nh09NIOG5`QZGM7DrB2XpiGKDQq6 z23Gql^S_~<5Vh^D$KHEBfJ&d9En{&=FVh1fq?7-WLmA-o`90nm6(vuSUvawJPcS@) za`uAeJ+h!MZ(!B%n2n1nqa$XtQy$r}OH=JH zn3$XKFGur@Ih}{qS9E&pV0c5z;qgbbuP}W(K0RiK74dI$@0e0uY%)mid|NpIY9 zr_ckYGv%TsQi4EoR`vEplR-GznUO3r;g0^x#YS$@`z$p5bv|E~Dhxk6cr^7e15%BP zy=*IJjx+Xc_WlhHz{3^Nt_&P7WyO_cKZ~Ne)pK>Qs#*{VB~%BzPP2hZ+Z#Kx9Nke( zwXA)3$PEh@?7scrQV^cFi_9A8uO-4%qwb7(W7W$GkLqHG@A-Z1MYY-9AUs=P<_yw9 z$X6+eemz0Cj-~%bljBK89_0Hr(uDGDU38*8h7eyzrKrZ*#1q@@Ka)p3nk();u(o`{ z0Xn3FjwQd8qrI-D$hHn!e0^zMeNp5(tUZ_^tZL~1lU$J#8*WivUpsT3tGFxrlDhJUJ%DVuwk8_za38AztS^<^2*~rCAcR~yCKY~djwzB!JK4Y&m-03FOb%G z7M3~}+k>@)qBGg3+x_|eli60VwW{xa(+KTJnrB&F-WG)V*Y4Cd7gLT(guCl4>eGkU z+&MUH9Srpb;>8>OFu~b0%GPlq>CnCj)u)lKVY2GE9Q!2``ch4fr=`>L{N4SUSh5ds zZb~11Aw6HA=6cOtGdL(QQNGzy(iBSng|=$Ay5mpjTZ>BNNEfV`&{RP@vybtfDP09# zSTz3m`xn|@OH@D16U(L?#NoMWaX+X(_{6hfat8~4SD#E9n@_&z#XoL(r0M`eQgJXA z=swdqxnWlr7xE@~ZYl^+z1|=f^xzSjyar(jF#>^@kTi7xkl@z8mf8fG1qA5<+?=X8AN_;QRx}uE&n#c~Rx@Hp@gtvxN z7Jt;FJ*HAytN-=&V0XOFaqT(sci7v?tlzB#0;vZ_-^A0t+xNk<(sU+f>>aL8h$Q~~ z_D<6`j)6GXVxjzH7d;0*OzU$yWs#3NofsDF56wcshi8+&FtB#aPEwo!3!1(#&;a??*QR2I0aye~TlqyF2s?n|E$2;B^R-!_9jcV8G& z_ohE=Hd!tEGmC?g@xf){s~GTL{)zfo@* z9O_f>^iE^or&Eg}Dt`H6?4xk4yh`Hj%AUDsy_@nY9!NgkPI|}muwrXi9gLZ}i)u{+ zC|7EOgxh6(eH1r1UR(IV9;aJk!RRLor{|BI9%v-q#fvBLW53OSF%&*#tV5jIm8t*c zdbk2(L)EUm7H-hZJ>Bx*k_&d!7TT<(T%baYh?v}OfVzv-W?MdG!kzbv{*6%m^l6K% ze<0~EM`hYMwng$Ns&@nzUh#s|ce&q3r8d|n+mbK&^ZV~d zhjno2x{R2_*KWjX{F4wh#szBu_kIg!cTDEMxTX6_6Meurwxi_5uaAEW_%}u=Cj#rGE+KNW0`=4FZUnmY&y=> z6cyM(@t`t~(put^zG%I{Z6x1lH+*V|_JU-)1xaIZV~sL<v> zmZi(qzWIC`B>@fFw!NJ zuFetZ;DFGp&9OtTv@q|j)XXgrr0@J9`zflFi7{t_UVbEQxS7SFBU2fqXa6@*v{1qe zOx>(xPmXvYgxj>3?$d+VKUpPFX5?pY74;Pov%_BZV z@WIZi_@Jx5yiiW!iF*1a+8eYgj6Jt90tWMz!ni6E%8WyOww+~x%y)HtSWkPlET`jP zq^Eg(peCT&#~dB~mps=|VWRg<1*@Va4=}9h;Qu$Daw|*J1Us6oaPoQYtKpaCXy5tk z-!x_g;<0Ze#ct|JP~Ihy0owD^6}E-ISWql6w*YbdSjJAH05hHU`g| zA*uqwpniPM_ku1i{`qI~!@1rTt<9c=OlT4>JFJM0D-r~^&wUyQNG83_e8cr;K8T>v z(ar~dDv00nG(6(gcj6dVUlpm}0qrO*W&R=0)D;yTrEbb>?X{f32JjWU) z-t*&)ymyeeVxJ#*H<4ba*WkMhXTB$dG(DFyqP;DDQ0h@zaYsC?-zz8|9So`kJK7Q% zE~uHPK3PZgWb;imql)|guZte;RwRDpWXIEnOKj3V-+jB#YS;pKUNP-kmwad(-&OJ7 zsb}seQ5bub!PUjgg*iHSBM9 zMV1P0q=M`^NO}{JDMk5)qAC6A0~H?7yt-S(*p74xLv@_v0mMl#irJpIg!FY0hxDV& z?LaeDqb;vQ9(msOO9#z*+E}vZhH84R2lQ?px$sR^g!0kPne8~{03pqGIwg0=57_)M9`iUFwdQE&XlcFHHcKEha9?oIxeO)Hg( zT(Ec5XNiN~9pKnUee>9CcO0ENf5T}@WvJ%PR5LfwL|*9|v6hq*;Y?-I=QqAkIC*-R zaJ?)(Y*~C&&c+upa#|FJMxam$#iygeK~(I-=htF zZynNQ9LfK*aB}!Ws}+{V_V45~cEY?HN@5YZ98j`&=(PVm@vpbdHtU`n1gcj)?l>V# zzV6_1ty?w$Se6+La-U4Gr6Mv-kM8LuM}yv*6%hC2fqSp*5iYE&nRCpR@+7X^5T4i8 z;|XAYc4m?~3)gZM+!;pV7c3JOZ?yLS{$kU@lABbghd!KAQ`Lu6pWlhfR?^%%X5+Jr zBvIJ8zb)$0JZFq;o16RjsWlqD`<*8qz{CLwN7jxQBUJAG%{6ARFzCvYbJ8C?F++0O z6*lFDEtgYmUE@enFZT_e-kXC^GO^TJ=(HsbZbOeri2LAAm55`TSFuqj;#PVY@kM{e z8LCd|P=7q!*X8Nqi7c_pCZjskUkv|pH*pn1p~%(+x@)QbU{zdg&2j{B=fs}xmiDL# zh9x`5$B}ql$;gO!zN#mCPjXgz;H7|YmD>0ISXS9F`TK%9Oc1bCJ&yK0Uf+fac>PfI z))FsY@*Rvs#=fqj-@8}-5_y=s_v0!mbdluU?2usDXG`^oCet7uM+rZWU432b~TUYFWJSE?czPImg>=1n< zz3`m@u+lf|zpz{ibZ_U*;hbiI!j2o!lNz4rSWx|Oxw``%b~8MgRYLqm;kAzsIl92Z zint~Tepej*`!f2Il`-Zke|Fln!4~Y-`wj}~P(S|St**i=(revnbTNIeg*@WVhhrVO zO%p28J{|~n1m29Nx6GTJXs#7c{hP#lyIkPEx>&aBVo&6`>-;*Z zO9Put4Zj)0QGMh#`TTdV5*ifSv>T>+WBtk>yB>j7$=VC}NUh{5H zVNwT|7eucVK5LKb8$C9*Pk3SJ*VJz@TtB?M;M$un(Z00rc+4BBZi3cNrr!A^dO_?_ z<@pBmbB)x_HhWxNhddI23BiMbDB~5b_m;g3(59}(ndZ^!gpV&|8&f@-T;(3(=Z})2 zi|}wTZ-rOo@~|1^`TtiyDjCpdXVYq!F!h{0LNRrJu73q z;LAvastM_-x7w`Ni6y>&cZFNOaf25~J{G7gJmH4fCfjyZzx9P@y$3Bi^Er^baq#j) zm?;*xTWi}W1VPyHtCofvY$-1#|9l+f#;-85zt|MY!0snYVrSAmOY^&3)qs={6rOm* zSfnY9*2~H~*MD9E`3BMB$p_3ZZ|<$Av| z-(U5e)7a&V15!oJ%Ny1Ke>ZUsX4*i;mlsuw22H^HYInxPHkywuRBa7arJTe1g?!TY zIFOP3>+ka+KOEE8aK=!?5tC2W8r;35j++;rGn)R%#@L}>l4EIZ;9D4%v~hw3V|(65 z>bp}NzI*118-iTWzkH_WM~4o2quuH-9^%#KxXkX?_JD{H=}sG(lW*%@%#0?^_EP;P zkvElDz~dh^R{w9pl-2S!r|5_=2LF|}sD43y_%E50oC4bO&$9OT9p?e=S-1WOr;<-r zBk@7Gi4F3;&Ce_kpgEo9MytwE3#ih)ur7?a3%;ZO;{VeQ2J7EOe?HONc=+eXdISl|M@qPN|UU95PHy~70?!-D5TGDqu5-iMNUFzJ2jnVuS!435 z;UW!L9x%#KaZi`>0q1W9{v8tDaOjj@6(+E-T=ls0{A}`d=&UkK+h>5Wb5?v#q`GLG zjIzvGDJITm*8b)0_s1_Kk$nrvXZ>f_#^{gR{ov0*v2AJe`$X*A=p<@D=Vaw&<(&&H z@cgk-eK~vD6C9m1teJ0)!bSsOj#0FK*EubwR~Up9^RA8mLrX~IoBu}joge>4v8|bqw^VX zNVh53VRG31^*S^vPL(ssG6Qbuw?b};2NqTR^+?_Aj=gJDJJ^rbfz;t8@9Un?IncPW zB0|6cB*uqIo>MkSV<7~qMi#%%cK|=u)UAFvv?4gBQ<6k8gcbVZ9 z&Z%BrCKLb1)|rMw`Tp;J>|>XZHA8l??*{Lgv9Dv_8H}Z;?K$u|4?CFcf=S5bg zdiuywn(LMbm*e`o|-#T#%8Gc zoJ(V|1)c||n#ri_!RJ+70RQXPZpgA>MkKZw?-`flS~Br-x)?{Bm8`>a|AVgC>#Kr+ z9ymjMlB0{v?@9hOX!i$Rr=J0yTOv@C^6|eV5h1`3=%RRA*g}b!?lVO$M_>(0>&gTN zfPYBzZOXA2knOBkO~?Bi?|`Id%7^hBzsp8Iq16{2ZvX5wD&d3n2n8s4zxF{nj_=;+ zP5WRjYM{)SE=#of`oG)U(kzfd&#wpR7dSqH>QTj>a#6N9IYzxTfs#Xfqe zf28b4+y{9nO`XZ|1$}4b{OwVZ;IodVE^z_R6+Tq|_@?fR?*q{yqnql8r8PKwhf4{S z)Tzes=~HputNHN-QAbppw=vlGv>Qs2Gfmu_761~r#jYB5yMv+0sS5uq3>2?p>+_+; z1f;94nVk}6K&X+*gWT{CG$2b~(FriYdw$hDr4d-ibO354Ad+4U<%#yLYhu-&Q_kG$nMXzcV>36AY0h@a^(Z|k-CzVl8K7KI^=?8PfB%?Hdqr7e)Mq9m_MDS}ZBEYz zXR+R%HCZlQl1hfMR-b!E9I)=MkzY^FCIDI8Z{xl7H4Kf9q|X;U^aTmEj&v`12QW@+ z-5kgaL_H=yC(%n9>@GK!dfm=IglgYc{r@Q7`25W+8?2w`cK)zGA%+St>atT+SQvHh zwT$EH^2B-LN;9$82U>selLcpj9_B5QzTVmt0LyQ-j=h&5gH4C9?u0MybFNcgj2`fZ z7uwDxeZLuCeAeaE5g}J#*TnC2;!{Th4mpK7B>}M6biO%G!5j9z`Q+L87}xQUYi!4_ znIMmur4s(ren_L#AnQ+`C)C!ONWa3Ig)bzD&+4@yXxHe18UxQy%5FKUp0o3U1M`Cm z88}yqP+fd*-#>knwW{LGub_xxFE%PY%J4*?aw{jh9tOk3-F8v@FL3US;nDSiiPk{Z zskeDot&7Ai5h^;|t$=)mw0+Gt2Dm&QcFV*40paW0z**{0TN3-wgX;6x$K*bri5i&L z7s|4HW*LAy%1ra_-3US?1*U)f7wjiG`+%N8#ymu=G3N^!p{URCXCHae8HT#fS$XP( zqOB7T4&hWrx5h?GFAzP@xI_4tiDT}N6{8jE z$NSqh^zP9?+7u0@TNE8vF5-TIrdHiCOhSCmIj0=`d=aP7=?VTrVK}$9tugMdHlp9n zqjY6Oz^_s=*}xUg_qfW%Ggkc&*M$$`XR*&JaW6gZ6eSd-=b!({OtM5hixJdkTQHYO zgRAsYGv?LkMC#MF+kyVF(cR=kOGrGDHC1bwU&| zE=bF5uX2PCkqxO!dAKilyN#8z{ZE_ieiai_Cm-Yz$6s}Jgn_1a7ns)ZMj>0?yT*5L zeIRtnUq-qi5QR7HWD4W^{GXUbf$~dS_e%@6>#7)|mszc(qq11{`ab!WO`i!o8RXsX zYZ{Cm?TZ<^c?aw4Ud4MCo8aebpLs=5i&)z;NBH$q@Ixz^38p_|?c02quv?isd4Zk3A7LI5;H}S7xo)_XUtGJy$j%KGnESK5B1P#BNeCO~gB}#ow^CL4{3cYGIy2mhsr21R? z#4!?q)GSezx*xxahwt=0@bHGGK@!_kjPQv0D40e%QZUNusslj_qYue_9ej2U@Yld4 zxb=+}G=C<~_`Su_8oR=+2fo;$&Gh)){1_pUHfB4sOEm!17xyU_TH=;5`H;}DH-;Fk zYWzSx!W+WA({+kh3e6}+H1wP%(AC-)w ziuM|xEEf+!hFnF*JvQNpi8$$h`%hDlx%RPJ>7v%CEO$d?`)D}q%IlGrk_kb{<2T@#7{vW_QHJ);9_9IE)_lRY60Y9R5@KP2KK2t6o63vu~gO zWkW=I=+SrU)mlgzrtE#gsr5gL>q<^NUhRL2s$6&62MF-Z)KyCbWf`p5|9(UQHT{e( ztL$j4u1j0O7)#hmMQUpi;isL~>IkNaw!J(drKX zwr+oi-6aJe_-r~6;_$_m&>q7p)H-b&f1Ru_){CVA?x!B>U8bN-Is;dN@hB|RxA|l` zj_T;s88(0U4qNeeh`!bJz@I}`fBk|eUS0pbaQ^5hUNHzxlsK&jpxU*sciqr*0+;0n zciwKnD=RZjuTnQn_{IE3_@#ppVlQ5|_3iUUD5vN_{v9JQr~NE9R>s$*|JeIAOJ3-z zJez#vML246es4^3vO^Uutu0*j-|`rdh)Nm z3I_Iw8>jy=w&YGr3EcXCt&Q!`Z@wF2spX@>^GA!YRPGsl?5+-u@L_!yH*dtzMmM_S zvZnQ+e8F%1dHq1-HRd?Aq7{gW#2@`PwQLQu)MKF!_Tv%wrS~?P*t+Lbc8o)1%n5aL zipJ}<;rk@5>~_`E4cqkx&7J+#FhXzd_4}v(cl}Z%O=p{Ml=l$l`7#K>rQwN{SKn~! zaOCZIB3_vj9{;i1+vbWM*^`^1(xs%CjA>LAT#y!^`QkDOXX~;$b!$5h> zX}OJ9dX;#)AT<^#&$E11=J6}I#(KBBO#rY4%mQ}4R|c~1y`P3E7-9YSW5hvEFVH%1 zGfwp=6FT3Wl}4>hH2Y+Emluxy)BoB3RpuB4HQy_;61r;ycD5eGTN;r-+PnCodT=9R z{+h>Oei+peD{$}yuMu?bIqKPK;}2{(tG4Q&%GhG`LDhSZ1X4#ATJKw4t zTEamuG-Rhty10RXB<>xoQOdOkcB7N*l!#Dq;xp!xUqV&LBn}M#CJ@0aNA9?b zJ*fIOY~B4e1et30-8dc)i2^I$F63nf0l6YUFcVu7QmjbSJNKOs@i`@p^Gz6Pt#iAw z;iE5Pt+}}Au6P)%-Tk&p8$Vw)2i4U>YVr9xVDcln$^tQV)r*f|NoS8!yngD66Ws16 zzokcep%$-_n}1#hBXY~>!6vf+co8qd^lo%Ptjz4)2hWK?Ns@uynNb?bSKrW)=ZK{O zD@iCfnTaavBA>_d*&_{ZsrXP56Nat1?ms;0j&k**76J<~DtgPlRJBb}AXVwpFe-+S(=ZC#edyBoChr1^l+hP9N zLk^GR4DCN0+vf}-$qgDB+H~~BBO!O24MwNGy#BFqof90Bca-~G8I5eNHyICbMS{sQ zWzka(O!QFVaqJ&_|0s{NJFL;crFopQ{H7hU&`dV`^WP~7e%{yrYNz$X)w^_wWAXrTxFJl{nQditUattl}NuOKLE-uGB?!VFLs{ix%q zb*MUlTSfR4jy{P=|59k~fk>P;n_W!wfk!*!N31?x?G;{mCsDZA);#}UTf=$WqV>ld zpRDi(mdk&E;`0=gL-pUsw?6`YCJ+7iT!|yY?gfk!FX3n}p^IW?^X$R5MeLiPQV8_% z#mast{-4A!U$I^Xqa;{j@!Lj&5b>hVE+UpHZ@zl{Qj=j6OsziOc#;qWKbsdiw%5j> z0mH5xA(r8wFXyQ7q|g*q#4;Wh)#3VT?#b|{lflpv)6-;~7=jozrl2+-jBT=NEyGL zkQjj@Zt_Pba+L6DaDscf*8#T%Tg%0qA1XjY^nbT|H^riSYtJQfjM$KFtm~Xpp#aJL zv-#}BAoQ5^x#)K(9!2i$Fk>*FTX0VrA6#@i`M{-NSfIr7CvW* zk~6q;U+noGdQdRL3`^DRM%zD8F=CDNwDo7IxgmJlPFNfc_CvG(;FF1vE|QzD8|cho zAZLe~3B9$kh`M4t9Q`B`O0757PA$+OxnJl|A|4f|>b6-c;8k|V{ED32kT;YwT;rQg z;_GpA-{B#*2uMElaU$n*1gh|UXe*9a!di-%^HEb6$y7Asl*kv0;;#0u9-&2}4%rvn zZ*WvE=_n=h&6Xe_DjZI?y6p>Sm(aO~bvSx3KW%Dru@UOo$49gr!IC6_tpz_l4IuDA zdHU#QH`wdf;{K8YTh-oMyc~ONfTC=6ZsS&TMd^J66o5x~42^KnGpCKv^33$yng8%P zE%_<`$p&GlnNl^;6%T;VSq!6-doVKf@fbzx7j6Z^ws{zHt3m07q4ZfSd87sN7u@5D zMXncbEoYeFDDnzkUrRX#WR>`CQKt(b(XnSHf}7~buJ8A2>eg5oZH%-T;`W4sCLyD% z3o&SO9wFOS%Nwnp>EAUG=mZC@)cmp43`SE&0~X%vU}S;5wd*fO52RV%y|E9sie)dN zDwoBgP)NfD`865dXlioCB=kKMRJig#ymG^+usvl0?O5s`>bkC~HJX9~zYM1ruQP}7 zlBy4vJpqmXmYghoM@PaJUXlJO1S0*S)B9#s%utefGF5hM1R|>IrIi@s$dFq-(Z80h zaOCG#U3-2@G7PzOkH4ZCxsL%oSw2*jks2bCSdY}y zJ;3_ch#;macI28IaGyrZ{-9!3@q~{*45a?uHz|4&i5gCOOxYcahJ3l8^(C{ouDP}I1H*@g z_KL^a-PXVeeX`=t9|_nRmpfNzCKdxg9dbWetcr-ux7V-YNR(NNlrW+*mR`=vUf7lG z0YV`kuDy(lM)Ec{#`Z@BL%~?%TC%w>Txi%_Kp6K!b_r$YqwuIVgRuND^>GC1A+Zu} ziQ*yX>euZG>P3Dh1M@Q^nCFRL}je+i&jqdQ1k3~ z925n?S3*;pQb{y4JKTA;!GH{Pp0)36Eio$NYf1QDY{6p;ckfnzsRo2+cXQR=@ME>3 z&-eJ_W{7)B_l*$I80d>%9NF*_M{ov~oS);tQn-YcUvt?ph-fT-|D8twSibsRvUSuO zf=s!>Rd+HG!R$izT<4FrY&9jhkall0DB7^o=xrcmkEZim+u%sirPNZM!~Q5#&xcsx z;s`-AvRkHyeUQA}oi!VF1;c*}uXn}T;Qox8&0~CTEllg*y?ZOe3zc!PTdJ!=k*M+d zo7o(A1W|oO;uV&}7~5{U)bYg~WhfHWJLAKV-kEpP8uUOE`l8$^*ak=9RCykJFztp2 z>#}t3?Bj%N?W+$PUq_)VQR!8T6vGnslMQ_O;V_?2cOdu{9+?cactlr|kTf$g@OxJ% z%1Y<)yjRDG22Oq4p!%GKEojp^Un)J|;PVEHop)l;%Z#8#5gHR^B^=vAnz2H)TPTGQ z@eZi;NVh)K#RCZ|p4&-$j#nb0W;Z5JM1b@9V}+$5I0EcfpW@u#V4!txerbg3Ce{S8 zQErA$50~hW-+jGkmKPMXdQq}^9Nc|FAs`5c7ATHCh*8{@pax~5IFiIpf z=!4Hqd|$M!Kd51jCHSRE+AEhGQ1DUeRM(jpv`0IXn6M8^!ZWw-G9U#(yKine2S*er zaw_PJZVN`KMwfN-4&jl_9m3}=PFSLFh=av*0Y_II&os#@i-Hh|dSyBDXjD72Q2gEp z*NSSebyfguG;^Z-x`DF-9`r{pS{PlHRCTCp+7)7j zFPGDdRXFIDe^sck@I5(p_n+dkAG=fP7=wHnZ%?do!+l_{z`N6|@3xw^w%Oli&{6wEi8|fj%5p&koXo17bQd7S)WbueaA37c z|L2!ygLawhK5r0@tq~W!Cj(tWQTcLVxv_N&un+VlbpKXCYqLWA_IP?DoBMVu1s%~~ z{M>qTrziuaE>)&>cLyMI4_OW7KX>$Fr0R>^ZXBh(dgVJi$`!I+$_#SW(NNNftdSq> zw&0ukecLC~1)PULMBMs7bm)nonh?L$Y*G?CQmT4BQZNaRsD+DCSbMQvVu z-*rx6X?o*0AxtGb#ZQv-%QmSB0MeD3?`ipqqyXIn^O z>F4QHl-=(JS61~Y=?2SEk>Q9f;eR^=zR3WI zD|LVAIv4cG*zfsij82G;8GqR8jZu;t^Pg%P8lk0Oz54;hF|g6(OBf9!NM=`hN1N{Z zg7m|O_f!d}*_~qr* z8)k`~=T^(~Kq%5@J-qm(6Q392OJZZ5Z`(BF+SuCIn)yO|`0?FnY^9&!GMM}q1%eX& z%oF=DI;EU@;OZJ@WNK08T4Uvf6srcst)Jt$jISy|5U)O#@3#Kn+luQ2>%eRtyt3Y! zv?vud?2GEmEbl87ctOefS=r*1!gzt(z-Q3MG@s@S(+xNG~|&pa0Q zRsP7)8(T(p|Kyble;kG?qiWTztqX?fZ{rOVPCAPHs_>)aOepFId)KjB%^lFSENi=w zwLscGHL{1O4~!J6uGkVCj7*>>=6RwjvuVKmM z#~2{YIfjisF-5V@;+D7LsKv*zBK3813@~*1KL1EF3LgJc$^R0~fQ(XSlceuB+DI(^ z#B75uBxYY(w>T1kz9rv}ScB(A%Rv^W51e)ckyoUq7SULwQ+eh&yCxd-znkEi5{^Ms zo-1HpC2~9OcM#0;RiI3m=V~~ecla?b!nhF&LmFNA%(H#5F?o`xLL;4@p*Y-a&fxa8p z3%*n75GdH7t>cfSyDyc0o8r}$l&#qf(-}H4FMO3Oz=5qpgMQqkJ1`yw~jqP97!>r?7gZ*cf!B=wYhfX}#-<3IU{94oxMFj5_ zudBjy63roxvJPyy(y5E|_qT`Fl#b6I)?vh+x2v4G9|MUnp2?}GM4{wcraW5LLr_6^ zU{2m~Cj{F>RK*;%QS0{1cmkGkspuRkqfSIXTLGtva!(+#JXcxz8!Y#jNDRzXxQ!M@;!{^fqG|L{evFk=vaZp6SO%W2zZ~MTBXcD^xMU!I4K$s zpm9d^zJDz8mfNOogQGM^Yli**lP1A5`>jqRmH>Sp*_5bv4of~u`=Tm_7i~*4g>=I= z;wZ9qxh1J*?uc>Hr)nro8^|++&x8-Sk8vQ-Ig0UIO`+w@jYSJc&3iw?ei;eH-a6dV zp%KW*(|*t5S5p+&+Fn_d7y`sBhqN(HKh!bmezZ%&3t3uB{wa0E62465I|KFDI%S+L zYmyNF$#>g6pWBa7F(S-j5udy{uoL^Te|(_&%Q!z_0~6%i z)z4<#^oP~-zt)A0IC9i=SDegI99c>-vQ1A7#}Osx&F=92Y;$Ugv-HD#$Gfb11%HM7 z5u8sSe{aKuRx4ueR#(}shNEi+Rd>S`!cgCb*ae;; zU&veG{WCA^gFe{(^v(~%_syg6`V-&OLEm+oioKBz5OQ|y-j`$zt;cWV>Z)P{f-v*X zUyNR2h%}zXB{X80Pme{hsA#zCYQ1T#E2^I7jV~IG1j|)HzkVzUx$&j&TsoGH>fByf z`R(9_59IjD~}A_&lV#$ZU2Zm{?CFHK7bB>_CkgckrE+qvBc-l zlo44CEhyRof^39XenRUn|10o^4_Qe z1y?%@JY<3)>s@Vu|6x^F&s>%;!;!s3>%yue-6_EBe)GqngJIx5*da7diG`e08pq@< zj7TdU_9(Ou1E%chNfhi4#=(0Vx$%CndB4VXsG^unB&YLCaf=f?SU<_5IVP4=mNGhH;;?l zEC6g>eY@X*u}=+2gxy?KYIB2}QRC@c&k%6wICkH=D;U-AX}V69+o0{g6egXHU~4n6 zPHLnz0N}!yND#;(Xx6FsRr?CYz#j$FKj}g*n%n>njFhskB`Bk5mGGU$EOM?}q zDD<4Rs=3LJiA;lX^tPslq2R}5whjOL9Ae+l&!l>x)rp=uV-7nY{M8QjvR<*(*mjDs zhw1~P+Qq7&!!D3@Yznx)>B5Q;v0C|lC~D6e4Wx@>UwMm#VIO*S>_ z&n;OWM0!kR_iR6C)->1KQ|JOiPqTQe?}Xy>gisi_F9uck zzUBHs3qgxE`ZsgABcOg-as8VEG&nX}>ljUm1o=oU{^ogmBv$uhX&9epN8fzxY{T_P z?BMp#YWC8feP)2I>CI%DISF7qk=T8Oi2Fy5Q{%g%jnJ`ePaNz-1CTLm>5<{K zKwwu}cBWkMM5lXcYt(qWk@J!BM!zVypAa?GK5L}{Wt3|lQaKDUlIpJNcVj#^+ded8 z@efDB7{>7KY_x?T^W$e-9^n1L_RAjO7!|})yLNIQQ6F{AybD!{2u87O`<;F=W1%Aa zWGp@h;genR(9zT0P)4ZEpHITp+_(9HeI#R4^UT3=L$W8#UYpz^vfm!DAH{kX)f>Xp z+buIcUo(K;LH^yDHg9xaWo}m@MwWCtMDLv1fzk1lM-gxGH$ZEYXRyouaM;U7xFC+7 z1L-BTw$}stXlaAd&b=S8H0)^1^}U<0v@%ZY_tuA2C_dstXW@=OU~aqJYm4V`fgg`) z-TsXum4cKeN59x0H`%=$RvL~-=-$7ewY+!`*fGbejiq>I3tttv0;161`qK*c8?a@_ zVa`S!qoo+9iamZQ{0|+#Om1ZQA_k9(Yu;&JP`T(W-L*X!2AUJaw@u)Bq(%GGOp`L? z?4~@9@DB!}lD_KcJV($W29|WiX(}D@)s}gW<`JVvF@rkqBMCUbOa^782@Sy{M1p z#8VLIC!ibwth=>swNnmwKTgV8RE+?u4@9m*ePL)|PU#rGlnb=Jtoj+59}Iaa9-ae{ z-k@LL94_sRf6gUtYPcpwk@x<7E-Dp+-p7_P(;o&RQoSqoLMVcm&V|S9Dt(k%5V@zo zn1Nz@a*uLLV+14LjpMT^;pk3!yhY_5Y#B^UEk3@+40;!$c77A{hAWPhXM+WUP$s?M z!s$y`k{dI5bIdUeHC61C^{Nbn^Tk_q_rIbe+v{tV>fU4PbQNc}3n>_C!dKq=`voAv z3B{oU)GuxCAFK-I3VR~Py-kt7J2l|u&0l*SVM_vam)_bu9LXvud;Lrymh_(3BO88M zDg=dBGj2FtV8CFr=<%)`KXmfVnIeP4ShSb#$8U~jOn4V{c2ja19bJ*Q;V-p{rG7H2 z*`IqNQCQx$&~9@knzmiXKYyMHS#f-YE6p@~zb5MDFB<@|n#{NUs*cVI(PRQ&;5sEO zt&=*6(HCO1&E>uLy2QO7(F_A%XZT8T-X$YwIy1}};RK8<`k=QH0}3{MeN%o5ONA0; ziq7jO;U*~ho$Fi}dt5s5VTcIsnZnDsL_0bP~9qf0M zU*iQ*66wxjc<s42uaN8HvQy4yYODiUc}>KFGUwijEx&z<>P-GEV*8Ww6RX-v>L z^hb8f@hI3kp(iUa9gSFiZZ?>P2x%`{&EG6r0C%Xbn)GFiG?{g!TT5U`qQFY)DMb?? zsa)FG@QDI8ZwzhTJi@x3E$RWg594U7(`VsPSOk(%*|#YU*Hw8FW7g-w{DFDXa7FtF z1q{_$-^y&U4k_TG=ma$e)yRntir1MS@`1h6SH^siSi^Lbgee)<7iRyyp1~IU=ue+s zdwaw3Ijsx}8xPcElg%R%gr7HS_T4{tJ~`Z&nDX{KuIt45MK8`_L@Ikr3>70R7PADc z2^tYl>!qi9K>|mU4yK!M-VTJQ>>V}*SP!A%C$xLp5ql{5nGt_`2uGb>-JX5`V!>a@ z*PR^91orHYhy;OH=z2zzs4y{rPkC2E%&}$Jc%#mrWp7_#O#JNZFY^MbYwwjqLNTcR zTzd0yGbKd$*Zz}r@V9M~RO_;!B|u_^R~b8(0pYK`bC~KFF>C&`hzGx?&7bqK#dOJt zbRpQ;;gbzUWc+x%{2N=8wv~w;*TnCUYQsmTlQBBM*YRwLf+Om`+a@r}d)Y0Wdoeb!DrtI*v%=GzeMJhi1a5tp~okLq=hn+eRGqpz9papWYe;?AsYs zhXn>ya)fuCllFw9gtP4yG#x0SHXc*T(}wTcB4yMM;D}e55+~E2QNVq_dTo3^j%wYs zn0EXE1;nb`6L?z!py#=p#K`SnASCultS|O}vN-)KQUVm{ay9!C-;AZBE)5^9rTK$; z_r6z;eq+S4;r+v3BvjB;zx3#q2`6B;Ro=UUEtN#p{qW+RNH8~X)M&?hDt%QilON}7 z!SGV)`%kK&=*{tHj5w#^v* zwbSCX>N65rynE)|OYdluZ#HaDcZvy8**C5)9H1j#H{Bm3&Pb?r6faZy;SQtyRX$>w zo!5ULAe5~}cj>Od`>g3r-vR?QQM~)`OWpsNu+zQ%%bN2Hl-jqq zfAKH2{PEBlB(McEkkdU^;hY^}cYgY{)j$lHpRUae;?u`68g^32Pz;>Gyb52{Afzk7 z_;yAx3W^6^;`H!*Lqq=%Nvtpc`XhH-o51@@cAaKQsH_-LxNxWItRvo2nAr~U+lv7C zp1r$%14cG*dOD7QIhWGA%AeX`G>J}1WTy1#-ABqEN>$b|9VFC z#{kKEjkUgDH2TVa^xLGcH_UTna#mJF!G+K4rI&bqTFY}pgg)zx()yhvUT1rO?*^me zSH&U$bT;fhK0`oxF%8k@1Mqd4^?S6xmjcT7whnggjDema+vO=-UnjkzG}I|Gq0fHu z#rd;1Iv`C_H3wU>R&DMck^F}(;I_9n_|-Tg_7OGb_H};foz5%Xj>l2xPhHKICnP}AB_-iEdTO?XFN!2}o_tU&JbF(#Bc%I;7^5Uito*$mO>UxsT z2X$FLbdIhvKvWZ0Gq3G}N-oLEa;1AGuhMb<{7QEeOMV#o zS%Zo9NW$tBdtD&on{LhYEgI+>%st+=grj$doZ1?TJbJ?vY@&_z1s(Jvv%*F%M{`J_)?%YTe^j~l3;Xx*% zW(VCH5ypFI?Oml`AK-pv@w`t^7ZXwx^3=3(6hrd+>=KE3j1FBl);60=N66&y3!Q-= zkSA{DyJO3MUd45+ybZAcX|`8htha$~+MXZET8ojN^ z8|zBSL$6$n2L09Et%ZCc;9+yE$(-VemajO!yE%y?z&3r9-nS41sBgMk{hTvGktc{p z?UYe>+@;I0*7hh%F*t$FKi5{~J$LzlhZm~xm#VUOY5>W?d}o!@{ZZ}U?=3BJF{mKb zq@{u%qdL>|lytL|s_PWd^H{$2>Wtdi4 zOH3rn^5_WdsRXqD?ui2n7wG7<+GUM<#lA?s%P^zxCPpXsd@odjVH~Scqo4EhB^|`Ba?9q$` zJL_W-n%f*9ees#|hj<*}`YFc76`#*p_je6xKNLbmPi8LP{%L|bC)9g(;Aq-3?U;38 z9&S)};*I_0z4+YOwm*HAM;A?4e!8BN8-xbS+qC_UQW1j_m8ndSftoWq=u+@E!7_dD_4KKD!32aGZkcpjl>dNC4RJoJ5QwtXxr*$?+O{jfvi1X_UNVL-%P zFGjB7{Y=usI%i?6SVTV+q^;B!n&>kbEpXYdXQP7~qm*}UC zG!%OH^2RDX3XEpno^z?k(XT-Zw40kS@^4Mq(FeP=uqD3BkZf&@;(3g(^63RahS(m@ z<$4_D$MDHoHx-T6RCAj);e9bl!a?0^Z!qjo>$thhi=&7*)?9Ud9}2@-a)%5Pv3~Br zlQaLd=%bI%a+;Pkd@(|5>`k}41IoJiUUX&<*Xb=TUKa*%{jPZ5n5gZ8)}QJleLc=V z6T5S+>ghzmKYO}#of$@i8i0;{fdlHh60!EvpJ1TctSSGS&V=i?GF#UC#_y{uc{bke zs!&pFH?gId0kXxPchR>qC zv%*%bYs5m5VgSkzB<;9Sfa~?ND_V>kam359Wo0TF>*Fe#jQ>*)!PbRZ!5hL3@IzYC zl~>Rgz3Nvn71@FJ9OoZ8je$41E>S4#XB>z+F7l6e%(y^O%bPD&zYUO_$Ircw^Mg=p z-p+()k0ZdQN&MD?8y$rmoaTG>#~bg9&)F;9iH5uXhF;_`v9*70D6N)A!4Xkha#P)~Q`zmr3IMO8L;9%;u(|G=UFnWg+8J2x2689~9!&^EVX>j#S zL2DJ>+m1=+y$?(ZLiujN3$Cs$ao_oa>s$R|zre2vzF8tz?Zbe*=+gToTU(`YG%bPZwTaHG%&AJ+i95}L#*J5>e z0`H+z?s?NshM@x0D2{L&3bYnFS%yT?q1)PM`0D?xK;-Pt{+Y2TEy44|AACP>XIh+j zLv#az;hyCu3AV8M4X zImwAv!ZA9_ziwtrPFFMxDeX<(hb=?1DvHeIGFJ$7-)(B3>WIYHb?3r|tfA;pIw{+X zj8tT7qdl>eFtPB5T@KM7DfLL}sf*!x@UkI8Y7rpme4?UTffA1T<8iXQj-w|yo#Q6k z0WL?b?OVn32)CR4>yA9}Mf-@Yl6xc~5a~1RYUZ*Ru%tLA&l>x|qOwYE?td<5>HE0J zKWu#x`IEKw#ochUdPVq<&NmPA)J^DeFMllZ{Vn~adPgWUhnyAkRxw8flE*4G54s^z zo#3MOZ%wq=a^`gqKEH#N$_6eCGe9qa_i=}-KN75*NIUZ$TiAsU^u4q6L{$e)7<{`B zfL!ujHuW69=V#wu`vVeQh-@<{TizcH>@`X15<@t;HC1)!X{Z}yJmgnDlH!7bFRA?{ z6fhvB%j9g(Dz2+-@^A_fM)zc!UELf-hORPan?mtmSX}D#pyIi+*g!PzvV15iv0V5V zc?6&P$|^&}7%}JE)ma_6k%5w;C^rt=^?|JpyNcZMqrmC$5djsADAb;|`AWVQ16~*u z(AUUEpq$Ekbt$P1$nAUeBnO@=6pauFpH(q(_WCQ*`pdXVUsg=2h;oD`F5kXCQ4VOc zO0UYW4z?^S`Z;{vfq$P`_y0Qn(O^e$=Hzt_9R0BKRDAw-L!ftIdWtSa>bD3V=a9qu z1^Mk;=QRC5b8-98Q=gcqwA$jS-Fb`#s^40Cy&6zJ#>|Alo8gAR+Ip3)Im_dGh{XNrs;-_P#s_Q$dd z9%4`)1$x5czjDUepst%`_m1cUE^ z+angMjv$oTsJ9#I_sZ;rF5UI9Mm<`O%siR_?fWnCl0z!C>NY;zc>?RZY#c|fy%xlg zJkz#cx6h+hEFJ3Z=Rf4x#)rNo%r$=CGq^r?CxAFJS0vTXnjg zO$jv=rODUqFLR}$%@=$0&fz^b`x5(7;P*gqyK$RU`rZ+w%`C(>>tYM1kq>dl8-Gag zt1sxr^S}7MLUHA1(OB1!6_f9S^`qA&oq1>c;hxediNqUBbcj!pTR%A(NfcPs{5j-; zy4Lc4aB9c%0YQgHi#Ip`vCsb%|0;kIUtYXSt#(6Q-9O5uA6P+c$n|2t{m5qh)!SoS z?uafjwN>Vt>j(J1SBhRUy#VL+gt z{T6y&0^OO(4>cI2qh;mq&HuWcQKFL1AurPan5vhG-25R1l@R&0PuAI^0nN{Gs|mOt z-@^Ya^|k{Vmo(nnej^xye_8s!nZc-$1p(R_atNw<(Z7(Fp%0P-$@yYV$-l*%I+Cmk z4oNXdUP)egX<|t}XpHa0{TK27y}6_a8vmbvB@hTJc^r#rcHH=H_#1GD>>~hyL=Yly z0|$d3Papy(i=aag0xmXzOb`QZ5{DZ>0(cl4p#*8*WpN}Dl|!%7lw0S6Og7`zo6 z@gU3Mt>s7rIW})AM-s@B__{e#K!L$G$dLx?S$tC*8KB7KTjj_C5{W3pnFC4;qC96F zD6@zs#n}paY{6Ad1o|W)A+8QEUu=k0BY#6G-uANhb1yQUchL89WS1AW5o#he-)yNLBE} zQ-WDiwLFQG5Vlk+PZA}Rw62>cg%ZYCH^`Gl31_XF;>n;yu-C2fWKkF-X(8SmN+d&C zo;QyY#gf+HEuciRrOCWS6edZ=jkkmn!;lH(Eu+M;WDy!Dhsl3X`$6J-lSZjiT`vXvz_#oJ2R#+F;!$2r%ai$fC@CZbH@-f~PKH7#-vA|*rI5%sNZG|!$lzmB(n#wI z_(mz|jP(_K6r<*ll-!6C)B_BqP@**TAWJEcC{N90D`gN# z)I5@M0a1l|h@o6T)S%|GlxvAP)WdA$R-!(&fV82TXiP0+Y#1b(Q;)DVOcBY{qwEc< zM4%RtRD}5LsKpEwd44DAF_wxBzZ>;9TZPO|r~MNkd2=hkBl&Auo_ey};7Y5h$QuWNVNGil~(&O*erOY868>RG^Gn&C*O1 zsGweAYi0UFmEssN(alXQdxJE%7pI`V>D)CQK0j$k*nk*z}(?4dT1Ho6J+QExIfh6)Z) zZ?QHe3Jy|lvo~f4vZ>7^-2%Z;>K%q|h2S{#E=#voaEjW()@>D>rM8mvx&;@h_ZWJE zg3HwVEWIhgRcaesZ&i>$LnM76A#U0OhQ7QIk=D-A*AWt;J!I>Xg~VtbBm*}g3ECru zL8y>4?J>(BQAnQF$u`IkBGI}?h6O?@v?mP13Ly>JQ>J~Dl zJ!cpV3YpVhu#BdJ$h4Piqg5fG^^lB(gzadr7{>C#PPAT@v5v4C?KRt&EKH~Mkxbl# zeQEs+lThJ6+8dThqHrkfE!!kRm_ZvLnHC5$Y3~@O6~ghf_bk&|;Y8X8wrQ(y5^a!V z)-9Yu8)BFZ3a8PAS!Pqh8MG0$*{X0BjZHEa63L-`WSGl~K19DeP>t=iZs)vSyodbt+W}o)v5@h&63DMq8+q323cOTi#E?9>xg#K7T9F6 zXb)|XWbG!}NBhCB4iz1sEwQWavBieDE2OTjX zJFbsF78A4kKg!Piuj%st|5Q{|!%$ICH)Q9b8=|73Za8)4y>>pIPp=)&4N*~1H$X*2 z-4GQObr6REqlQsYQ4OP_q8dg;MKz4WfKkJ!sCd`VkGJoC@crewZr5+ub^UbRuIKY{ zzbC?X0V%1JrwzN*DQT2v8g|)Jpp<7vcR5osC`s_$?i3W|Im2#m3Xbx8!|p%|p7O%z z?qEtb1p?m_O39%l8}@`#aw&@%_QX<%lov<$#8b$W6gUO4m_k`>pglHNd%Ep}4YjMC$a%P1Le24qP&1#VzKmsC&?4GjDe zHw8J$ATFt*px{hkNi_v+V5*nYP%sTl`w}k&JIZt}si)xJEcX&0CDXw2F7Z>=Hn0Lq z0+e;5tl*Lk3Lef5E$O5X4D9fdE=pDdJGLZ9SwG5-FX^FV!vV11ue+Y&LMzONS_18aVc)VanD~j&tb z8~FHC2zAdWpO~6LrN9M1YATg#5U5krsI&%wJrzo&j|!Zr8B_*b=uSmZnFgUZ6-Q+? z2m`5jDtlBIOwFbOa8W2VhsrUC!l}7bZi6V6N~H2eMe)?fzY%aTWEq7jFo>bc7*t_{ z7{3gliblo6Wqhg_E&-N_sS<-ky-ZG(Hc0Ht)KuB1#JNmQmBXd(Wgt~ykb0NdsLBRu zV40n&8kGi@6;jo3S!h`iRb!BamladB4YJrWCsj8pi!Upq>fv(8@^Y%dAcrompc)(G z_~mY@X;e;JUPT4r3SfCP)of6xm)B4&4GQ~mFV#A#a4xT>+Tcp}avwF{p!6>HQ};F~ z1Iq){eWS|Y@(!vUt_m&hq&f_$@bWHdL4zu`JV@O?s){e~p%%i`kQKeu0|qs8MIZHG zgBrggL_IXBCaxHu7Qr>Zib3jOgGRk#hs?VA zr5Zq(pR%b-=jjqWrQ?YzP0 zO~cVHG#CSEc-qBLV=yh7=7yU>X*o2H!4yu*rBybVVrfL$rBPEnjZCY8gOHUJ+GPU> zUCE$bX#nvn0ov73khqdhtA?9_m15d8gIT>&PP^V zgAKZ>g4Wnz!>@AFnnrEJRaG<}JRew9O=~vft5?;~S{m~0tGu+<(R}BsdYT`;*S*R| zYcuTiuJY4fZrB@G6`;K`x;MD0gBF1A3$5y;wHx+@S9Q_uH0+D53esL3-4|c=xGV#= zLss|FUNhLCtNUoLH`wv3L$o(W?Znjsv`)AKSUpI4)8J6A9-_U~;IOX_)7~C+I9HF* zy5I%w)e+h|h63;EDDB;bg23t+?Y+^0;Obdg5WYXOdXCm@*dJa!PrKW&Kejqfdw+C) ze02i72VMw)CelAJ6hfg%^bZ>f@lXi;qtQYlG=<&^KL9{e=^q;osG(`}PZ|!`p-}p# zqX(SO40<2@pc{&!e`Yx7h2rR+HyjK=@$@f74+f#x^bq_|2%1ChHyjE>bLsaQ4#l8E z`j?}J;!rYu0A2)vQRrV8il8tC{p*GzJPe?JGg?H1@#%x`!vIW7|JHC=4U^NqYdCC& zsp;R39(Ka?^da~WHw>izU^wE1+2}tu90|be^q)qL1Yw2rF#Ko;Rzx2*91X*Y>GvCs z#$ZnR&!b11i|D(Z)UlXGLIqD>?8KBR>UBH?_`dQAQ_2_CyZs#j3maBjb-=@2;-@-GGaywBN1^D z$Vg>8Z9J*YNMk(Hc+#E$Wjs4}(wULLNJ5-)XP_9*8BcjLaE#|0PX#jYj2FgE1v9c4 z5X9+FMh+v{csiVs%UINSI+j6Xyf}6`oIaaCxc+T7)Eq4vKlYO5JATJv5Rp;4jAW#sT#W; zL>DsEh#Mhv5mRHl5k?m?wT(AoXeU!Qb|a21W9kt#5KKAKV61^+DwxK`8a&3$G>z2| zF;z?uaTCB)GtI`EYD^8&(s#4W4Gg&9%doJ3&Hk2E`4~R*godLMlT*4Vjddv z60rl!B1A2K9b_Ih)~d0kL(C(MwRUWnd33DSi5+1UBkJ7P2=kb+&WnvQk2lr@urcO| zvAQ63mgz*i6vECiUB;Kf*m-72<4ZAYoLM^dQXHGWDnrymaEYvw#(F3&iFK;69*={t zPLI_SaVe~FL<4|JWt}lLsBvkmvyBaQ9F%o#tig%PU{xR*-8dBMys^=X!?7+jHU@Bb z*2S^LATFEbMl^+RIV_K{DU8cyRW>%oa75Onv8FhV%&J28Aej`_Wup(8$zWY+^x-oB z*3~f|F_X`#Ml=JNV%9ZdvpQ4Gy587q&s4K+j5Rwm^{g61i#ro!-88m%Gi|I}jV*yp zJL~pXOE9yL+5Xus<5>AmUTly@=NUd@B26<7;Ys8vB#R*X(#G`_r-4ocIiO zAL4a49>xC5__`O5V}IWGdH|1Se=+uY5TDHsA>Ih#bJ+dHH^TT__Pxe8Vt69^%dt1& zcrtqc(Fq|?*k2htp#%o|>&8wz0bqYK)=4Dr*@K8T0fLzQt?^AYLC*fJ@l88H&HjGu zO(#Lm9zwk3CV=c8jBj}fHujH=Zv_Z;_D^GP1qp@hFyieHp@={&z*vc8x7yD?x}EDs*z<_H?)4G&|BQFN>!a*{8}A0z$Jqak-3_jvWycZkht|)r z7mV+R*Uz&bHohNQA7?)rdq2KD0Z2ghK(Z5oCmx6RvXg)(n|kos5a6lt9%6P1kcj*M z$W8^GHhrMZP6M84`oNwI1)d%Mz?q!^Bq2X^XQP1UOdoo)alrFU9|p4VzzgFa2D7sP z2=b#)b`Fqi`Y4>83oL5-D3(nGUL5}@o=pZ)kiC!%6kxHb7rKD~ENSY+ZvcR$|G1F|a`lEHizq-XI5-H+^j1paxcqf9%|#2hxzAxHo{nO4BFa4K`p^(8_NN>sSmob0zfqN;WxSg zL143~U%hDv*wWN*-xLP6j`uq^jR3jGd+tpUV4LZlcT*JD z-gGapDF*BqzZcvz3lNcChBnOsB-5ASP4hrr)0eSLabV~8m+?&r95QkMl9R~UWg39y zByn~(4d8PioIT?M#GDik1^E?_lggo*zEbC;acE6n*>j*A`uJDQoD2>F`L#O-#bKJh z_U7O?tfsF6Id~3x{Oe#&HU~g{6Uxcqa7^EXb8HFAbCr3B_eSC8nM~@tW zY$@j$OheEu6&z#J5PplBV;UbKZmHsc$RB_$)f}_w2lbX3j-}}b`xY<9I{t%mOFhSi z{L#I|$H_PS=-uMy>}~onuqD9RH~wRAO9#h}{3*1hljAV`6yDOsDQNmBwk62fKmJpE zOAn_I8HQ}_j0++ISgzaxgE`|hm~&N0({@75^ic+>sB))?o+`2FDHfRYpWb7<=v$7T9Cymg*a()4p| zYn)R${&Re50=En~0?AF}o-~a>bCbBInnv)s5bo*m5n^r%w;VYNpA2p*0LnTGLc` zTQRq;X)3nO$$e>jD!#3ZTaS!FwwH4oOi}3e3T|Ul6u;fgZ5odfw^wm}$Z24EHMiL` zt=?Y4ZE2deZ})Or$ETg!>$!g9FYfI=Zky>B?{+`;<)&W(+XLKJ#(xQJ@8AZIzlOGV za@$S6hPQWd?=<}y+aBb;I{s^Xdk?n*8H4QT<-TT$L3i|VUvG-xcZ9fajK_#O2DqKb z8DPgC_f6A`ddCp=t)?0KjxhJ_@fqii5pEaqfqO@U`;O^>cSn@_ZqtLnju`j7@dv>j zv)mx^x6qC`Znx>T@Q!)z-KO7SJL25;$A63ONZ|D#XCcHy-Up^xC^3omVbd(02;qG+ zK1(E~@OqKI1H@F`$EM%a#5CR~O~2cTP~NBGzdMN;yguX~ZX$~JnduKN5y$(y>5l*r z&--Hhk03Fd7ef9SBIfY=O@D@oxx9N#f5wPJ-k0Nl#))L!0CEmOqVT>l%|S^F-q%fY zcoM+-W_*rF;`0WPe*q*h?_1MfYLcAyUDIE7lA8DZ_+L(vo;QU2+f4#_KbZdZl5D&m zoBj@v?7W}G{|=H0d12%~AyN@<*z`}BRLr~I^iPcB;y z1#hfr9-rstjgQY0^Qw3ez{pAbCw&P7GKBwBB!NUu;U}V=;5_a!eHwfmf==T<<9otEhVq|{ zJmDf|@RLwaddMjLbKsM;WE}r_-;?cRJpYBrlilQOJ_PktKRJh=3_dkX&gC!iJvBon z@?VTRwLm8GQ&5S?yD0p{U?OZ6gTKU=NZ1ANmqrpvyZHQ6)YF_@V*WDlY0WMR}Nz+V@6zI%5EACG#We|IOJ z0KPE1yNjRYdtqjGkiS0i!ouzzel`k{yr-AH0ffNz^zk?PAcQ?3{-y|ov}b^ygG%P? z8RTyUlQnyW_*;C*jy+-i)=08z&j>#kwaBw4!rulis@)UiZ}%-~-xK5Sh%D;fGs`EU zUhLm9$0va=4)2-g=lNco*%RmQjJ&w8C*d(wDJ7Zm74Au7K-XIzu4}c#)+G6taMiN=>Fx1OhM>Mr8QP?s>t$gYN0@l zTG3A}5@^5`!_;Dd*0*AY>J;cAD;B6_0zE1%nN}_^fN3yVg}~@bBhcIeQzVTU}!Cv30c3MENFS4qe)*-N? zR`=671rBiaFs-ypP~clVLkkM_M^-P;dIW_iXfnN5Z~%nD=zW5NJ}7}65*&&^N%R3h z5emkk4+;*0Fb#c3aKs05(8GeG5txfUA}B_sd*~6tF)+QB9u*w-rMJ^#f)kPSZu+di ziCWW7pA)#iHN*6IL5Xk83_UI=jjUOqCkV?>8Oe-9;Ylz9#z+#L@?{Vh5aH=a28od( zEJwjPj8x$n5UydQ3D5fA4hB?sE&_KkGK3WM zLUWj6;WZGgVakQqeP{<$ExZvyyO?@m4GQC7g2J01rj}_F-tuAEnRemr2&S7^DDJr}Zt(##5g|9}|EwFlo9VmP{($D zwZ5M{C+r5-53}cmcYW(;*m2?ek@XAg$8lm*b~2DC`T)#^0ZF0{ec1#6BKjzjO#)Ix zy{HWwAXW4+xIqJ?i9YddZ~#!zr;!aVAVbuL+UNmLqR+sMwE#}^xo=}TfERrc+1L$a zi$bVP{XmYWAKWwyC0rJZ9J{S?{K z%_$UxQCs^tMWSJF>oBKSblZXa?Ld%r421UO`b}VpvM6)PjGOt(kJ4l4_`b2;Dhy-3p z^k;-f;th!AP$Uj-Q1ll_((r~vfBQ%dURd-`gyiClh~`mw9$rNBKQOPB7Zv^M%WLPw zME^zdx_PsrIBI7b{0=+m5P4fsq@k%qTR$vpa zYNoXd?BdlEv~EG67>cI%3yQ=rGksW4EKYBx&j_62H52p&L76xM%}5rOi{WMlOjsdC zG&2Z7w-`CWAPK9)C^VBJtQMoqOpUNcjA>>%gkCXrg6R_0i*aa{N9Yr0npw3%zj$pk zt6dlnubW_X3p>PkG`nBeDJGcN!@@3cRx^7>7!0P^6mK?jG@>E#mS&Da6c%rt;J8F1;#@S>BZ`Q(nYp#1sCaub zw_Owy@0j3ri)O_{G_PMYCnlMB!=ia{UNdh-6c_KD;4O#}BxE!{S)3@@W#+@gNs`^o ze1aGv*)zc>iBlvLw16W{l~BzBjW|t0YZf@fPzilP;1Xv@7-*qKjFK?T!dfv-!fF<_ zi}4cngs@wjEdkJ?esPY3V-^jIb0yqn(TtcV;Z2AZ#AFE{El!qDBm%P-CSgc~&0>NC zkccM4Bne+4MoTynu|#5)Xe4rpv{~Yis3o!qiA$oF$k9@d1e7Su(prg4qHLD7OY9QW zgtS{yC{d$j{gNVy#w;6_6ic+tvKfg}qMMK{NXjI7v^-f_E-{$pFlmLv*eoYV-4fG; zoFuK1fM^9rS}ie~6&h)c#L}#ANWBv4gu*4Qm)OuskJKm0H!EwUrGClYW@WoHAlWye z?3Q*&>}XZLv{T|RtA?drl7eQ{j5H|OKcQNX_DBlR>SS53HS zwp%tUaiVqovN?&%tQ(fiOG=t`GqSj(bV9cvOOTeK^~v%?=}EI5CQp){YSt6v5b5a& zJxQJ-Ek_$T@>J;=vq2+Ilb&rhIOI_2xe0?yo*}J38$EKA^t{PDOTWar3ur5oGh(EgUJet^s*U*DHzf#%^*PmNUu(S zBn4kujW%->V(B%rS)-6kuQ!_=3bpjcgxRIgOKZ>;j{=n5G+SyFHtDTqOS{4@y***+ zRuoFTXluWsNLp*Q4l9bKbFdpQf+{3^ zW5P~S4M;oD4vuP2`li{TQ4LAoYIZnOVd>iw4wq^~+J!Fgs3OvL%muZosPx_Df_7C* z`rbrAw`x`zMDOoc%}Kk>`-fHY(!0(3XH;?N`xE;YR0*;kbYZeOQTBnk5T;I&eb`(` zP(x%NO%#&UDY9Pl0ggIV_ObbZMx7@6r1^kD4V8U5aloa{koBPtdekV{XXb;oYMkuz z=7a5OyzGmKgWc+ESqOcoU!5cCHy;{S=gRIiADU4UWnWGlT2PZ^1L&e;4Mp~qxd^6V z$i8kaB4_~FHxor94PQ2hKFraGW#5_)Ycz7%cg=?#8nx{EiNh|9UN(e2;?aPzAIwK; zH8$Cg%}3fbcG*u8N4hnIvM~B+zotkwY(6@yDVE)DK02du%6^_Wx}YhOji8H@wdJx= zb1_U?AscHhCTQKV@rh!Rwn`R3ALD4NWfSIO8f}eiviX=p>y=GS9CK+)>t#{&agWv~ zn>HV>)%s<>G#_u*24ufZ9Pie4$YSUd{n}31jQPZ{woCS)`NWJiDEn>V#Dca*Hj8#9 z>w0Cso1HLSpX`rjCqWmI{W;+z=>}wTXctE}DErIo(&&a{e>b}vy0Gk@371PZBAZ8- zcytlj|I8(|x~S~m=8|?@O!nVINw;oR7Dt!%>*iz&=F(x^yzF6f>5MKedo)qHpi7V^ zV9Jv9iSj2bWiWk`{K=Lwf*vA&YO;)^Pmw2LPIC0A@~15)HTpF9Gc6|_dZ_%_$&)U9 zhCB&#%A-fgpR=5*)#K#Px14I%?I>oI`xm6o%$2Ah0U%h`5=UA}tqY`39M4#k}7Hx$WXmUF{~VtIPYxfz2~ zzGm{=f}u>FfvHF~mdoLm3Yf7%j%cYM7~OK@WCh7sB}ZY-bBxt;wB@|USR==@oOc+# za_r=Jm$6=s!(8wfeez7pg<7LuzP9B;yD=bNH+iAk*dfPbF7_Kc^9BHiI_|Mra3vua%tE!FVAba zG-Ha(cTQefFeNC+n5twjQL)QX1p|{5yIZOVAVjfevWf(zC@7fA957WuwOrPKX$o4) zWd{gV&?hguzzhWgbHxLq6imyNS`epTwOnZj@e20jm2NOw0bs87gE3e0NoKx6jJd`!ixm>fHH}%WkhWZN znAHl|EMb!3I&Do~$R?Qk3PG297ONdB)P9 zv85@`wlp|wQ02MF2A3^ES%GQv*ig#zmd08ePI;lFvE7DOUYu;~wq+~bn5KSPj?!ak z8n)#sD_fdoY((Xy$)*JxSy_egCFfI=mn}Y6K0|q>#Ye~olvgKxq_l7b~w> znl<@y<@J_kN4{ElW3t(muUFP!T0Hrn@}{MwHs7Ya)zZ?QZ&%))Z0XJ~RC+P3{rN@8 zT1)G2ezCHyrFACXseEa&bs@h@SR?=4q0Sp2ZP70SjIKVh$1*);jsm0hLuVcIx* ztCh``HqG7|WlKw&W3N})I@#vhTd(wEUiR$uDcdYB*Y5QzUv7E1eQ!Yd%H+%4dpnc? z%q#tSJC*I0SBCd?DettrGP5_Re0B1bg}pt>4oo0ockBx*-=1uD?Hf^cVeWYLMU?MY?$qv!D&K9n)4ngJ zd~fnj_r6(W5c6vPzBy&L<<;SR^UAv|ug>g?E8m}dbzxtEst41NY)@2uVCjI_lT;tJ zbP((i)kl*ZBzuag7xNm&o~ruT@|wn;ruwAiHHRIl`gHO&mpwz(hk4y&N2xxuyk2X^ zsXlLcz1@yieKGlZw>?`G!o1OM&r$VT-Way$s_wPCF=HpHzMOnx!A@2UU^6xCOj zPMCwC`nsi)-~d$LOm>nSeAOW4O^!pX`quKM#vxaI*Yc*rp;moA`KHUER}Ep_@;E@% z505P@x)Y=^_-kRpXOgq=M2aRRr@6r=VIjVR=VWP@|e`dB;)URZUI4<0`0E zMKSMs3Vf<*%e%D&e$_86@3t2NRKHHX+g;G1iecXCFX&XwSl$~h=u$msd2gm5sQPX4 zy@i4v)hs5MyuVlVyCn$Q->3ScB}mvGQvEp@B<&wi&0)GZ`v)JZ3c5A>hg5&JbUXHk zRsT$OyY`Q$<}r6Y`y;CVS?<>EkE;G{x!b-!ruuL4ZukCKRUGqv|Nc4Eg5~|;{qw4a zE$`3lkE!4o z3e(ijw0__ygsPvN`oL9~p-#en=qW_0pR;~gTZmIX-}+&DAzuB$)Q8=L*=h*(qyEAi zb+YxN;lfc!Sx*a3!mNoz0R0H9tv)k`|SSEph> z<{S{Kmsvm79FVJ*w|?w6pjNM#`q*_quTH~$;yD1SS6V-*Jz!I>YW<}BfL*XYsR zg=#4F)BXcRYMAxY;RD6$^wv*j4mj0oraoObP^Qkn_9Y)ISHrD+u!9w9L~9@6pj(Zc z>LVSjQlqe+aSm3i(bms22W!-r*3TRVy=v^#XRd?wY8>`+&q1F$)B1VsLBD!!>*wtU z1L}2CpLZYZP~)**^dIb06RckhAM8?RwSF;kFsNQX^~J)$9(6W0lzgaHy}=rS9qLnW zYz+|(h18p-LZm|j>Ktr8=g^>fv$bDyXh^-Kwcl|ltlm1+?>aQ1&c)vI9Ezy7S?|>z zimJD_-fKS;Q}39%*L`SKO~iiLe`rokvVJ*yXkMMy`sK`_xO(T*mkWmyG-T{Ra#5mY zmvsPEl%(0+IzT9bX!cAEkcv_?6zo@=qErpl`jw_AO+#z_%25Q>(5Jp~6=i4`*sncB zC=Jv4b!`z&!)pDyy$G*iPkr58l&t}<-}D#dXgJnyhKq7F+}3YqiijHC)He%7WDOrX zn0%O`5m*OdhZ!1S>mcDUpb`?NNa*e?{1Uph%p)s}&5stVurl}#)ktz*{{eg3&T4T2UpgB^bv9$i+IO5e< zr+#o9sn^)BKYEV%H2KyaYmfLfds}~OKN8UFoBFZ)NQdSzUbp{9r^aFZY4}K&rl9qw znIl2X{;8i9j`U~>vEk&Sy_y5oFzjfb=3r}>a5SVjG!-Tt9nchEhdDa{mQ$H^pP0*HMN0N&ZwI{73u;L``sn!ufF+_WMYJ^mrqAkaca*9*6 zXRM=|;xz5q)=@_>RC{h})K#3Jt-y|Xic#A0*0I`Roc2QNSbH&EdvR*4yEt3x#*X(F z=V(3F@!{fJZDs5DOfgY=X=;3-n5?bBMv{+Fw3n?B*fECoN^6904A5Skija=+wbj@O z&M~p}nsq{ROs>7&I^j5`*4~(!a2?ZYYp|1^W1#k?b+Y!DO?#_#vi+D{dwXiK`&gmY zi=FB}R-~=9P7NO`*4DL7%^Y)TUz(a)I98^u$3~NnmunlWQP}YcZDVVcaNMnJnu?N+ zS809NY0mL#ZL@V+bG$~|(mL%p?$x$VO}mcQYyH??JjZ?7HtR37$Nk!uTYqUk9?-rr z^-K5h4s8JYYya_1ZM*f?;p1J}JFUOY91m(=o%(g*c#pON8%sXXt9{KHgPrKpzTO%m zoCs;(n2M233}`#CGn^BH+BdB;niE6Xw_0Z$C&Jpdr)FFyMzmen2c8oV?K{>7wI`z5 zcUvE{pNMJSn|jcFVpbc({?>nDPTOt$ZTQ5z_HOHMGbiHO_oseaIFX?1!OkW-6LlY0 zXJO7H-G{BS1SdrI(bO!-nWF2({?2ix>OQvqu5qU6K56~k;e_fwo%-G7%+U2=|L{0b zy3eeC)H-px&s+a!cj9$lO#RXA%+`glfA%|bbp6&phn=~)d#!)YIElJ1r~X`Ul63>v zxnvhb_my=H=3?l+Zk;2z0NpoJb0imEH;Da<;}Yw>wf?1X$#vhg{^f9~b>C0@<#OqD zL)gDPE>QP__3v7jP4{E#-|a5D?x(50yIqC4F!rB*SCMYm`p>YdSa-kmpBa}^_w&?0 z3$D^K-3WF*xuje-YMqCbROrT9=Lscl-T2fzsiaC5!Tyg^Qmvb?{!dd&kHY=lxIKDZ}euh(39@EL#u3 zJ$0`vN1tqa>V8?Sev$vF2W3S4i_xbZmXY--xWq*#Df-2>#PpL4{Stp-)=5CWG@6)q zlCMw2JgY3fPZsK-xM%O3EYibl&)z>-tWWnp`{1Ngzb5+Z!;@wD3|!KpQ{{TNEh+s}g&yHg z$~xuNBcn-qr>gWQ+;iMh)q1q;Iqj($J;wiB!6~mE8-1?iRJ|UDd%p6NPoHUfzV4J? zzt;c!ol^n*y6E$FPj%?=xEJo7>eLf#FWf)XrO)!e@ZeNXzdriH!&5!_Y#e0K>0bQ? z8zlX7pMIksl65+y-xP)9ogUEV;F7tg2lbn6$=cIH`YrzCg41FB)@X9c=@ES{Zc*jw zh<=-GQQhgNe!G9sozpS>j_9Jhr)Tv<+>7^4&*@3F7w?~**XQ|Pd~iCh-x+=J;pqee z8JDuCJkhYrmXcncWZ3Ob$ts5!_C!} zhJk@wQdy2NFl|ff%5eskf61M4yn!8Ea<@F&0N|G1E6*`-Y)kK#=Nh>Fr4P!923~aO z!*a5Lk4s&2hGG!dQq#{c3_^cu))~Mcil*kB;Ty!bW!y7jgT%H>dq!@M`j-`)Q5$5@ zWhH0y203nd2lI%kP}A8&uKdch3|W)VLM*&J-CmwiWl!lolJb{uK|- zI1Re!iic;)40>GJqO;`&gDoxnY=yz-Ps=*%HkhJmd1tE(AZ{i1Y_-8`Td6%;W3c#F z7M%4OtkIPvXX_0%+^Wj6K1053Roz*?VXuGHowEVMzUZpEXFCjb-0FL0I}Hxo>icKA z3772jiX_G#s|U zwC9ElNBppYb78~LD6Hh%h@lvlUU@F^n5djycP?r;?oYpSE@n6pO}~3?*5Jggxp!{P z;Igf`e{SAT;$QRNT-;C^UGwlveYi~Sg=%B7EmM1;#@OP|EV$q`wnj5cF4PKKF0D=&NhW`o9?-DO#QY^_uaXsd;U!i+(grt(M=ECWYYjH zXOV|u`pTA*?qQg|_UB}I0Mj?ooIDTTG>F^G^@vU1+BR!Fa?^MI%>^E{>HFyB5|7?A zgxgZ-0Zl*Hw$yoSrXT%V?s)8`pQ2mtdP)mTVcgbxo+8t*ZR>qcvFW~l>jRI|^mBCU zLr4U1 zm*!0m{W~9AikluqcRsw7048LT7gZ&KPvn!+tCGMc+sIi}5b&vKa$Z#mn3%bXTa^kv zoxe+4l?Fc3wyU5D3O+l%tE4IeOv>C{S%m_h%impBg#({&+kK}B556$H`)*Y>2+7=Y zuPO&j&fjytDi>VTw&y_=5qxoa&%-J*n373ZbeRG!&ZnebW`IlDC|Q>QaOpH9?=l}u z&7^WKi@|02RPALsxV(*8a9Itmn5LFo)`Mx8w93mMxH6wsci9H6YNOq`YzJ3Q)9zj_ z1fiMqdzXtqSU&y!63p^BL(^DnLXVBkPJAL{2mE zu2g}jOeXhAHHgkRG{*3}TW zX&T78IsoQma=2Fq!Oi&`?bRW0OB<))Y8c!)%_+G$0_J9NE3ZbtZTZ}~t5I-!8~4uD z7`S7ad-v)rNX+EjyE+Gw@_F~K&VzYvya!j~;Ld5@!>b8qawdOKb)tD!K0m!W$-KLb zpH&Sp@0sT3Ri~IKnF4Ngs+pQE&{n6JX>EdnYN(k$EhwqZFf%fRmDMOSGhbL&jWe^_ zgm{%z}Jz`Zb1G z*e1@p2ADLge<%_PDn+^H$ z^y?L7W1Bqdy4!4;mgilsGJ}~4?)7T3IbWf@USqbjDGIK8&DLo}$@O}(EmK)}-Dl3v zSJqwkoA@lSvNxFL(}TK8w2K|Obz$Op!smVMtft(e56fNa3gF!I;|uyBN$J?}bZp6$drnPr(%$l8@J z1WQ?_eo;-Li2Sb8m_**YeHUn{vzbHgmyE zwdKaNx#XtaQj=+^ya`%v=3DA++AO!)EO&0&Ew`sFcW)M2yqVT}H;XK_`PTb4i!F6+ z)(1D8mY1fj4{w%P>N9PNZk1aa@@?t2DlCm{wyaxjOVhM1?^cz?mzmGKRc&d`&)43n zv9z@17u@n%TBq|%Zq-}-nR_d5`7CYud+TobEibq2y>lyId1ZR<-CG@&K<2)Cw>mBD z`TOqQ>ayHv+xOsB(DLf^zK6GZEFGElMYnq`ujSj*Z}(YVZ?k9J4q4uqw&&d*uykfR zxVHx_Z{|C+w}&imwK)oIhb?bUJ4$YkSh_L`DsM+D@8lQM-Huw`Z7aBQJ7#%ry5R2Z zSxYc;|GnFDmhSxh_ixWz?zZiJa64{!e|rDJ+X>d5%)&+9MC%9nh3VcT>xXTHSzd_s zqv^stFUB2T0$uj}%FQVW&#X*)e*eLIYkq;jt}9;lxkr!Axmq@9 z+i5-Z9%kL6RrntM-96@$s~?N1eFe#~*b_aupRe=g#<*mw`f2l((|dY4iN^l-k2A=D zfup86xLhJrWr1oe|%Z&N76EK+N+0$ zw~8`)st&|HuNC8a2DX-t)_n)-;Rn-3h*1_f@xRfe7xq)g?yr{cuZ;;Gi`(9NyKCvz z9`f_R>*#z;&#%27eOPS~^{nM9Jnz5D?ZKTtwQ(enOZMI}MKR`_o)-q0A1(Qs)ieKI z{6l7NJGn+ZTKVIqO+DdU7x~8Pmt3Eb@SUMM-$+cJisvpRa8?va=^A`NG1F z9};_76NG(7dzd|}mqvp38tFayccovS+|KU_<-+meS#A&E`sp>j0%^}zQu&p!HgZpR z-Q!Goem41)Pj*L!t6&vVY{`ee!!GHZZ zgs1jougc*@4=o{YfR9IxaHKs&u1Cc(6pc*#c0~81l+=^`#7_Xyy^@@?_X7G~XGTxR z{?jkHmw9BUW9j|A*S3~*^NKuWf zg5OhHesy4|O4tL$|MjLW&L)4did^#YlE=WPfCO6A3*kRDQsL^jp)%o z#1y}s^nVPUX*`u}5XNnlvV@Q=WXrzq>)_ZA&PIw*6qSmo7pWu(MYc+kRFX=PBwLxH zLXLgSYYGDU8VMxEA>&$qzc{EYxP6wisi^j1ax4ZVSp zvnz4nYUY9JcZT@G{ZHZI1}1F&u|KxU&juq$J_&!_u7l>h2U}eCI%2H9>EXs@=D0D^ za+6*$6AO|jeYqYu!P;6(_SE1; z0oW+tA7cl(VSoOO=#a&|qlE{!0-Qma710|sU<>0pTD!szYN5ibNhh;6Ca6xacu@D% z3SHlG4_)bW$AXOya z^BnBY-$WMbUa(fVXXgm!)cy2kae&Uy36Z3CG@Q@Wx_>r|h2`6;KL40;fO$U^N#_C& z%uJ}Qa7|(2NTEK3Us4B4JVfkmC7rM(@l{NGj|Ud*%rMxrN(qN*`nJF9R)*kD)-BpY zrkJx~9WD4V0nyeZ7@xbyz@eOH*XUcdF(G|c+{=Uo<&SpoG$j)t;y}|?hCc(B`S`GD zX}F+EV5iTbq&2RMG|e1b<%|XFr|xcyGlJ`l7J^e`apOqYI@!6)P7tTzb^W@IHU2!t z$s1iwN7-#WD)SP=TF&O`tI_WQGV;GnTwwy3GLE5d>12;L=gXWL_sFIB=9B;(Y_Fd|jy!qk9EC&a)U;7}|d8;c5oGZNG zS-l2pr*^h0U$zBd)4{X->4pen0iUO{Tp`PC;f(CODX>g4fBmPd4K2R4qE^B#m~Z`7 zaKg_J8!DErbbH%E+I7iCP3EFFx1#Wq;5#i`cy_t9?WHlW8B~>3q^|^rjz5xvoHXn@ zp8D^XH3M2c_;buf(6BS!`)AR1DzZuN-rg=?2nXW#m~pq!aLBNvQ{P`6V?9r;`&&%K z@^eORLtkiEbcnLz{2DFD%NraN=p>6`7eqXD*VseLw2jKveU2c_op0kV$^x&-u`MQC zx*%})cjxahIUE)Gdwp!lo&Zg4#KkZ2!xm`{?!Y7$92B*z@fo$j2d{Pig$uc1Aw^z! zxQL3yJQ19q`{^judFyEs%No>A-r4g*%^6%C%uj97^}w0b1yN}NXdr8+DH?24RJP*$ z6P%|5pGM|Cp5P$>@KuKk-#)Pf|6N@lqbGIItM8j(fPewge#x##I$?p-gr@Z=n;H1j z(y6#@jx22be(ja~usakiKVTCs?tv~ZVvYY~GI4Nu2+!qmQ|#r>A9uN;kA`XPfxKxJ7=IWsGTgxrpDWGoS;Cg^`b{t zs1}&_9Y}t+iaf8_YYq{P`WWKOtx_y(g(EWF-n+A?(DnI)ZNgnA_{x5A`Ozd6=!;03 zq|ex4qz~tl=ZRJXn6fGFgq0=q%*wAwJ4ZupyYQlDIVy}gWc}=Z?u5#zsgvi);#xL= z*YzA)nvk@~w(&k64a+5)qMGtuK&m}v@u7zWluv)th}-H262~Y0sHQq&h~T~dlJ7YI z?>iC27X;K2mJ^g$q)5P?RY#)kFbFtcAjMSMhlyf!Z`D}hiV!iWP?BTrf^z{;`d6;2 zqwLth`O(dWcw}s?*~l|8biv-{eM3PNo2?%dcuBZprMnF6)pI#CV>nOvb~s{6f>f)K zi9Kc&9e0hOTSM&a6N)SUQ-(6ar^ zXwVjWaNgg}%;;r;m!{IHvTj}U**KBGu&@JdHV-3Cv$ephGevnv@|`*Na^IukwlHZ_ z&>^d4iZ%}ZX5q+!-ak=~Z+r5i^s}7n%GFe?yjgQFOHl_0QfJuz$}!RYmim~@2MhFS z-B@T+=mug5=ff64Y+xcPy|0u>z7La{GKT5|yy5MlRl~yRNwy^!ZBJC4jIx=iCcw^&Q2vHaAKi>J$3CC@Uci;QC4g%@F^tsDfsGyi3;m~VSawIr8FzV%nc;DL%U zluBKb^zsw~0m-?bBhKQOSpTccqf7(aEce&Qcp2gTSP%0Xf2?s6zwxvi+23xfz*fNP zK*!H~-GcV>41Dx))s@648ZMt(73uzyj+6UO+3d8o!&KeOUD2I%sQr}EQJm-om5R5u z?Ugl<>)PMYAUzit56`ubx+R0zhGT=;!KB05>z9q>46Mhj_@C(n(qmIM&; zn&PmXu&Vvke2OLS4$r&G{gY16k<#}?LM6o(x>EPdNR_hSm0fvjt3HWm{sOD3$YRjM z3;VoA%QcbB_{Uk+F#_(~s2AzVu@VDOvqZey4RUg^K!eQ#Lvzhv@LB>W#j!|BkO0^D zn^mmrP2lO*gTpIA7$9pM5B zVpJ}(7k(Q>KpsE-Z5}z#gzjRohAmsE2pNT&K3@|Cvdu8r&2zx2Ozj|_$TSTy*S%Ep zvvNRfU(@WfBu@x_?PQkBWdq8i3o+sX?pSO*73s3t5@``Gs2>y2xp7$6WKBf@y0wpT(r>65wf~?tHU7ycE78)G3Um9uQsnSD2Tdum` zkKL8*J56cOqHaBD`JX8?Hga@*bk~Fq>gVf+YwRH6Zb|5Ub5ATPjTgA6FOLiC!g4cB zOehuE{BzFwYq@cw?AoPmTlZXliTP^vVWu`!xm* zuQCK$!ujR1!}9nhR?<#%6$9a`v)tcG4v-A1_KB2Y;oUPWv%5=N5cXQaWn~E%lHz{w z3~8j^I6E@meDN$&4w2O{c~ zJFJmiy7Y0kBn=(2=8v9wXozgLC3jIRS&*8ztp2<-4Mtk6Dug+mVd!aN)dO1xK;>OU zU9*4cUY)U&;o4^daIGTLVu6a0o4wD~P3hp!^^dGmHa6HP-!R43!~pKJqsiklwje;6 z@fu{(VF(lXYdw@Ogd<_u3$j17;;x~|x|>XBn@z%DA%ZR# z`m*G?>xdZk%8$v_a<0cjaaM(e?gr?2XgAb%)d`DET9}M)cZ8y-v2?%x9D$c*cI;&U zsWW;S3if)qz%(<_YucX$p7YO6d^Kc2aPgVr>*wX6^4xXo+|B^9Gophp!~y3T-`;3D zwE@`*(tfdStp-}1=GC4!CJAVQ;osABAjU}e$=z#u_*Z%Cf;6e0&A(bsognw&(5`lu ziEcy4x1YD&VJ3k*t2X2m9d<>w^g{oYQ|9m|*(c^!ANl#F9p8CnDq3)Ob^3+cp=B!L z{% zPb7I9U3nz>hDjZp{ z(H-6gv|f5);|Oq;IFdM)>T1Q_H(VhLpF3~Ui{oF>p)2c3TEZzO0w8YeZwgwCl&G{I z#{vh)ov*OuKB@=CGda_CJnm5GAuheXR29i`rral-G#~+=QFcfd4Oc$+Zk`Fayf*81 zZ?O&ZDwRn3tnozJlUq$Ias?sKe{30z)bCP_m*2cQXoegsUY-%{vB5iytqGSl(s6l7 z&Z{?x1h94ZZ)$P3EjXV1u2KBn2F%(xc89t;qi$D!hRSVQRM{V+qin&zUZMBDUW;qP zt(4FAtEo&7ix8-8W9h<;mg;Ju&t&oT&b*b;8wp^XXJKlQR~wR?t-N>Lq2s>wTda3p zc0kqYQFYa`W_a+NVTjp27E)*n8+~IK=reyrIC?JuR2`f9b91>aP#?9|F$r+AZHU_P zrb-c>OlxFH-iF%G79UhxG;eXZsp>KeO#p?W1_ILEq zU%n@Y%XS6I+VChD$i!+}TEYY5uBfLz6 zfh}iJ`brP$;Lfz9O^S3EOk4GyzkIJ9v^i8ZUv24Iq~J>`6`7H!th}CFFNp#eY?ElF3J0rlLmqu0k`Zo z3lcpqN&tsG`@TgAz+CtFKFwZ5L|d8EICClBRXejHEmj}u%DEzUzMvxY>}^F$etjrg zrf%DL+zxwppKWmXC30HH0-6gtOL_jOtH_AeP*k=+g$o9* z(}yiV_pahZf}klfgpBFc?hNdz_;~ifWtFr%NL0(tX19B%t?mYvrAls`#G$Zs}18 zJNWChV@;|B6=`#Q8pc%sw$YN#5BeQ3MBaiYUCeCb<@MZ|9-9O6?cOC!Nz6cXYC>0SR+n)QU~@5oT)u6;DRFqXP%Z6vhW;z zVcA_r>mGZ=5`W2+Z`?riqWtdU%7h#p=&QFZ*|W&RP32=>f7n?Qih!qpsWAZ? z8s3?FWo{4o`}dYv7PvvYz)I~#9z&decQR^Zn+@t-Pt6HgVTBc3f9JN}wSldZdEZwX z(Qrd$u36|u54^l}Md`U4?wD00dBI_YF8;U%FVYM=aXRqisH%z{?E6C(6Hl_kGs0Uc z*T&IN;M*y?j7oU~@1IkH4&3OOSo9*pz#1wAUe9<}szLUb4I8#MP>@ZQ>xSBL7l32C z`Na9$u=l>(rkWlKr14)0I{Ho&`nOFDU#TDfFAk}L(5ZuG#QasjLiE-Vg|^j zd3>8`^ithJO`V#5*-XrsbzSh^MfBx7Ws{mICs?&9@=>%Z$ph!tq;++V)KMhvdChdY z;jWI&E1!`#WPD?xrd-?zvV;Xyzj&D9hRYiwmDQav_}kjs&&}Ncn}_b|o#VwHcD6^e z?PzfOyI<=TX;;Xvt;4KDo@mY(R7dJpc{m=QFo3|?cg_`Z?yCn6LJ>>(oa=6Vd){~ z33US}*k7^hD4VVY8Y^3*{P|4k8qBWqNC!-h=8Uoj>x|rb2_C5 zRdlP%z2J$xnwN5ihL!L%?UuR53ObbAT}XacK!>#5m(R@JafT{Bg~9N%48%}PR$l-E z9M7#Y)2b#w8LIchrDJT^mGsxt$C!!9otiqW4@n%PijRffG6jJUR+{P9THvUhE%#4g zz^a@zJytygB%XUHjN4%4b=T>yDoL)urq z{~pk+k=r-1=5LfKXdmw^CR#Q2+HmKcw0A~49IgLbk^+iv11~)&q@hTjX!@WO(F=GF zRY>Z4ph92Ojvw+wr&UwhTKbHET!!c^BtZbHqvj`@{MC^nd@ITCA`>0sgq5FO@jy-i z&3{{5Y?1qRw?&bHGt?~`5ja%lilt6q#@}Fs#d}i2R|r#qMwX) zm9tsrlH5V*()Qt({3JfKU2ocxpbzRF4xLx^)W@vSp2#gJM7P-ZZPquPyvINHci8)D zVN9H->~{h{FrJN+j+CH7@K$g0iy{^bL;^UIPd*3 zu)^u{+r%_;+;HxJgaS{gEQAhzZdn((9*(_Swu}2X3%v%)GPYW|0M=hWvzloH+Izwt zJPlREhJDYItACL9ZwcCkSL-7x3g-nCSs^?(6xnpw9co+YuZ~3M!F$^$H#J2pkU!UH zy>u|iA4jzZKW!o3vqHYYxx^al18j=|Bs@@Duz`JloCnfES~^@C-J#8r8RT1_0?BXi z?p8lrq}eKdH92nu`S||t#9Mc8(Nf<1gvS&8OLiG+v`PW(QmoJ018OjID&QQ2)L$p= z{<~sNz-hV8N4oDQ+d^z&{&AN?Cm1UDbNpTg4Qs^%mOgxS0X7+CWbIK`40L~LHgnaC z0NJ~q@_Uch$$sOgeAh1s?QE)F-;Pnyt8B5kZG#)uUVFUFp6Uv0O$URF+!e5sFaLwr zLl(>(-_Oy}>H^;=!RM|Mu*l>WzlpOx6l~t0^Lq3<1KwT=n-E&(iut#X>@XPT#iW*2 zU~<}HY+Z$evnz&eQe&B+Q!S-+GZ<=9I zWo+%Q0awTjc=PZ5SE3t-?Gc7iQ>YDo@HqaQF-Y+T^`E*izx<<(6yK>-Xga-~-J^t{> z$^=5$w_7Z~qzja{nq$5v%wU1ucR=ogA^2b4&YYD~fHe7b4lzp+P>wa<#}lfHUs6JE zj4#qrOlj!nx6f)Q?}9~ql5~uXJ+}GNUk(UdvutOKoGyqt?HzEPS&ywTt2gZ+`i6GP zvB*9ZSB%})9{ppjJkEJ5c5F9rMZUoMgzm@9%#cNOV9WN$q zdsZsgbA^DsM|2;Yr71(pZqr?9o>tIcbe?{Z=!eRiUQef>CGznOOGtg8pz$$oEnX#g z^p9oZKjxs08=Gqq^sfU(PN{@89dkn2<7tu4*EnIU2#49{!-h~A?VUT^1Q^oN%Bm4G z2BqPo+SlBG#>TDb#-y)x`0mvX8$0rTMZU=4d+UnIJAM}*;C2Lmb5HepF&9X9<=K*A z$w2;ndfn|gHsE!8m^aW!8M)--`v)Q^kb9pJ_|TOJ{P(Go*g%Kd4b-849}JvsdaO5` zN(0@^XD6&w%+W_`%|?bj6$aVapGyUk^Wtax%wSRzxE+F)-pJ~Ml$6Sfgj>2WkaxpF zD9Z+72KojEx+uu~TH(sSAyP-*IC*%}dL{~+rlibry5o%(SszV0Xjs7Gf43!#0+g@^ zMrFu^w4`j|7h!9lIO&_t#^)xOU*#Bb{1h)ZE>Cng)9wT}ou{tlh|*B~-9&Fak1nKD z?o*r(l!QT!=3?FHrfzUkG3{LYM~wzeC=ENlHz8gF@xP}ZFMDa? zvBy;Yc~J)py;&sMF=d3k4blrU8ccYxjCtWj3ImsC9{akS=#r)D!Z&@k7Q=Y%=%X>K z%s}&dR8bJAfAlhz*j6=oAZu1?{Zn@m4@Uj&E^VU%C8Iy!`wmx_xK=N+#?BR=n2D{% za7P5QRgT+~l`xCPYREdx6z7sAYA>~`BYJLR`u&)wTe8X!%sA|ZrC$VdoXQMvF0-Yl zcfBd**KKQ&?qbK8cHODz6YhASS4iiJHel9=oX*YaG|azxS$@wm8PXT{b+_O+9iFLv z4k}ygiTCNFf3{9DF*lsox%j?0hTaoU376Lf7fDkixV?D+&Z7L^CoPiqbdU_Ahi`Y&Z<~eYfpul#nR+Y0(K$Wk0Uh~BO z@^>z*ks4w^R#QEL%h>`P@B58DIOGf+y{-TKcUTEu2whBiH{ycY?UZZhXALp2=kBjYf|4&D)a>_J#rF0GOPs@OKuW6sq~SnaQi+p^l31z(rk zLme+LKz;3)%uh*E{Peiy*~4@Pyj86r_b`9}*S}Ja$3#0re8iTEEA{Bm=$}dJv)00* zuXj{8x!J-kRow%Lfu4vh53iKGlLCr@M(6Qgr0%VenhXtf0{h7ED9%>^jRgU7tEIG{ z^mXVnv|9)MCd`ubog8rN0@r~5aS!~@f3bV3v<*I;@4l;9XifS!DXdYqw7TjxxBk_y zZGdZ2U-UrXeKogE3?Bg~T~d8g zA?*ONyOpeCF1wO@T<>==T>zTn)+&_ulRUF>P%f13TAkEjh}2lC9sJu~?EP&o3oqU7 zc_;Ur)Jq*V%ghyZk?ow~pj(I;y!Z)%VH>EJQ{}Q5U zpnWpSuKkoOn1{FS_uQxlv5%^WDl`2Qh(>n8Iu1@6Pq?H7E!y(3_8ZIDPQ9J2f)k2^Uarl!OC}e*Qq%<1KZ?9y40_(y4aZPXtgK`jK6G~ z$nrOU+{R0d^jr(bpZj%^tc`(zWBXlrHtS$v(03l<>ci&AA%o-Aq`qlN>e1S%f<+nk zDoq6_&?d>aHCJPRjio92Ha~44PkHF@brRpjcfEPVE^C8<0t5Z_TI73OyRrDOl!mKr zPVon&vS4N*c0-AYGn&Vok=v_Ff%uQ3DXZy7F0|Y4yT4l( zy+6;N$tPt%abfl&HX{lIQdJLq%h1Qf>kA)`u2#oO_RO%O+@y}l9LN<|DGiScuat{w zF_3L*P_|L20QhH^_7)wbfY`@l@yXwGp~bjCUUbn7*o?l;<#upjsNOC-2^iwU`{)Imqc9@-T@-y#d#*0Ga6kLrVAkIZj&MUuzLq;8^@nnQkB<^H2)&hRU0 z?-$W*3Wh{>_FiYx#ecWY``=Hpzz`bC`Os50SP*RV9VsOF=mw?u$pSZ|O^5~0oKnNZ zovcma#23T-E^%A-G#z)gz2~+mBJo0_RwO@}jw|z>_N6l&aP_7A7e-_~adCyS%iOdz z&P`dihIOiAP@~4@a(1FG3-3HF@KOmHW9~TV^{b%v)G+7c3<|J*YcCUxal;)kxxC9~ znSlM4>&(37tztV5|DE4!2pE6mKkaAII9#_~t(53PuUA;kbrgA^N6?$Jb4Tbnmfw}p z%d$g%xlMWFqZ{zt?LB?}vMe!i{>wtAw;gnlB}aYXt}tSK`&I5ZIY;4Lo?q`vgSLxc z)xqDyrxf9sYa2y;1oiz3ey=2PfW6_9dyg}4k9#r9U1TxQHe%R3MIW+icX9gh+k(;S z-C^&0=-7SAHET@#|fEm|%=z7TM)y1VG1czol6z!vm@9#=kZy8N!bAF8+y3GxVwXkN>5$ z6KMBX+~Ga11flc3ja;&HQ1+EBdTXde^6#5@Pc*c#t1FOpX1_Dma(|qDdX9=bYb`x^ z6+H1;?#lOqu5{#j@+iG=$PPT$tpBd;O$S<6go9cb0~%6Ni!4c9Q82nRnLSQ`W@oo} z$E8xhP?YZ$l$w+HqW{Z8mv2K+a#@p4dwcB3SwDZIIIsL(uQ&v3oRY4N@B} zZzfU^p4O^=>-SCvDJ|cU_QbhEHaOzin7)`qfl>x_MRGh7Qx0XlIY;!!?5rQ%m-CH~H@#>p-IfM=t-oc?8#1wU zQKqq{UKg%+{k| zIYNSbeCmMeIUdzBJ6(wn#OogysSA3g*oAHH0)BQ8u5S25{BJ))(^9|{dR;Z%$A1uq z7M|v?JEOW-d)m;l!IbE1<^u-3JsNO2;YK+VJ8Y(NisR&Adt&xwFJ&rb#s8b$O7w-chciXLM77~bW$yQ& zU>nq;7icuZQ1I=-ya>OG8@8@n`E)hWi$<>`RM@aBqHw|8>!TA#B;;vOS&C4h1ZZ zZr5Dv2r*M3QiY$$y+8Zbl&{?uB3|EqR{y~Y%?sCw6`W?^K~vcS$4GsnD;zEdre(N#8Au#_?h45Lh{``EVx}zf5P~pBZeOS+?mTS21^IS3x$`Fc*7CBb5FPl zscQ;z-tDC$_e`0@o%-699Okbk)WIc97Mo{3XPY!T8BFSw*iMFfhrxk3L6yUk`*9!_pj}lJ&v5 z{(~d7xK7;sC{BgrT19utBZ)qBJwE5$CoNQ7di}GpmFVT0H!X8|E(@BY%?9aL0Tj*} zYdv~EgKgLK=67(>;ob1%3|5B=h8bP*_{Twj!u$~*t{v0`tLO7?FUY%NZi!#~Hc2Yv z%5SLH?MsE;r$ULxlZh{)alqPbmniD~W5g$arbEM0-pD&AS72I=*AIA6v19V1)$Avt z58gi$F-CQRY1?=HEz3QyH*HbF`>8v0Ed&d>3mc*RXZ2ATcN+K`%FI%Z^O5+e^@8)1 z4yiwz=LcFzeI0o~e*JFJw?2Akfh}$WYOgb@|D(!4o7jP#wJiX(u$Qgsi5;e$cQ33b zI%jjupB5vs%IwKEl&k*C#Nt}^_tM375Z$)!2z#~-G=7_Uqi0|T|2^#g@1VXDPRm7> zKIt*TxN-T~yTk|DYdo$WGok=;6`P+_`#EDnp!$UA6-S)g9=HC0z8bKxDXA@8l|hR5 zr+w20jImVbW%u=X11#m9YG4Ez0PWk&Uo(n^(7R{&7e|abhDQ%8iEgA~ep=1_Co|+; z+d}2OO0mY&j$x%O2Z+D+%8iQ-o2@W!#oytmHI_&@sWO!)#DLn)&YvY_j!=;(!_6Ri zUhu&#zk&!YeEyhs;S0Yb3T(_TdTgo#3Nhs<*=-7cE5hu3_qd_YPp<^)G;NF#I$Ih< z`WT_Nwpx9X-vHxTXHM)qLHe0LUt5~1=;M+O*`%FJ`skb6YBKHU;QxpTA2zeYNFe2Q zcdP?C?vdK~)Y}kyr_UB}yj=m6Ugy>+AJ~B0j+Bez{xooGDL7cZ$`$x}+&UUXZP9V# zUil~_@jh*J=dxBk%;5-CD+zN$(IKAA_p1RkV`WBztZbmeDs`~!zA;#>5rf8IKn7u# z)gLoQ==J(n5WP@m{f@o1M$HyE6dwp}3}k@dx>7DW(N*^Usy%fti-l6%&O%$8h@WNK z`}|9at^g6;J13e|pylpjV$=(FkfrPji*XZyyX{N`Pa_93tq>t` z&vfs7I|nS{B>2oTj&SwPjui2omY$(c(^u)9mTWNofBCbqx7 zuE%DAF~L6hu-gzM`AT2vgxh2NsUD^9GScp_XJ329Lm!Q2bE|xFNq#JhxId1!#;xP5oL$dZ=OE-a*KSveG$!kvItcYWah*L-OpIm+Zjw0Uw=aUfoxC@0*2i97C4FqcY2I2B3utS&Q6kVP zfN4KAG3Q-e@JP+TwL(z>$h|9ncWILmaxIR&Un#``7tbO*0nv;;PmtVlSNWrZ>9OD2j2BwuFI0=?Fg@A<`3 zXXiR+Q2kGdqWgx4ZLW2HlXggfg3H*UtrAoau0UV)7z@lfDWW($$;9ZJ29)_YQ)H7a z%i7NO(yArtLN8yUEu7MdoopsRdH+C)##$XFWF_vj%OJjy_@<>mmM{wpW^XFqFe3Fo z{JZyMNgZXM9IU7s(SUKL-vLc!C1`WG`S84-8W@*wrcA}G2Z68-8FvD*o>K^WRX*zi z#!f5W9}c6CzV&sz;7wFORpW$;y#z$hq#Rl`Z-x2mj2kPLyQAaXz_qtMh~9G5nOj6c z1#cC-G?-MQgFdHt`i3wnOqV%5s;YH{!P8SI2ja+c8pfE<#19)BzODb_FDHoYwMF0}x`1VVxxRz`yAj&#-$m(3d!%XGVM#MK7zgat76rZ0_>$ zJu_dYtPnyw+{GgOOzMFd(m!h{NUA+Xr$AoW!2bJ{OkBLJF#RNliX$b*^EbyklRDA6 zK{7>*_!nAl8mPFyNQ*D;G6Oocg;kzbDrmYGDG|8iFdRH6CtF|}bKIi9X>Naih`#){{yB|g67 z)HADJ*@0KgbH#k`4d@v#sAf^i1lF0T`jMx45IFU>c$o?vn;%5~3?=zYT5+S{Hj}j| z_HXFL^=C9Fl6ZN2;~@qV8*TY^=Pwhg_j?58c#`^@_bT&1v_6F8U8v)&cfz+fWpM9flsV!W~!cn)xob+(99B9sjsu? zQZ#6?W>0(OK*Pe#$K?68(va5oY3H6U2?)Nw|F85t21wYKxztvZ9nC6{J7tL9#@m@@ zSW|6-K6(Da&o|N`TjE#!rFN3XKd_KHuO^AkeW_B^DjQ%+;Wtd;aY9P^)h(Sl<~Rfw zzR4V+LP>V^$MgS@deTpL>y9f-gf`tDHeZz>P`atY{FDRGj8;k>kamV;tGnZGM|xuE znctlI>THRQ`nNvZ$rK~KJ0IDQe&e}&$L3~zsCcZ&Au*l!8{C{8iOsBHK>qOJ69qRV zIJsSTO`Nkc3ezqnC$_o5IsXBkK4B)Xt9<)WolZlx!1OH|a&N7ma;2VM@I+mQXHezC zH_CvWQHQ2`H88&Ze(J7n21Xtod@8cb1xjBens@R`K=kd5s>92zAu{7s42O<9zQ2_J zx&5I#XusvzHXr8zuaZ>uhTL_>#{WX50=H{G#@T7{*St&+5X>G)3nlvB)>CT~yXBDo zms!q8Dg!Ei#umBI<)N{UyJNo99k}+{Z`kEv0If}|>Mw2#l$EPYR!Uld_%xHYg4+bd zRGriFBdDM(ui@^wU;#^eyWOepZBV;@vu=~HF&4_n?Eih#5=9j~gp2JMP~!L5Y&L>{ z9xs(1ye0Dk0>U~6PlXd-e7p9xTjZYc{uY(?UWMo{cW!HH?eM^6&lk&=Z8S&k$m4(I z!(`4`HXXoloD*!TBy zOFF_rTz=nNI2AxqJnIG14bm3NI&R6yp|a=Lvu0&^C|}jX^LmFlnr8(Ueh8<-2^QZT z*Lyaoe#syuV66_;J{~>TxXuQ9XE?WO&pLwff0AnXkD)n&eF%-0y8yvPGe@ zwlUMamNB@ zZK2V9*Wcj|eRMP3ld3Gj05RVFA1SwFQLjYQ=^ycd#0uvQywWnmm_Rj?X$Lom_1(_r zm^xc`PR(2VD8&uiK8428))Sv5v@p)EAd&*!FuTS+aX6$$$iKS;w= z2usBdA{~x33c47Bnw5PRGr$@zmkoCI^x2}(MHkOcJj9=^{RkWkP2u;yeka!;20rd- z_m(Pj!~M!zEG}o6<3+}j)0=iDr2V}_@1It{2H}jhG9F71>e1Z#B$5U>`_h=+2h7pp z%B_LT2U*0oxWw!GObQpyc!h{>w}oJH_6V^;6ZojemD}0ni2AFGyKWsNzJ8@f#@2PD zU!<*ZB&zjKU2RE5-5{B}^LcgqO@s*1Yu6k}gp5B{BTqJ*$=k1u+g>DcZ6nTD!+EYqu4Nk>~cj%K&7z|8dj^FhW|Q%$bUvg2+~EGWz@b-uOT7C0Nj-pVdA4oKUs%9*yIi;FR5uix3%ojZf9ZVYltGd&A%982BOUY@Cx0axS<9xvE;@HfGpx(|0=9 z$33@c?PX!?@V$s_J9yy2-F(sjW}(1QVXcZO2|RYN@n=0*910wa>R!sUKz@l z+)!espmu|mGrl(reh~8A4h)(^LK>b>FtKWJwc{6aP`19B>1&~mx$+*uj+-5zExIsK zv)llxQ&a~NDa0RIt5;x1>gI#FF+to%%rLOxz2aBmJLa`-TWdx1=I@6}8uQ3=7)cNK zwcx-+zU3x)dtXtI*C4uS#FGh%=W9}u%o&(JZ)7yA#=svw7ljsctsz!zwr}UBWhh)5 z?{IyK0S0a^?|-}15YL6ZY`IO&*N|m~e8xv5PK&vn~>|UVx&&>nQ9-$BjVqaERVnDMZ@SFLeh>)cKA;>y79$-w%Dn#+)Lw- zEtXCkj6VBC3VOaN32J_|!>CuvnTjeT?tO9CrToVM6BM6K{FAi8-uM4izTBgSX`#5^O=1}Rgw9nxmIZxNN zdG&8{N1Z#4zWM(Fj>!Ei*!Ix`qZya_9`Cir-oGcW;!_j!ml8N-vPuDmJnNj(=DfdD){ezR>_bY}{@QJg!)2`_1?J12^Oi|Mf2R zr9E)G14UaOGLMoWS#!bB6m5U01yT$h@$tn4mGhn~Xyo9ya*mzM0sL@Ms%|9tn%Ae? z71zmqTcE;P@^OKIjbaasXGolB(oYihP==1(8DG`UXh3Kv^GPwu^CRWrVt+d`As?ExvDUd_mQ7@&E@tGJha6c}D~uBRo@&>shR z!g$u9N9pn0xhh92dbT!4g~txd-*Z|86xx95tELSS8WtED{xl|`mIhakZuHzJO8k%c z;VX`6y{Ll|yJ`)}_z*%4aL%XLf~&P?CIRN4&#pP=zZtghgS%Jco2nHCclOV<-=l-< z;?>TL7Gj`%V`24w18Ue?D`q<6XaJ#GbCeF>af1yy+Y%0V+96G|Z&Afs8aXA;H(y&} zivyNU(uen(0ayH6Q|dK0^vW6#;r}UzZK5HM0~ckn@muP=M6VV;YFBL4OWPqm?DzfjLVGtjlrsz_SHHwanc|AE~16@b;ZJ9)m?l_qp`I&PA=2z%* zYL$|EqM$EO3n;*)n6|!DLLW7xQyn)_sVEzLMQB|V8`NBt7H}f{sdTy5o436nehAU~ z8=QB$W8j#_UE`g0pdZk_=?8}qw1g@6#csBT_ERhb^C8<&(}3n@pnCIHN*F^&-CR|iEk-cLp?Xu03wg_iB6fiz-hOZH)ncH zV0W;6mF`apjt{5jkBO?muKq6XBt9FMdw)Y$OdX(k@0Zo7`R>?Vw58)@|Z$$kqTZTJ7`c~!a(C{F>)8?R?n?#8Zr6=iQ z_ozJ-a|QP}h3OFgZ+&f)lOQ^NJ7Pa1s099P91VxNmt#IpVA-fK1>!1Ojx0Ovh}ijE zo?fE@Qek>ej$2ru>=)j*QpZ)Wcj0zw)-7QS9S9NLk*R^3-o2jg`b+xPpACC$4NRak zXlYq#u^7ab*ZEv&7e+SK_?14Y19goi3WYXHmY~2TZT_c^)GcM#j(GAZ<7T`=6XYU& z>#`7|0B0+>yGBZNi-;YHnWt-a6&gVK&3Udtq6?YT8ds${koYxZdLr6{_@*_{`!zWq z0yPS~Z%kxhge9G6yoCmbq}3-Ah#&Ino2g<4k_Y{0a+v(`k=(14zbm~ICaeO_)3O$zeqfw`lYe3CEmy?^E9Q6<0Uei;7O(P5?yX@aN9tZJp=@+o$zdRN5vNjLznZCAI?CR=D4OwD9|BK{5Mo)fR0>)<$J zuk^uDP2imnWskBk!`z@pt}++QLA&a7hncbia91`AM_4Fe<%6m}D@c7Fj4S@Olo>*_ z=+Wo*Z%|QYav9Gp88Q!E`>Q-*HwC19)kZ!Anj&PT@^Y?O23d<=+TPi^VDWVQK@%r! zOuUiSS$s$nLUn~9#Rx+zL~G0! zDfntJC+6xZb377WUP(4);av3OC03py{5$7-MEe61hV3_IA0~6H*QHzTpK$j;?GFp` z!kMlxed&v`&PiSLr0u<|l23=o3;z8pnM{=QFkG~pc1N-5N%ta86O5<|IDANh_zq5{ zJVX? z?cA1X@RNpB3u~`_+GLAyy&vm&CuwlX>EBfPE+&R=`FY++*bdpMg{RDBhwEhDT{ZM` z)I(X5SHDZ;$UW8lGa#RRp{^@KYgA#@1}lUE1G1OM91LYP_xYvSx`?H3nLZlMz~YqN z<8hGWRh{Zy^&bYUV(T}^%UB9x_#9Kwk;JcB*NHXJn=MenV0hg%Q!-cecBOXLfG+V} zYw;ZRGeMD*r`LqSNdG+G+jP@3CTi z-xE5T&u8$2Z?gmZw_~0!hY7E@!eIeD7HVgm_~ln~)N3wf4Z1is(U~RVWs4HuN)E4LSYtjd{e0(H6`-#5b^MWN2Ssfsc9ncq zL2i!nezWr%pkUQl?~XEiIDPoOYPTg5#%>S<>8uO3uY8uYBi{oZ&jhD`-(>23}#lCOv+rBQw;V2ydZbYLNA<7+*z7irXkcLuvNRL*+fEhG2mM!QH~Pu^I>_qiUH zj(vAMs2~87M?D3%5nZYN{Lq_MM30i1`|p2>&V(K7t&78^428^*dCEM`^D;l?XCg{v zCaEZuB2kJa5tUF8$&pGDLRqCmNs>xJh)|Lc67}x)1DxwRSN602d#!c7 zhJIz6a6qN=F`*wWxq<$buQCjNCS-8GyTPt+3RV6uHP2bOlJC{7oU1z&p!44bu8>c3 zY@k&B3^H=YYKy4dX}Xr!YHrIS(?`Wuro8qReI);N^_m3hk_tRDzCX$u?}?Q&>1mm3 zoKR}ePV`}*7ucQbI#VTJiwT;RQyfuNFrn)BM){l_RG5x`yrD|^ApJYd=LuKk$g1$# zhwg=TXIMP1E0Vd%?sp>^Zrnh9s#Cqy#SOY=63fNf+@W7+u4q@TD<<5pQdO4ILu;OB z6Q4>a)N9S;5w{@U{ek1bgO{1u;_Brbdzti`XLAn?J1am^LUyf(%(#tad^Ww*#R#_y zCd^H4V`8l2<98dsxqPH!qs zwe4d151C-VYwo)b{R8Dor#B#({}3Yylj_CHuOgQ$Wtqu)-G zzN+)$hbMC$xNrfyKPziMPaP3U8IgChn)`T4m^CDvG_JV%*^%T@qj|N4Nxn80o4Q#f_<_-2q zHt~W0>b1t74G!r0@bbT!4pl5@ySmoph9)SeWY?%RQ^DI|tnAKxI_kHsI{zk|1{CWN z_`8+xG~)YcW1Ed&Z4-@??<*inuii~xmM=CBGwfbx&C*F<;j{ks+%Gl}FIMU)pD_WE zH=K^VDpUw77LZrhcScU;+x(UHNq#0n(xj7cjSnT71{`hNv7==Dm8;&>z5?5-> zd~lJy>S^~TlBYGWQuJwEwDBK2a-mY&2E;QgyuwJH#Ni}kEM>PnOt3yZCbh*2xcz!> zlIsCiUOW9>g^dnN3%yN?r_3=UF>dgpuq@p3tkWuzaKoubqr)PKx>!|w&^_~}2PAR$ ztkevbf_e8eyWb9ui1n?h$)hX~cyapCS0)9gFFH_HtR{15;SwTsb%*FkE(yN-B*$*7 z{F5zO0u3G2mR7YI!beS^xNnAD_;7eOpuCa+LK^F-QsHDSy_)s%1|ACRxL|0ex{L5W zU3>du$-QnmG4y`+sTO7=S&ZgvCV8yu<6r%Rn1K1Bw+Dil$XT<=_qYJbQy&vO*?vF_ zUd}aq-_uL{35oJQdHHnFe+OGez^n;&s7vm=wT;|&;omgp&pTt}BYEb^GYsr{yG!4y z#S`R?UMj58vxi~a)wBPaGbDM&q?(1WLHVlNocvSzP;lq`K^qAPuv_()vERZQ&y`tD zvZyfe`M7UP^=B$3nVxHIHBiL3ODi6h=$K*e<;WFUN8PdgN_yeNX8>dJ9b5yqm9fHI z!PDoh13cln;X6#;=^1x_%I!pXU@&+2vArT(V26l*GLT#XUHH53C=-Dm&lOb0i(#^h zwk>O!5dE;h+?WVtIxfwBq3*LqIsY$nxlv>u6-QletCvD{e?uFV7#ddj%R0;CxnLTd zmKS$c4YoaB;JE+94cjWJ`e{^s5aiveouWa-?y*Jhq)U#F8fdBH%&^6-vvC*IIqk8` zz_?Dq#}<5xIV$#({wOv1(&p+%I+()C`XKR_85FO1W>Idg2cv&HdR?1ck*c_VI&?+= zQ}Xs*+Qc9D1F&X<`SLNvk&|0h)9 z?E!1W;}nBNz489*)uSdP$0Tmw(poB_4gztUiU+@~LEf0NwHdQ^5S{kRCN@`)fYrC3 zYPtzS*k@PpEpb6h+2&ifwo#$1iaETihY8&5bj1@7C|FWf6qkI`6-!4PAGt4)xqD*g zs`7)juyo#F^Pm9*bUsd;y=?7@Nj;Qzmvf0f#lUJZ^_(jxtchj zbJNf>S^+zmH!h~0rvlrJYM(svE)2*Qz4ffZ2I6k7k-p=sfPyos&#int2>-8lIA?|d z)DO$^|7~<8{ab^mEi^6^(RqK~q?-c1o1VRSP0@jb%Nnlc6g!Nquk0}Ba)RTM;hgbn z-7q_0|7!OZI;heMYpMb@kmEqFT3ZLXPowuFv7a!*71H((lC_z*l`;!6tKsKLRZ^FJV)n&2L6Y;ztYhT6Zuvvz>Bc)2P^wm{ zvtH5_3Qq^@TjF!aF89EA`SaE=5wt-;P2Ctk@7B{GK@+5`rFh8asUb(%)`V+!EI}!= zrohwR8Dd+Obanb|N&eG!l{bqICNX|bnBO7wT5{P>4NH9(eG^%z|DQAZ(wB<%ce$h5 zb6b&Ia?bgv(-*FadlUXEZ%2-tm0&JzC1z)H53`?sy_V7l|xF4sspE{r7he*EVK z@4Ox?hA+`kV|#VtJ~MZ;IJ)nKZWdr|?ew`=nh8WFKX($HUIU#x$2|>d$-QY^e<-+( zaL%vPX|v z1{JhyWQI9+J0kO9wq0F6nHP5@7M>w}h#l45Hv|X|7Ll;+zS3V+$hNy(Swi|r8GXw$ zRaqwZkb2MlKXyB~Aekv%>P|t3-572Y#tj0(!wNmw#Iv>MyQfQnJFsnkv2|}f1-T6j zBl2quaLBYNK()sUeWwbQmEIenFXO?1+oVpL+~y*b@Ra2EFY%pm4SjaO%ljvFZ+6Sl2B|xnB>E zvC^gL@kUdK&MQAArr-tT?8`bAmxvEauVVV#Icb3EpUOw;=NkQ0ZyRh2vjDlaUqf3~ zaljSV$*{9bCieC;Yh8Ir=1?o@PDJJ~K-2%D_~I7AA>SS6S!S@o#D@!I71vF$J-F!I z^Ou&mBSHD~Q(w{#uAf^y?xv4-n0{XbJu{4oL9y$9dW72oQ^?|jzP2akam)9}ZXmpT zyy5Of4dMe*IL-Ok;+qYw|8AMBN$Mp>i$QX7;p_1G1QkBwX-oJaaJtR{et)x6?&GB5 zUaFo?c`O}3P{#9F%c4yR?Pb_tl>>4V?r)SLUX7l6tfD6usIW65#-vG}++#PS{RX}f z-fH~QowTdUkS*wQ`vzI9Oy#e6zCS|^Rb7rc)u_t@vva6_zmpl-rtzFlIz{H{QYA`j z-Wb7@j_s!h6uv!<&ldpdX@*>}J=c=-pCOUL0{jM?pr;GiM z3n?BEUijkCA8Y&H-l%O{I=PjL^aBn2H(Vp%5spE=vtM#t@W8#HJ!g2xd^4oyX&tXI z&Q-j4=<$V#1qZ*p(4mWC(y<%#tSSR!+4`eYHFct~LjP{E#|vwiJ-E?lF_`2$bgVx` z8mgl4*zBQzPyhkv{4aO)+@bccP0EHhx>&k$ox(m1GpxPYl6+`R15ZSRwi&rofx}IY z#qp{!JV}zakUM6Ab736v6^%4>XWw`rgS;nJidgp)_FCY<9h%Yk<-}8T>iiLEB1aHF^YM4lp26^h%iIo+cyTY`=DuhVRwhD0OV|#-!PcFXzszfw)q>cd9G( zF`@AAdZmZ1FkDNsHojtr0wr%;o`(?6h6nEvb$2=(eo+{In}_6m&sYoE?Un)Ra@fmN z(T-63^hJb5G2vw?CI3o9EkXb2?y*F2uNHh+6R%H|LGjHyw*!wE{JW4B9p~bR!&fp{ z&o~%C+DDcpH&!aTH;#n#M3KB^!VKF}(x(_z`~1qziHaM|Hy!;+>OZ(*<`VvPz7a}- zvfi$DhRV&-(`~yM7_r0X8s|9SZFx$Jq#B7&>S0KSyaJVMyIA?v-@+VwVpngB=mB6E z*gX2*3G#Omk?mXFV+F&WZ6?yj24H8g$L(4^69Vo~j^|gCypd(vDzeWKGyW?aiQ6WI z!{<|je=Fz#%c7D&u-zjYOY5F-6%QAjOqb-d7^g$hY~g<+pY3q%KvTPRi4}@y+@km@ z5)Yr}F=w4O`WW|J{Pg?-FL>tW$d;+^feoBZMFvb)!t;8uTUuE`*SxMwP>wz%YPCpm zTQESTqD(q*gbKd9cZI62BlU)Ne%O4OJU(8fuYd5ZH}TkoagHA|hbZ4;)$DmBUr@LH z?b4_=cKK{-ID3-<6QO2z>=g9zXO`DwcL)XJMcac9vpQkfS?80*W5f^C%eLyyePNIN@*V?8onsxoQF>Y(zYioNxN+#I)_)kbe)Pf8H|1<#)a|S^;*L(HE@J(jf!f zQ8(XF{D}vsO}4q!n0Vm!K3J1+jRGYlY3Fj|y^vL4eD=?M;ydWolslF~9?()3^1cKz zq5En~#1kJ?9Il?{taqhAoY_Rz5ql;El$q{RIp_iTWq(^j9=L$&%(#asj{~s9d-mL# zr{VDH!0Y05OmIj~+nh0M1gv4N-$|sCey0)XRGug2W$OMI-nlASv9(GE>a z?8@l!`&N}Ub$5;u)jl8VQ&X{C*vBa z4VQJnv6gkFSIZTj=QVRZY^TD4fuWkw2GR$g@U#dbIf#P6-J6?>Nq=xbLNsYGze>jvygr* zV@{Q_ns5rs9wmqJjo^@@oXzr1I#St3#+uBPQKpS!={DiO-QUlaHbX z-4PJ>Rn`M*?!HaaP$qpaJVZ-wU}C!<>+#o?o>G*1QOfqozWEDNxm{yhkgGYgehA|np-bB!0VV~lkf)}I?o>Z5X zZz2AH)k<%<_DZ0C)q(b}P0pCh5}*FpoaE2>vY$|XllRNuXS1WfEg`wcGVs?Kk~7)M zrF5u`yz2!9pFgZnz=WT9azkTu6fxX*{KQRD0EloCw{(S^N6+>z@+o3k`=Qk1Kg{8Y zh}fN(d1sjQ57HDBARbMLd*5?}>2UAn_FgxVZ#7(Xqq!=V+~c$@H@v&GAY*K=nS=j2 zDBG6xN+*Hv@H`jToPUYn$|Ct*uW36tTC&;r@=Gc%JT`aLB0Tfo?EKbigqIcQtXCYk z>I^%^dAVYGi4UMCa&D^82@@V2d7XL~&^_AS%=?2Cm>=Va+g%OV-kP^z?LH^`k=Suh zIGut8IVP8#0*E&xwDN6}7x5J3x?~=*B_5mp?ms^J$h&otTQTh_@%-u{v-Y$HxG!BW zK7Y>^!alu-7R{rB+xrx@WAPK=MX%>XUcgg$p5{@L+~k9pRYwUuZMtbq9_wjnDsDk$GX5 z_|jcccb87P#AcJcywoL*7P&qXJSIU~V3Q|v&7!OC-jUyrWz~tp-w8h|z`^x1){5k~ zf_H^ouGD}|_L*~OLkvin_K8#?d8;hPw%32cXgGWN`JX9uQy7q~TDS3lB1VY(30~Q5 z1C~B)4&F>75Z{0C$k+yQFP^!|_g}sqw6wFeh4e7?#wn-(3h+|jZ z8*hps^9JP>f!-)&^2qbb3K#S(JkKjfzMoZ9|2ZuZPjilc_A>XZ8s-%6-LqD+!4{JN z!S)-3-%>lE*H}sFkFO$U21b=3=hv?*Qzk07D03)C<|^S+wt7?tNx4Bl))hg%1Ucd> z^LSvXqzD~W3-$Vw#9K+0n9b>sysLGpCeK&W2TWIa$}vND6y=dFmMj^p8@QOltnV-wUaRzYk7uwDqv4<;(N2S6Z*kN04;PQV*O)=x<;J8JLJ+Pg)c9@NLNO@;d1B))% zLmT&X-M9|Y#}cqzp665n!)TQqCvK7cW98n4WWqOAa9>?7y~-2?KCg30TS58+m-6Hp zgq})D<>7okrG<+<0#5u->`+N{fAlVU3hb=scz&vvc>8uZ--<6_ATQmnG&rAdn=8Ce zeEdM>BW7{SR5k`AEGI~9I7)oA*EnTGuIi(>qNL$keq-2JtX&;{5>Wg=TP$mrJB$|n z4SxK9jukOwBGepp5I^mm_LN-}lU8n$zxaY3u`F$>!Iu+Qj%8QA-=KoEdd8ezXGorD z&nZ^h6Q0;@=+JF`&;^W&V2a&|hExBZt&kg9tS?^r{ZZmh+zgfg`}1!a=MX;cedYX*JaB~{?t6p6JnoVN*vllg^<0MQXjQZL5}4U`|KeB z!0Xla4J*7bs@1DimgFJab3Q*hCt?Z{mtQykJgpCx+F2t@y(xHzyZCYQGiOw4va&ee zMZA4`Pki}7=D9gm$~;_F283HMzLiP5<;oEchGzIl9yiv6t7BRiD0*9b{dAQaYLx;+F88LSQjzMQC9&!Ujxa)#U&pm6w72%Y%$*BIUw&J=^SQuW3^-rABpjH9v6XHhWx z{AGUo2{&-{Fx=5?N(1(@7O&2ebJf)@v}%y}2BHJ9{J6Qvy&ZplS6bFcW5s3_^AE4r zK$ynf_tV5z8sg?TS5u>lQweezT3iMsPoLoSx>5;qx8Dt@i2(<54wD~lA0jx#Qg4wt{ZL;Ni4-L(^k?E&uGH0g2tXT##9J{r3FV$^6>Zm$pxf;cP!NUn+x&u zvHql8RG@*t8J0D{Z5l9dA{%(ogNk#*>f?qo?wB^`$rNNA+O9`gK?Q7jqae)MXe7vOsO}m0rpS zndc-vKO6Da32(_OX(l~)#}r!rzlZZ4Fts3BvyMZI@CxBSgh>Bu%4}1Ikb^74woYu} zS|IO}0Y43Y_SF!SsN?~!8PM@SLQFK&9zG|$*I@f)k9I!?Qf^9Ek$K;Aux+3M4on=N z?h+$habV-Ki4R_&_3KA7wVQz%P499}ns~w!$4sBVRqjZcW*J$@;Rs_ThYwwdAe^tH z+nx2NO+odPCg+A+XAoO^o!@3B;Yr2@e*bq?69rbfX$h}g15b2WxS!oOLjAqHFW!pK zu~XGLZ^7CHYd@{!nibZ;4By_Z3rsDfy4JWly|Dr6AI|Vcil)R@A9zth!2`xV2|E^K zJHrAOk1$7!2gEAOZmi`rfT*X>awACITi}iSv&LhZI4$V)?V`IUMn6iPeq!kXMMv(v zn`b(qKvniGRVh^jImY;}WEE60yx2W12gqBRwQ*61_>;yPJ}KAefM%g^(C#UHH01Yy z)9p4WbY6`%=R(DV&4Yr8gonsh-~QrS+yDCwg}h~3gn)yd6_-c)ncS0)-6fqE$mz?D z!emcF%vR&)JL%*-X16I`uG|a?ii{4)JhunA3RZs!6G3E=w>yqrL*Wj^3y7dd!xNkq;zM9$8kDJ z^Io_YFhj#!%YAUlZ$v)`agb^*D!#w@<)o4Yvyp^(!Z&*Vx(XG?v;E7s{;N#p8u;b zlZt0zt)5=FMLdt?E12B3e%s*F{Pm0XL{V(#0wbN|BSzL%`ghJblJ9o-w(*ywkLDGB zXxm){aMAYLdMulQ<7;2-h_ZDS5=Rf%I%?pQ} zu8MYu6aGN>%YhUF0npQ1D(B23bMwEfp#uk~7;xox;MZhjNXfR_(K_G=FB&dS?;`o+ zEcPN3E7D&w4nUKoL}$!ktFd@vrUAdB&+Lq{Bl%{AYg=a9I`ri#8)N?Rgn_mJ{tGRF z@bH)1NdZz<>)xe|u~({q0HO8`Q0Cg+xX|y4hlBMB zARLaTL6uxB6VyLwf7z($j51mAj)PSUnC_F2;aB&fzWwO~=eh&Y;0xwiuD3b@fh{C&UMA+bQHp{Eq(R)?zDH07$h8 zavWmC2N!gHKR7}s{h^}eQHizi`s&No((6gTwr@7B;Jp)U*kWf?OLDi8E~DXX{I;mP zb2um&5cq5A04kKM_HlWAmp^$yYi zI8}V5Hq;7yyF|aJS2=;p1_j4q;wxl{989?tK4;@Is$@hZyh~E{13uAfOl+gxew1D8 z1pZY zKO;Gsu}xya{d!DNe}7u@^E=7Gg!)iyy0xetlT_P8GN$YYh>TS%0z?)gkK4 z=AOzHBh0^cqP<>>@I3o&D%t7jfOw_WMLs15&^>ryIPsY%^u9N^{D$m3xOZea^5E$(8c0w&%0aH?WL8Y=gE7XA^+0J^Q<^BYZ6m=ZQyGvg)-=5^`MbV#0HNT=gg z4W})}spYR@KS99*sgBp&8)%qt`87Sej(AOCSC^KMTxHy&lG5$Z>``^qrqgzZ)}d*( zT--53Gt4+KsJ4ORzf>iRzUF)){d3!Ut3ISUK#Q|_MEEH^T&gHD;k!&Yxno_D6}t#G zzr^{@rmwlNvh!hUr2rN3zZJCFCwhS|Z{06}0Xw+pAvM@fbs!$3qgsW+ayYdu!#&~( z`7Yeo>G{Z^fbKm<|5A&n@Z{uhz?WDW>@E=4by2_@vsJ$6nT|U_j=qGHpDF26zE6mX z8X@&XxI@ObNdp|rm&&bfbHmQeuew0`}F!>&4dTzp8b~nJ(zflPy8s?yW|O{hWKhflHWt#_h2y2_DJ9pR#O`n*)YH20^zNUKRCr>(p zO_Ao_HgfNE?~YpJwI%t(`HQ-m+B6JcQ-AiBt_q9i%f6ZHVxW+pU7+G=XY6Jv-=4Jqb=GT9XVMO4#V}nef-?f2)M#lF4~sI@2n2 zNxswJc}KxMU8LkOlE3E|;Vq$vYEhE6kbYsgl2JxDKCQ1kcidc{kj4F|e61^L`_In3 zyiYvBbMHo8Rk&l2S%>LAPC9N*;COWO9hrCgcl`Vi>x08bP3Pacn*!(Un*1CM5B#-x zx3NFs ze%W^taK!;PRW;PdyM^@?HaHu<|-PeD6NZ-2mcVx{z3Q$@U+PV}C;feQ(qwDm@_wG?m6NQ6#@BUoa z|0Y2Ul8PdH>nBwqY2}g7n%8DkP-2N zuM08uDA$MLhF$CLpK`}G@m1F8!Y;_or4#q@9N~aA3I`cJB=@*C+s9-+avwa)WY^l7K4A{0>TATKcbj7=+g;8v z^6oM3oE2)4b{C>TEL<7S0!R2RTjgS#J$JhE3-(~Azty7>< zPzc$V;N9b-_S_X9?7>oEf&u=N%w=e}+hVExq4p1H%2;|u)7f`}I-UqV?w>Zsfa0l- z?zXY65TxMPTHQ&9SY6F^nIu0a^E`^9g5(R@=7L7c1$}Tu;EBaQlc&=T7#BPGxXfo`VVuI6F8ZA!HNqfY8D=LT3KV;lU46{j0kr>d_9mVL*50Sn6s(p z$i5WHsD$Hp!Uwi{pY*>#_E(tHFn)^>|CU0#z~K=y;Fc>`6UwU%3rhS(emce&@@z3J zjo}2pq!iaF)Dger{WwJ-uyeSOc$;iZo0j`@K)K8HFwCk9-^n3Li%zy z?Nh&yx}rem*1hu!`k3+Z^~k9)Plz);m^=K;0E$_*-bivZ2luM0P4*M+sAXKSfkG#~ zZ@<$byJStkzm(1F8tLN>*#1iRcg_mWuRolzU)c(`?#OOyFd^Q}F1PB4_f!mRR~>kn z?S)F>mtNd1b0FWDtGNDxD}vF~mZTa($hCVioG0#uW{Q9BxYLL~XkCb2Og|0RmG>X2 z`Q(UO)gq;KkUq!y@Q|6{MUu-AKfck(oQBfxQ&LY1(TU%DsY_Ja3rZ`aw%?p{##6GQ z^|SJ(;L=59-XL|q+GEKK^LH-T>`|OBlg+>uB-}*k?$C`+K-=nZ{*x;40q8y$-6BJS zz44c|bckL-IengL*z# zm|#d~N(g(e7gA5h1r(C{&r{JoIMJL23WIktSBkk{Qs>*#0tK?T_vU{d!dc$f|6*H1 zH~H^lU-#|%eajKVz3#N=GnB#qWb9iDlCuZXy}CU022c)rYm1JPoZjvTI~O4@;x%N~ zp4_Spmuvm(&B#6au(NZqkn}GOc#AS`khx?Mt9XbG%Wxxg&l!CliV$X8ee-6n$r*s+ zv169x2kAGzmz`=Q`*y0O_xk*&37uhCcI8yU^$b5gdGRiwUG7ttsCp~p`r38#LW~#L z*Hc@Y91C}Sz@^`LG?eBg{?MXmN$eL z*f!EJSyOI``Z6)ws3ezdbYt(#xe+=F+*eri=d?k&JYVOgEGM}7aMQQdKb%o*(&w^9 zxF_L5{doVntpiO#X_fjzOc>rcw2STk`|%PC4{mX_LZ<8?wu`g6a3PcP?i|UjX?{JL zca`j~cyg1f)V#q9Ek8txCSP)aK?oIgIp~c8W!z3DiFbF{;Wz92K4&OxwG}_WZ%1(7 zii48Xgv$<*b+Y0leZ|BP4|P^&Xbs8~NcD5YSoQHc>s)Qo@}olumzM*qJJ|e4r@+p!@fJBoFg6}ka1|zBUv2qaJ_Ur>4T>6H_|TVIsx@tQG$3H;l9r3<&Ez) z0mldQJuej~NFNxxdWqx&MI#E85*i%|m&98}RU;njlB|fl-4Te$?5j3J{4cp;o z#(-o&>1fqulEaE>J{(KlV{UrBuQt4OLfxc-&w6VdK&`mB{m4IOT>CP;UGSYbn$`nm zeIfUK0PFQ-=Q*3Pbw2qmJ6s{{l8~>=ZaHMpxgPH5#SU^?#}l615QQ{$W}I)R8(h}^ z(mt;Ge~zCv{coodXi8q;>=Uqnh1hMrH#IdOVxNjn(nW2ko%D)se!dQ)Yn~3-ce`Np zTk9{oGYC&`yScw>k#JRE=k})FAbz0=<}6DX$*t9-UDj42?-%XU2cxwB4>)}vHGXY} zAI`*;sw+{D_s!)o`$aRIMDQD9wHu7G-YuVc8D~gr+jPtSS zis;K5qqF=QeCG7*BHiGUK0>$h!p8w5XcBF7V7`X?`#9b+xxj4s~s{h55ch z%ELh_YA@+Jw0k4zme5-x=rTYh9KOa?ae>wq#Phtx{&%D# zWOJCyS^gwmGR|*p4OOPltGxZv2;ovQinbe^2{**C zUv8G|rb_`tFQ>YYeIm#z>>y*M27-EBLFe)a&;PP=#~My|h#K^<_YWsLHP72IRbnKf zd=;^8lhneb>5*&s3$l2C`Rm1=O2YRrk54sRGr|?MMdLMv4B~gAtFq@2@0a@c^@PK2 zD85xN`S)F0;A*)dbe+Qs^mRyYtKs)~dtG!T|Y; zg;YE=@s+J|-T@C==2mkTSYg$*+;oRjSFp?wm08(l0lM0j>Op+cxO#ocVa_44uS&Uz z)j-}A4YpR5e75$+$<^mBnax?DXQxn@JeiZ$-dK~<@xlu}G+(gp3M72!H>Gij3Obn5 z7bbNWUXZexy7VWP3cD(=4ED9zf}Egdc2}q(9?{Ne+Mn)@r@rY}`*hIImv_BBtw@37 zF&9+39?OEUd*hG5LnQwgB58IrpxGv4f-S*--x;i3ol3TD^1+b4O-&nY$lN%At00m1 z1_GW^49}{2!hcyCE$OOsEHOAf;Fn~HMpxypJrg8(gm!MRkQzCtE7*19TNTNxGUvwO zsyS2~^M9DTTLn3ui*@?QFrjg@UpSS#Ut3rbZr>kr0B8GrZ~y0ZDDwH%?2!^@sNfT7 zuie1}6%P1UHc0$revETR)X6>PDzkzzY6I+Zd2IY#42+x(FqNMoecBK6{pxfJsQ5g( zWxpetBR3^#?!BOhhGw^xS9~S!VfiWddNFM@jEg_ve9ay>BhN^k{Xq75NEjq@%~4R* zcuab(k_s1c>pECenP?fp^|<@25q3X4eXDbp@DbkYAH7~BidESYnj?eGP-k&oJ}`~& zUcDS|lh&IccUscmk^|{i?Rwa*Vxb1zwXrr6;i@2Nk={JwMZBQ)r&Dg6By$fkRq-W$ zW=~n&e@RVT;?)6fyqCUEQ8i6`Yi~aV$;2o?Lh2zIoeNZmtFNU=NRoG=!HUA= z$J)r^C>QW)^;{!be*S0B>ke%}C1swAOw8WEFtO9MfWx2JI-VV|g8+MvPyK}3`6cq% z?AdYB{}Z&|d0{gh&N{P&x;_UuV#*6624tSp6Z-trdo3`GvCHrx{ePxTGtYxx6cC~O ztyFoY5B}R&-t5=&!7mT4JRKrjzS1-6q5u1AX4^j;(3v80+@M&Oc~HmhDTQ}y9%(`^ zWqFLkZUJ_k&-4zG+(-K7-bo+gKjfB(F&z2g4hGe$K5S@q!hu(3lsE5Dg^ue<9o&54 zSW9tJqFh`D$Gint8|7cT?l9p!!v_Y>cc*QbWt+avszrW~MI&si>?><4?Wt!Te5=?}LLkEZ4Z*y6i2 z-D(}1t+1Pa#_KZ4D=p-{cyd`1(6FY;L+_a#Rz6g@A2?u+RFzG4Zb#WbsoBwwI|d0C zbMAuMsIe#ZaymuPY6y39z)ybfRtxZb{P)xLA_e#wVYWN=EfXig%I@B2BJ=3e(^ZvZ z|Jkd=)Z!{%3aK-*99ds6P@y+d z@zr6vByc>gR8SdlgOJZ>x^xqr$UH~!cX}f6&u$w|k$fmk{MzQN;q|K!`D-*r1UzB0 zlGn{>DJm47E;qhaLC&f3E#I?$jPPch&v`*hTdaMyTA)6ehUydB^reX>Mm3-JbFH`v z7R!sw_5J4p(Ef7I)s1Xu+Fp|`cgPmfLI^56=nlGBJ~!kgOtAFSt9APnHE^N%QeBLp zE@o^tEGf5AN8ZtMw=as3{H;KPWeGXYFIJu69ynxA@-zQtYF-1ZejOa6$>)K(M#o;( zZ&JqD0G?`>e`F7_o?DWwu?J=x(c7><>RY)^rsSIS0yq(u8D*c(0Q*zCM`P1Sj_K6h zdj_wFms-;|*Mal}wH!=Vg>U!4L>mjgEJZp*WW>rDYpbJ3c&39ss}sb1o9<_!6K^4R z&*bK18dA@{UvqUr4y_dZHf{V!_^A(0p+~q);MCZa{PlZjs47$C@VA?IdB5H5i+QID zLj3dei3c={n@%xM8#RXN^q2Q$iad}eBYE!D2`Vc0NNl`Ja#qFrPw>)ZMu%o@O$HtpV#Z@xVy44Mn=sReFyd* zo*W_hI>)Kqb0O{^_iFD6N`?{axXYiJTEm2iY41wLYLW{$!w8nEw8yAUM-l!yF(oH^Gdt=#{Bqt|*(BXZ*6>ne0h;b7X~^4QQ&-5)UysDaipWWR9%nQZ5}4!tOU1Wh z1HT?ilD!o|Ly{t_u8>IUyrUrJiJ>Qy4*ydjdFZwK%m?y3u;){>z#$41#X&VG;c~c8G>QwYG;)r z_E_%xowD5L2_Y#rUyt4)^@z#k3@gGVhFtyi;6MWLdClpq4f{ZTf1(!`ve#;%;oi;+ zz0EWn`1r}~X7>t6Xr9`ap`wdt-L?KGW?EvBH5cP{ydy+3PY(4I>R~p&mWP*xE_&uK z#qDw@zDXyk+RlCzG(B`7X)1`EGm{f*(g{!Ct0Bs{j?7h5SJ(d2A7w!2CLX6hq;4uL z-EFklRRbIb%#?-i=wp!Mskrs7Ozw7``BEGKPCA!_t(7Wu^`ncNzEkmuPBd>U) zuUhHgjnyO{vC_`Jug?rpgjR2vkR<#CxB9gv9kL$7XTU?Q$r~%2O9qPSC@59cr(^$t z%yFOV>(<4%!q>MU(>9X|o1VCQEV$&1y~Wpfc4g^8#^j}b{V!(N(_p7< z^T--?OFR0zwh{mAmIrBh->nG`HhbHf@G}R`?)04?^Zh6%x39+!+kxHo^`Bk-(Ll51 zR^9Q{ZqRG=afgB(+2h=Haq8(7Ap5S;%hma;#E1m57%@K(^Ce@&VrE%4L`|@?+(t@$u zSQ!P`GK~9bQvtc{s|SXTXh2-$riJ-^ia7N+e7E7IbvPNS6J)ua4whC@ax(1p;H3Xj zx4z39wX@}C%-E@fiy2Li*~tVY@7U}1k^tG8gX4A54Kc;LwAR_f14Z06rn&vmM{1)* z+Ke>u!^`fAe)xfoErmCJ{q`XBQ^vvUj6)QR3r@-NzMufbuGb3#_mW&;qvEZkzpl_S zRnkACsD)LD4wEqE1-DA5FS6fLAzQmr_$hkh32=`&={;I}({ykMH=6jzAisOH>cC+aStPJQT4Agl zWTY3(1QH(QsC>9kxjFG%N{eqAT0P!a!THds;;svnlHi5JSsJVx<5T}da$!S;1BIVR zetUy==I;!``47yszm634M3E9WF(hURsZTfm*od}RGbCUlf;1Gl;nDQtIq759Jc(9r zQOCZOAMrZbKd!_k$>Xq}0);2n3Z1a^L14ST_R$p~vbXw;)`dPjbbg(cw(27jnH_pT z+YL$myW7k7z;S7OGNeCpOT`v=|63A1yzB%!@2LO!>`B8GrC%~zDJIxuYU%uh)F)FK zE(u2oPtY}QHFjk+>1zt**XYCw19SKKQa4g(G#oE(p%MOgSYqtxR$&(q7_C3}g+~IC zj_IDaFVesiTj2-_;bC`FxBnhGh!QjgR+lC>u-6Zy3H9SHf;~QU;SghIN>$YN2KT~ zPv{uoDH%}TLiY8UHMOr%)`qC)s;AagG_t4Ksbqh#HtG*=C`*rb!<=PoaY8xJf6BD&cIMqUMKlbkdK_J#&}c70M1| zs7+LmK4({~(7_1?_KJ#&v+bYAdE~c7XntgdQg{w`R#5+wN$w76Ph=^tvR; z-rV_fzWfv~%&_2fkZG~TVRNnvFB4rMH*G3xP{AGtT6sh-EQ-LXujO*KORiuTAaQR$ z$!kodJrX~#QyA^V9`~-#@kYmNj*IeYu9zgHqdKDK4h1oHqceZ$07qv-b!3+_x3C2+%61ZY4kDib9PW>53qM zG#AkF6`VG_cHr3YR91J6s*`#NS8W>NDzWu^aW?_x>V2+V!74 zI;S&X$?a+l8^L*)W&ajjNbX{JAWL-5d&18!Ef-@*A2Z3wyY$3nDhfy?zyA z(P$}!*551iy_JD*OLYS~)op+!ai!bN&o69Pm`^kuD&I8{AM z;0h8q4V}#(j{l4MTVLXtFs;hC>_^U#?)tE|>U`kR9^|xd4-*G6xqGXPv~m1dmU`PR zM=XdCx86#)B1vUlXTO)E9-lrW&_6}|UMX>@iQg!=S^u(EGt(P4)EvI6l*I&@kz%)1 zx^{TIL{oV(#0#Cy&(ozzu6$^CDr-OCCi*x8KaPGQIgidBo-MZaa5irI9y}-aV$ z_a0qCN9}OYq+WT)D`|F6G^is?ITs?iS%TXqVv z{I=XUTin&SG4}S$KN~%8onNa?%o6bg@8@dVdBO^ML%Lc0SGmC1?y+97h!J_C9QJ0D zJr(A^w%iSIu|e(`mgUW_neb#WtLLLE4Rbmw-_-A6M`}hTPc2mv8Japlw0o0wSBcH(v9O8NBrw-=@=vmNS+a7ORkG}>1i zZ;TZQn=GnxgX3Xs^h_BCY~6P4?1x))Ji%B``M8DbyYb!Is}@f3k16$sTWSc`Jh)lb z?}Zz%1d0c5Q6_cS(cV%kYhzey3^p!{_9S`Bz+7=dZB(_-d=t6H9mdYktB#QUh56hM z8ePK4xptMZekOx(e z4PRI6_ugI+$bC&0GhoANKI)q&LU(aaWA^vtCBe!UCe&?AD*Q zvW10i?)@{?B;Vt&|6atGYNMr{U;oHqa|p@c-xjE&2XRiP&2R1@=UIO9z_+hz zsCnp{x>yRy-Jff2E}rHG-noVs{`~F`w|SU9@GI%#`be7j#H+#K(~)efZeGYZ)s)ma zNJl1PDBVMKd$LuC~%N@?6FUdXV zPm@&jyQGUV5|VGNhi$Q^^Piz~7s;g?PzEBUiNC4zA6;;@5mH&66p9TB zLd)bqQ|>$$5P|D5Aq>I;Z9LZegK({gzw=L)l0LcrS&GebviG~u@OqFL$t9g+Wdw&< zUe@-aU-v4hLB5gSWB8vntHA*&{UeL_BG-}gS?7Q17I~Kju8InzcGW|%^P6-~4-$^ZhQhBd|fbYb9?uCm=S2lmTd z`6psPI3%aYq4)se(YkW@nWS1g!7FUQSD-+P3k*!`-xiVKh7shR&LXzxc-y-|I%)!{VVTOdFqL3sNiX=&X zQ4~rgDT+iCN+?fKDUuW+At7Wf*+TDm|Lm%(GV@)YbD#VE+~GHkj?m#Ft-(tZ=M+ZP_a&ssM-B#t87w|Y&ZOnkC;4}K zqzMU@Ke6#B6Z*rgoFdk)0pEp%<4Sy}1Km8zuQsPmXic3;6S&u)e!OW$$Mcife`KVU%pW&#LntV#s>j6a&`w( zb@OjK)S=fsvW=zTd;SG?h8F`aKKT~W_y6m>#^?jf2?Lli_6c3=(uTwX|9uYMkN&OV z4b3{%nq>ZNk)@}x4vBoyFrF=my4N2U>3>$5P&)tmL@mEGXz#eylbek`GL5YNZn&vJ zaBhq3YBdYc&-p#8pd>}cUTxtgymY7(Jzy#3U<`v|t&?*fk-J1`ayXW+3f^%Y%MmuV z#3dwz3Ub%ns@QNZ+C zWmm|q7tDLI$C+4cZB!0UM?VDh_ifG?DNt8A_iCn2i}(Z;3V4dxlGMj946WzP3FAy6 zcUu#k=#>BZ<9~^Py%sh$$6i{%pl0m)%LgsU{ArW2mu|>Q^N$Btd=3`KR-AvpYCwdl z8HIPc3?P6n;Xi9RD+ow?J`Sbo!1>ieT{=h;g2U+BRC^r3lh)31v)-CasNVm^EP`_$ zO_!5dkOgF)dps-Is82e|%olc5pdPzJ&buD_CYnU5YGVhr2@0;-H)x25UebzIyJiVr z#s7&|ve}Un%Ey%x(AP@o{N0%>VNWCjw|&@Yy&iK<^W7V@_<&XX$u%u$+ zhvZd?r(#xFf#UWw&h%e2%;}V8xK49`0COW3brbIQJ^fi86|utnyC1ogSuWtMzvrv` z6=!&IUPs5v%9vuH^wk^tlt-H{cc6cBw)IWBI~)20Tql+v%3@wn^RMdf-MF8A z{kLfNo)UyUOf3;dKgB=Q#ryqYRFb#zc3E?(D^MCE_NSsAarGk8%qi3v54-p;_gZTZ z$+A2uhoT*%`S<>}g9EvQeF+XWs%k)zl#dwZ%8;1Mb3Ix`bmIG(sc>_pE?G$8S7c8> z{z#0!eP!Dw;v;JFh425|*aKPan<|mlvG(V`*={_)SMZIL#hb#dv9^JsK^{do39kNuy+@e`;k3EguK}R2`BQH0o@x^s+ z;%y@rw)UVt@vWWgtxqzCn*GY;-8a0yUI(06T95mfj8cK`E9yl5o^SdpYenp}zQ%sx znkHeAHqmnp5g;mIoYFy^f_Y{-Rv# zkfHrTsIU9ySaGUbLFm^HcdSy8!^hug-$gS7gS7m`oLD;Yw!YmO#C=kcSBn>}*d zI31q;_7L@TyWc#bSLDqBo~_(7Earr6laP-IsX9a@J!k8`GE?X(-Du5*=Yd##!DQ3g zAGM@?qtMn+R#;1eBj>)`5$%~kr-n&7@yKod{rQG6z!`7*FRg!TKTCHP&b62m6OZ)m zx)0FDz!jD9!^sj7x8)gK*@fTd-FEkmk21ttUs2&dS32=A)D0dNwI2{Uf1Rey0%FY0nN5D!pZhN&+u^$+eCu-*J$nN=R;JsX%?fQ{$80AfWVH+O z^A5bZHN*f$nn(Zfqu6_+@_jPXQ38Z-Wa@1f#Oti7`}nI8%#GexFAh8G4COEH=TuNl zAYGKN&R-h+4kJPe{}#tAhS-Ca);WWx z8V=-rZu|RiUG&9l7m-_vw*=_#wOvY^p-`nKwRE-Yq2VR}E+Zk#mke-}zVx6Hu{BG| z?aS8C(bsVJ7&G!t4A)Et`b&UfwDV_<8047n?$1m#Le3eb(9@o32Hg?vsgtJ+z{W32 z(vIDoFsjUG$X@KM*e&4zexH5l<@=0@p8Hy2g1$S>PonuqDI@xazZW*%(*edV$-ydy4XEo{yt=bl5y&)qvqd`>#6o_U>S;UT zGelK>IpRhNp65@zjl@2%iS-Y>wz!kXFOA1_KbS$eh6nF9CnLhXwfER5dq)D!u?Kq& zW6n;pY<6vzC733SYvl*v9&_=|e|yEa$?SQqow#==FMaNw`i1&nR+WOeKn@i)-7&0r zqH94iqm+#m@)Y3(?+`te4RbWUt7z;p=Ac#+%O-inoV=QTJ9mSFL0;x<{UYUtx&m*} z{0xg3DVO^owS@`K4MkHYKFMMq1?9(45MEp>7?;<)&xeN^)C)|}~G zc{{SRZ1ek*$m!#=ZO?BS!gC05FB{V)8>_P$eAWLz`2 z*Kv+Uv~zC+j&fPzoLVXUM^l}skNwx1@m7zNCSUDg|7rrmUKdN%aPOn9YqPxzIT@Lr zb!vNjoRAOr<;|j(GDJ7KA9}I@f4E~98z&STP)=YJn{}pxKGz4=tzxd+3 z6*Vw#a=7f&(opT}`@jH7gf$rs?KJKUz`2&@NT4lAp32*?Wk1`>b&h}Bb))~LuwFiGa zT(9udxViRfSdmuzdh`uU*Gf%#f2$pF5L=h?9s2~@9_2_qR3U84gzrk57U_$*A)_#C z4F(n4b$=D%9@=}-Be_fibFz(mGn+(6_W6=U+0Do^RF*LAk;OS);e8W-F@;ESHknte zqb_Xn`}rl0S;{jaI!DQ(lk==itdXwR<8t|?>DPJG10G&Ij@wqkC42dIeI)j!G3xGp zd_X139c;VQ)olsm=WwE~AMRHcv$pfhaY2)x2i?oSg)D_#R&heT6&||V>+#RjCJIP) zzCNi1Q*l{JgGX#2JoC2i1vYomoT%}5dC zbd7k&`&H0UAJ`Gl;S5%npF!_U{JF)G`KyjAlVs6Dlk`Lr;#+!t|dj@@Z?-$Xv8y$s?%h`l@8tdzDK*B}iNV`WcP$wMa3j-&CK6zKZ8 zg>OmOhD38l>HgWJO`4?-HS6PhmFcU_MgH`U6u-N7zIQ%Df93#Ve-?7%_*Z&T*%U3o z;CRWSIxox(l*o9=*g28d6!DOO4D_qNm`)wfF(kjDWuCseh3{wHYq=H*DiHfDY+`-s zMzB6}Zl3wBJ@(l>?uq4bz`3M(qHv86hzb47JCi^q_s1WEz_8$t5TI<4z&EFgLEz$cyt1nTmUOTs}Wq$T}o^ndq^2|3rs5z)W}cRU2wCAZU|`{Zb%74BD+ zB^}tzHK&-)*pn6StsRlrAI5O%kUVJO zOm1ITcRIAqg&g`LTB&-{3UvVG+u(&hm0!Cu?DzhyRnKwR^J0?>3C$9J%=G}ztESg? zhT(ZBK+Ym!+hsEdynnE3_MRmHHJb3XLO~$8@@u-*(!qC?(gV9-YZ79jPxg;lk}d6? z74;90uh-klRmO*Tan0>EeoUB4AnVp_k6jHWyb8L$sd)eQuXsP>79$@GbX%e z8nzYo%MkcJt$pRP26XXltw>NYB3oI6J-V&B6>8uPtCJ*613pa_ce*VKCa+$FkyyQA2}+XSz^V)3WUz%{k&BQG2oeJ{5Dvk zuldeP!(HC`geL#+bLkuGWiUM-bS6j#JeqIGEnrS5_?e7<5hn#C7kw}N_m~q}d>fym z!Ul$Q*)v>16hi%Df3P~)0C+DmczY_5XXi1M{5PKtir08@1dh?6S39>+y3m{yu}kNO z6=(unW*;H!K>nyKT{CS%J|+{NVF>n}MJs)nv)`>r{6;GeUr526wTOC6*L|GdzU+)0 zd?F41WQs3)iZV#=<#VgA%{!3!8MZCI8!#6zA=M%xV+9f=7fYutD1@dFn#Mehy{H>b zo$1j+j={wWp7hNoV6Wi2Bjcbux!=HIHAt}~0v|r@nWr(xr~jHi+62;I=-8Outz0_U zbU%7-#R?I0K*t*eK z6gezky;|aq+Y!ly^(Qxt<6QY>=3;mXzDJH1-j1FBT^s+#{kwL!8Ju-qo#uMMj`Y=@ z2zk%72B^ZHWahUN$uKc%zcAxO*pJ1nX{@p(fnt^=OjnQt9LW&9*W^O- zHd(CGa>9PoIs^9W=bYidi{$>YJ_mBP&(wm!_UsEhnvDfrGu?h6l@3zoQmo`><8pK@;4&(-DUGh#=^R!(-fVt$3e)Nv>L zlol{4u9(`eeWG+QzC8RLhCV>KG3&#x#DQ0)d-vUijfBg=L);qAhut@9%%?*DgxX$> zxIfb;mj#rs>@2~3ygNNp`U7(2FBB;M#J!|;M1O{5pgpLLd$3u4aV1`#6XO`A*hlX2 zWog4!W8i%eQ5^aQxxRKB3s#-T$2=PsfAH)UQWWmea6{J~nyZKRYn*i}Uo%K*&8r|!U+i5G(%Kn@|6h7{cj{ANcar$#*8LBjk`S4>?@)mi z9d^++$g&99k%p?xtPgv%$l2=Ejw$aM&X0bAd&kRmY}bISFHa7h&BlCYTl7`rQo$$H z0mGtJ>=oY@U%j=ShP?=~&lPoCf&akU&I@&Rkn)E2yh#FsRQ@Y7y62`u_?hQ}ZoIdI z7(cci*&pb`rk!Cqkt+b18cxRd)**LIvS5lu#hLhej7F9^2tm?=M-**db8;*1(AK^h z25}PG&fEFgmB=&n{!JgXCCS`u)y}7kKykd@wkQI>&*%llu*rJT_tw+JW20+xH1BDO{)>LlV2{pIfwI;R`-inS zsZW-~*1nGn7Z-qz@4|jQ_#7;7=(zH{)ENTS+V<1Z6foEOQ=wgzMikY)WEHG-fW!X{ zyUFlcL*Kx~*MAxEP{M24GM35=$xIUxFYIN>qiKx28nKUgb|bHU zhnmhXw<##cdrWLcKVIK3->T8e=Fpd{PjB_yM1u1 zQ8`*%*XE!N$*Q3lZo>8ueNp^&yM-p{-aq2;c9lKIn{#YkYvo8{n*YA}VTk$Ma@LOl z@13E)dFtRJEkly!cz`{m!WEeI9`nDY!3TMp?+sOgIntrvVXvKNjwkj#bJf}1`>YL$KVRQ6l2Jte5bMr^ThT|E zSKIAlhdKVS{YT~ubfAToqg}Qdb&Sw!#h>odiC@W;*aAEk?YtbFWsCc59)@Lzzp^9Q zSZbDao!y>{o=$S^L4Uyf4Nzww!P0W_XZbv=6kX= zKvwqkm2C9+4x5i<%y>%@+Rl8}kOWEcIR9&AoB_V)hBqHbL=H@Oab#Q0dJB?jHWw89 zR0R(EhAamm7lMtu#j7F(bJ083T+IzugtxYbH zqZT@oKWjo46}8QXMWv4XmO(2rmGV=VKUfo*r`(&KEa6-hUv}cvk^|xXxiRDyk0bHp zJnEft6LV}QMT-`0sgk8iiz_Ni0I^hFHTybIl9yria7Q=ths&ORv3_I#E#!T+0bafC1;qPyTItYmgiA5LQ-kzPt?(*u7J}rm zGwpX2+zIXTCq3}Q{fSECqNkDp$SU3Wcd}ChXhl^X;`+FMub*;WRfyd7pB#xlO5Mne zgkRLrIV0j>zHy-i&;NqEk30CRb|N<@yqtc>RkGo0{L0Ua`E=$(U4IjukWa9c(-hAs zuWsBmx#@!Ete4SI{VD43W?3^ypdZgkABv{6Q|w6d^t}T$>X<7Pi`lP#nF?m!KUf2v zqMv)pt;(if2beY|>{q$;gTe`UWiH6w7~L~I?1B8zyor_@F5fI+hRNNj%}tw>O9jXk z2x%fGMwg>j)&V3mdK!8^0=eXC#@JMiIq8EPO2_l@dLF)g`vdkxf`Cc;qSz9}SLfK;apXX~G!?=Rd}cYl$CT9bG=8@fvVqtGj)6Ri(xlv`Bsp-x@W&Uzx(`jeS z$JZ3yyJruXjYkA>cS=HRQpNjg70CaH)GkUqYy%xVzP-K`*f%oI8C-ZDb6`iR`L&ZU zKD4K}TMzdW$u`O1D|r416;Lsh#=MeE&=WqR-Ih@GOsiH1_g7->PplL1m{GTUx&M5E z3+YQNVbu%Qg2}F&d)hq=a#n3j$nm!&@t9{p=MewKQ+F2TsHkVQKk{p5fb)X+rH|K;W4XLv z%kqdJ{&-U<2q8k$T`adRBm(1YYR9y7ZGa!*R=PfD? zbwNl{Nj{VV^}B`8ANqUN04=a9dWu5=rY8Gsc5ZbAiDYMaF?;kQ2tVwhAFu%T6&bbj znBP2nF-LHfi8E}}e|m9f+!fhP4w}bC9SN%CGO642prta!{za!Y_6e!5H+d-$_0EVl zzIEtxY)xVRRc8)doT-d`*+%emw{)J=JKXPlwaB|M=8WggpyxH=Zlv=Iuh$RMD?W}M zOx$w`bFn+rcAa;3CZVaKFXBI9uC!I>tLAI;InP$VxSnf7xIzq6c#i6lyf51~1a>nZ zlY8&f?Hy{Q#-(;Or;-~vpL{l?d7~l8bbcREZ-MX2jU&4i^fX~r7SH7qjX3Z6rP(Z* zS%M8ek5SrBHL}v&J|EFF%6PP$BQ-$4gd>Vb9%t@&i-=Oj~Rfr7P z6D5dwL*MqL5#vp^kokm3fuBi^6or@=@E)-yX)nIbt$PKe;jOQ`%U zWDa)yz1xqy^eqeNrcyGb-tumHQ5ohQ?j5(-?c)fgj+9#>$o0yRZV#)eV2~IoBb(SA zR^%D$)$Cu`m+HP_`N@D4KG)uAmmlZSA)IuxYPotnh!}mI-d>GE!j_k|#0IRKZ<*u!s+xPXm!BP}nbfcKl-2{9T-_S^U$&4c zvEhhQyD-_f`bgkJEaq0epMLe@EWXz|&9u2ZCu$?wyDH81GDvY@d~Du3DmlEaOfOgt zIa!v8f;(q8!LZReV(>op^UNKd6tBg-z4nG9a9;|zU50*~_o5R0T~(ijmfVQ8#y%mR z9NgovT;!l=h(Jr?+e<>sCXmx$p~m)6o0PV=9HcUtK>T1#djP&Km^jb3JvrZ7J5c6t zF(rZb8Bg!q>Kn*C>o?!O-ro^|wOw|LV=g@M@ZSq1$X)P-n_@3%xPNY!^%;DkM+PRC zR5C8uLQVGlXJJ{$TL}O0=(O<%%IG4g9j!pU_5O~o*ji&~2%q!`TJHiJTc3q|yI@7S zuXo5LaOjX&m3Cf(SS1p>^ZVO{pR9yy)pW+&0S54ON}o?R!TpKA*P@@ZZiLDnNp6NC zZ=ACCPw9V3;B1n$JcRztI~Nrl-QOZ7w1mT?ajzrtKJuee?5tq8m|I$5+h)?d_U$Aj z*${ch#3{QD?2}RR%2_*O2QTDvmbT9$FP5K)o&3Q6$DEgf_Hl7i$+8&HbHx@qh6Y-y zZ)-uyL@}@1U(7*$xxS`xojWlaOG-LXgt}i#mU}+_`y?j>F8>fA(TaXr|0WHH&9qxa z?_(Jl<8GnZnPV^9Vo#(_tqV!msw{W(pd;aayP$82=Q*1B%ic2nEs%QjESveLG@&Yt z2C@%3ks2BIpoByJ_wR3M(%r;M=9$}={`{pujK@VU?gZ2UpItD0W#djhU6^;DKZpIP z2V;^yDIlL;W{A`HuLcRZqfpUMh&;BKW~)`N(6@78wZ)?c7-t#dc%|5YJ)b+q#a(5w zUq{DDPvo*A&Iw8~dj|(8eTo%VeewhVrXLRm-V=gK+v!KWfv9(^+%d}Ibb(}g*Cqe; z4v?uW^;#9@cG&|(uhQy}&-_KhOW+f zVVsK!#&*vieT6-~tRj2u)R6;Ta_HPu1#_6X)f=gHT7}d}{FmK0YEBFv`&PM@yAnC+ zjf{v28&X>Mh*M_~^CmC*)0X!*!OR-psT=+dgne{eUN6sv^mSgO?6X2Y$sYBSKlkGO zq<``Lv7h6$61$n@o?Qa?)CkGfnDt@)Y~?yl0a?=WOlG$3{sy=XN8;`bjzRz}N zHe?2gP&VfJm&lj?@n#+hq(L`)%la+L$Q?EcQ~jok`wJ4Y`a}ty8^_j|*C9v3Ps`!P zm2~tiRc9O&8)1ikvRY@ixX{U~C99(xcuu)Um%XcjzPH#(%^qmAf)<~Mw@EuR!QqUi z+JylZ=ty&wUW@xNoBSiKheF zvgj*rS(KQNqLJ9&l({|Mke~j(;Ji|!B9lKVs{Q7cD5mGUs&vA-Wo!I--W)f?M6^_Ks$Ia6nR(rSGQ?y#OpnP ztMC3MaVTClsS$y{Znb7^*%J2OJG;{a9P!*3Eg|nK4WF!YCHi>LcYa2->bs*JAs6g7AG@gx)(X=6>uw_V zeD7qgdZrS1j>f0y+(3OY>)4U56$L_L8QIW=d_)K`J_rKywSG$**DEcULyJ6%a8!o| zl>2-0ezDLd#QL-U02})5RKE(JKZ3qV#)r=Ks|>QwMBwSj0_vNU1)n8MSU~p?_n9tD zC$ij>m8_`eLP+zuw$uF_B)LZH-E6o6fw<8Wt$%-Nhx1D#0_GG5*UN-oPkNj{>iyNI zX;N! zN$_!TL zyeedAwI(d^D#l?-^!iYNjde5KjU*W>5ekWW&Ey*1&6=ec96ge-_;2J>~fAY;fmSw*k=X91N5 zJ0hwj?2)f2P%L-Dh%kPitb1#XoSo0_{V#oSg0q!UO588aAhl@RGUTcRv3XNVsopI` z_6UwUU&H;0zNfNeIWO{hsX+@qkEw*0Nyd7Mvl{5j+<9l)fIMo^Pf_KLxR)*%=MKw~ zBsqW5HT3t`fl8O$DOnpkm`om4vF&ptqg64EJGS6`ST`6jiG3int50ROC2qoA$z@eV z)H|5u_7*K29;c)lrk20h>p)6rbAh=pbU?+gLGP}YHW+WxzCH5)bH(%~_ez|zJOXu( zKJbx&NTxILcER=lg)UyqoH7vFt2FMxsYimZ``2H3ggTlzOUSj~n?dkLo!yZ#Gdf=4<+0n+!l8D@#9N*qSt%8L;$!Fo)Q!!dqvz zQb_#ZLRa&BN0R&X`jQQ|8;RS!JoQxH1!DItvWB1G2IV}P4WVI1gf*ditAzymjYVl? zt+JSp-M^_=IZcg-cxT)y26L!P)h{^6qzuzrJ7YFW$eY&R7xm$!9t8VI z7KjKNlZ-qBS1#k6_w-eaJC8c4x0W90Bd*{!X&~kC$PV{3rF!c>t|vKu^V#zUZHU=A zy=-CRw-`rGeB>-gUd^q{jr_LQvs>Sp6U^iS8}tWaK>+h5ilN2hBd%c1f1^9Jh7O}j zix(hud2lJaiTDe++J z1vnO0Yct{m9^w~YWaj9AWSGopmW>jGcRR1n(p?3TSnAfGyIq%rthlgGqHdmMtUuK8 z6`%LTuC*uPSivD>R6R9W9k_OWACu(3J@AJw=WpOSF!Yg#_=fMI@Zo2a^Y;-t`cjH5 zFO?#vRPJ_&2IhU3WVJ3`RT{1JTm5Uh$xbV9ZT@#$_mKrDe;<{z%SI1sOc?(TpD~83 z$s;OO@ia2gt?*7h)RH*lwniV^!5{&A+vCGS(O(<)nyXTFsWwu0B-g6M40UtSAKQLA z66NVDJc5UiJISx4!OSm2hEMOhZxD*_)!UuhC?mRrO<6^y=coozd+_;O&~fZ#+~&~h z^Nb*GSBU?zD@fg> z$?uD`AkR3D?+qz5Az9U#iUV#Mpl0omx`g>0p{I2#*WN3V!@8>t#pLmP>hHDvV=NtH zhX>Z?3m^|8^ptk8KXT?WM$Z@MX_EA7g9YcV(?BS@BYljbPvo~m=2ml?lAXcVe?=m9 zlJ}jdVCZA?i{)~!uKkPOcd9daF|rwwOME}7JjCbzhxoL8BX$s4w)V`Qeo0t45TCey zl^YTH0reACv5)Tcn7md5^1`U@ix&9j;h*76w{|YVRh_V`{92dP&+~tIc~O^mb0~LK zvbzwNDi#=wbRq*=J2uE)F#}%R36>u+HlTmv>-C^Z$bC>&ZtOW_PLRo=;WcJVxP*I} zPToe}j{TsspMwOn%T$eN-*zC%<0r*hGa0a7CpxF@0+poaPw8!Ab0qJI-o3i^-I*AA z=N)4F#C*qEm$}q!wy-a2<6~jeT~;D9-aMkBUUFo)wJ1poY*?p?wGOEh^OCo+tkXs$ z+x^OSkigypJ@$*?y13ui%rXwh@r!M#-D~5)4a>9hXRe_ymsncFA8oKFkJO@#eLvE` zmaq0;*P$`vll`ZVdwF-u8}^$tm`oiJ%@njHVr@B38?WJW*kH%LeVy85 z;L~hq_hAay80I{^X)FcW*8Aiagbhftk6G-qpO{-qP+c@b9Xiy)W9OeHUEDL@ouIzJ z{*apo=D8v?$vZU(YGXc~)ZBFntAB0}_b*LU2!!H$`r$E&E%{~9^U?+3fip<>Fa(iyX225PN`r1m&iHe>` zzTyD~cq-ppq&Sa%uVbA~jhQ;kWGvl{K`wFRV7&TM8t%Ika|1m#Dv{__zkV0lXh7Aw zGiLTDP_H_BukrdN7gG54L1>E&gXEkR@>#Rh3T$2uf3d+HxN=|Tn)h$)AvV?h!2|P!OJ&&J%z@l|A8nWCJB-mUy1`Wb8v{yZFCSN*lmzv<m^O6hCFYVSs|D%cK zqpwtm&O4t+=feGp*$ZL2jW+OU;YVeXDg~;JPR!1qM^2fiXYVZ!O(;KC`P=a?_MTgE z6;$Z}WR9udPSru3oVS?G8o7^&UtM$^o*Iytnr9A6Hm)S}L<9FX0YUJkXw8bM2tsUr z)LBLJWf%|LQKC2-!+I|Xo5n04aO6mjmw^)TbJIQ~7w-bat3y|wj^TW+xc5&h=F@HJ zYBjl86v3FiTXoH}1ME1Su;Hfw9r}zC5_`@mk(K=2gYUh~NYi1zLz!U!WXdJa`-KL9 z)r!BZ8d#u(Lv=>P7yWO(2g;i1xefULUxrLH{2kKImMF2Gi+~6A)ak@Ze=O< zC-gR5$v7_nyP0(P75Gu_-(Rwhxz+)0-wazDRANn%SX}a&hk$4*9zHY|?MyZ+%GgOO zVE<}CLtt1toiG$@AMU~&;qcqs^qv4)@Y_sz^5m5x$VCJ`xzd8Z)m5PN%|IPSS!8#r z?RO&HF|<3z0=h(ULjvXDHXEWMqNLaAhCbZ)5Zb&@*2aK31)kvae+Mrwp6=d7I zqn|Zf!M}u=fYY*OWahc{8+tkw>^)o}M%)>sPkO}g9+wsJ-eO;};~rzo@|3R0Jo2@Z zM~!x|iV+fLs4d}T3*Xmnsp5M>T=se#xAjad{2MUp=XI$MlsGG~CW`RX5oJ zGaW$|Z~9S(+gO|+{GSPw8a4{QGqVMUG_4-DJILb{IL99KK^DSejq*?YwjfKsxqUOp zEo3t(-tr&jfU?$a^q+M!Aho{r+PN&YBundC_=a`XpsudGbI9C)c*I@lSY@vV>3<6N zx>Bsb`K!Q((s{>G=&&t}qul zzi~wi$cDjexgD6pyCW2Ia18VI0ST3_7tpUtdNxNUDy+vjioO$xb|lm821}ZxD`Y-? zdDv)SGX#iS6!gKLL(tc#d}IoF>h~X=xT0!K?mRuXYEFkrqH_H@)TbCkf4Rq_Wxy4p zRprjT2tkhMVeahNI_$?_ZRJqDKqLCAwoG1EM4l2S<7~CO3Jk0Ley=F61=PVUM_#nOlB4h3|6Lb+3nu2`l{5%HO;ajpn=3>{W(A2qQwH_!tD5JX zF{gI-euY8EjU)F|mXW*^Du_;p$-ESQ>s)T;s>%v8R zPn5{^=De5m-Oi+XdDc)s(Sc9}L~>W-{<$}1p;##Q?6@$Z_X zFOFGrc*zL;QrXfoPybPP&zMhEKjkG-i_HV2PEI5)TwLMPN%V6tUwZfBoDuo^dH?J= z%ozrsb!*K2j6U2o;=&su9HCj7tM`7VEBNYf)@zHmf`swwlZ9_^4m_;WM8iCvPz-ga z9XNYam zUTRxw3`^0w#)s^&|3g3`yo`#zwOK`jI@EU)3)J7QNnk*5dBoO#bZv5xzm*=h3w;dt zZGY6F4@=~vrQ%63^wDlKXg_vr3otRg793?!C!s34^4`91CW^um!-ZOSeFe>Ldr^jc z;TPj1O`h??cYE92#`c(FxjA>#`noDS+Ch<6Q%ECrDHEgIrDi19sCeIjY*(nSH)I@3 z!GB*!>E=AU2I|DInIhVJK#s66de}8!&GcmQIzh}uf)bT=MI6+9e~cN=ZXji}Jt`Z% zIg#e)Dkl$)+u(IxS~7?FNCP!y-Rz(iByM_C%95*s{h;hke%&Ozj<(4GF>5915ch0JMF~tsoGR-Kos)RjEV&cP*0T9(T${xNRiYV z0e>pXt>Bo%+30*L1~CmUaFEPKoo&YX*Tn%_vRyVbx3CR!WfS&$bw%i)PtD(}Uc4DH z$0s=_4pZTQjnluE(sUvpk{#a=iCnKH!SUZ5cJNeJGv`dGIVtH=2tABErk3Ao+>u4{ zq;LLw9`jKRs5|=GICU2toC8-b?cYHq@P1&W8+j7DKKyIDzt5d~sbLQl#d8|B?L_om zH8--_dGw%=DU}S^mazX@h1a`pdY0!$C*UzUd(B7|&qq3e%7?o9YImmX(E2}5Jzd4? z)Pp&7A}ua5f3FvN$bHB5n(fssu_ZQSO~|J{+A?Ox#K*v7a1Qsk;wSDo+Qk`)nXjvc+uftCpIO z9F3_BD_wHHFRSzSjhHzch!E9Z-cKVrpRyhezmtH;+#}Y%FQK0Q5;m8yD-h4+4J$X$ zpJ(jEV{6)hy8pJ%FI!O8A6q}n8=h-OzP{(V;j$X%DKIY)`0$)UO?mh(?+%`S+fqv_ zlAOV^V}$MgRw_B^vAQR_3UeTBOwq?RO$gU%!&^1kwh;YLRN!cyEd&c(nKVM*P3zL= zg>cNNR<2b2kqOp@zQ*^7Vn;N{MV+YZ_M0w1mj#tDF3@{wYN<+-~p_`XkB9 z`}U{yVeifD-K$cupL~a&km2nNGvN0Cdf&(D-x8jg&Zs8 z_r<$-p!DyJ-C^Hkz{f93_#e&#TbllAtRAo+G_DifZ;vs+xah&@G(8P?W^%c#UK@KF zwtePDJ3nY=pBm+oF+~3>JCti_K>SlP8}Cx=vud`xlO!WW0;pFPuQXENQ?p=9$pcfu z-%+F|9A^phjCJB3kDbYg-!(pCc1I%n#z;`LpL)L$-Md>ZFOf|~;) z^H13TuhhSkhw1u6{g=K^`ffqI9++oN_5GkY>`Qe^I-~>XV`Hyg5`AFee=?w4#Y=9z zJ{=u$%@rzTB*V^_V=uv~XYXx9+%S)K+I4Lx?k#6UEjI0tB=EAbE(ZPCmaoNTF7%*Z zIsD4H<~CJmnfWtZA}tP|UcA2OQHY$YfXwR~ADH2Dg0*P;wk?F(zL2-SDNTa7cB{Sr zhW>xiRIW@#S6Erq7k#3YPK@6?YI)S5h5dz(Mo#=tAr{%fzNf0O_x#zw{hIT}JI*}t=u~?|f z-3Et$8exC@@NagzX;%nmdw<}Vy$kVNd@^3DZh`%@&5hqLp)STHVmDSIMO=zVa;Lq#I%d-w3D`RFb(5egNrv3Ey zJN2O4=3s=(O&g$@Jv9mYqXN@6cpay-Y)Q-Ohqt#KGl8S6BPtU!HY8w!=$oCZRe_p# zLQ-%Kp3`q-_8i8!`0xp-S1}hbXRV@B`aTwOW2s$}r$seMedk=xrX&q=;M_zBwZW43 z?OCb)YUKzo*u*3E%vz9r?`KCfGVt8J@T9^-#)`1a9sX~R0}Z~MW}Zljpb{om{c+I^ zOe70VyS(DU9@5YxpG*3f_r8+8U*Z@NwhJyp>z2CR= zFP=es{U#s!)~+L!=VzpI3emsfd1RLT4uuqP`j2k3)h2h?2gNl^7^G_Q&WMt*GhxaV zio2HBU+eJcEYATJS5h?c(8XU6{aZLSG47~9UT#0TwYL>KD*0l*?TiI+5L_>oF|I+# zH_nBtsSADtY%!3eD}aUk193@kq8 z+LK`;$x)VaZ7A58cdHU}-Ib1oMNH9DQm{L;w)!)Tlv`dt89HMN9*Z1oMIPu^uPmqc zbh41lcPE7kaDU^O>+ift&XK6FIZ(>B=|bs!izbCGJiob$ZL*7ApfF*>qr@;fdCH<6 zr-ysF-I7iT*_e}SJLf*F>;uHNf1+*KNr#jsxK5XtIDw?j!Be@5n?T38*kOoHCtf_$ z_1lp9M8X>$t`%1%GlR|9I-1DywtU^Hh5aI`7d;k|-GE3-_%HN%xRF*9xfBO?0O7akNM@;LH+H*%fbzgLCqVy#P>UVcF?GUydOTe#nL%K9D93ng z4OBYYGG&LOPw;%kv8~Q{y@#n!{%45(B3*^*{jx@crngG?TBtb`eA~8H*+2;td;LpI zu{XHn)4_*=pRoVu{N^{0H>r~x7L&%^?KJYI;aRy?1@2G0WJo^x>Usws>Wm#RBf=L1 zLsCWE3ETY438pC2p`zdEZmV}8YYZMF-pr?vj@dq+n^CSLZr!fz_d?hQ$!xNJ=+qWS zNckmMkNGUw+|S`@Q(D9~U^wlYl@a)gbkMtd2w9h_LnJrWO@u+Sfg^ zuhApEB7PwTst!;%=RDY$>H_gar|8#1j7a+RPcmB)9Kd{&x9%fnBjTKPHgxN4S91P{ zf28yV3*svyY<(auVui^3I$tO$6d8~+chBjX}qi` z6OyT4ABHaAdt$iZLgjF>8c`gZRbn}S``2edUxQBIUWE5>*azG{&6^E<+82fA9-$vX z;*&09Q}1PooK6>#6Fe8)Yqy5@uKY~9C@DvLKOa1Pi&=uCUMeb$fQGur7+b+}28l+96_p@9za)QN#wk;n(-;CDx$1XSV zykuo#8mvi$sWZJ}u4Q7RG@s8dK@@*K^}AFwW2H7zr;B^&s3JJ!ZQ=Q-WJ-3_MaOJ& za3_P3nlcj4F^{km+M0BSgXDeRuo9l+1Z+2Kqn$UH!u*r?ET-q0;BnP7T0K`86z#JY zMQ+QG!ZQN5gAAQXIqRMUZF}6G+lw!77dw(Y4~Ex%Z>K?mBd^S9VFz+w^AE+X!<=-n zj;j!yUw(dl_u*nG-k;k!9R8Cu2EpPqm$m}rDRCdT%7r=Yhks+2&2G4nqaQvbRyn(n z#4A@mGd6A^vYXB^CH}?yYtLe`evmd%`EAC$mSlX$@@Xd!68Bd+Zz4}lY`IHw+3ZX*=j)yFF$Y|bUad8Tc|`5U7axE4 zhx{j_dmn_iD8tBUo`%t@hY5~7w|iI!YT>8V9G5)*0`I(pU&Jd0|oj{h_w@(vqZ zNdnGWoL?#qvoHaFa6#`YPHhtQ)yL*WBZYY9arMP8A>aK*F zxc>}o-D7Fs%GrB8{EQux$^@3F)UGEsTLkyKi)O&yfK5?nD;ea^pTKEX?BCK~y;jiA z$^yjPC;2yFPJo|6S4%#@VfDCeH**m9IBJJ z&pc?r&oOtsZfnJQXnAeVGh^)vDwj3u?;Uag?rW=~z3<^ZbkK|q0@6-ZabO-R zbvW|H(Pb*ol6(1|U``~bkMl%nwK2>GJk3AGVGPL&hfJrPO`y!H&r{?#=B(p3CMaR= z%aY4wiP>xyBE&HJ{h5UVoab-RA0}y%l^@QL1;N-S87C~{`W^QSzPGcY7IlgAszRRM zdUW773)AAOLZ11>)s~!l|4@9`O$`LmkHfpfP<=Ho4=VMoB2u{Tj`f;TJRc$f1D&=J zPf$lA7MdJuE{G7D)!X}Q8@0gDATQ8zm<9!(PV!&PcYt$J1C44vcn*nS-=J{Bf;^7@ zk9@>E-;9NvnZaM`sQ-C2D*0C?j2&z{qnrkVzH{^BZee3H4MrNXEn>Hi=sY{U3+iXRe zl=rcBZ^Yc|i+xd|b9S?a zy!zWSYqZr#>;d}Nxh6Jn)k|nv!{tKWm0PDZ)T7_8X{~Ai#h&m|ul5(7l?VR+QFPvM zIsRW550#|75hd-tm&R#7?WcN*tcE01LXlKLs61ktyVGnm2i_^^F)N72k9uw{@jZ; z1HQ(b^Y?zz9?s;T+J-V^T$?%Yu~pO@w7yq{JUir$eCOn6EtTz1df~xc6_jU++5c?0 zZ-y<>1vjqL`nS3B7yDfouTzifL(kD(Ax(@l;cKa9P#!{JJNIewNqTy3UA$J0xNX04 z7WTa}#ZI<~42gE)OlP~F;El4tZ_fB{GwI*iq0E_E1NQJBfO~=L8x|A<&pli9h>0#$ zcI-d(Jix5?mhRRL$_E&o~@|WKP^CcylQtl)`_N^lpCcCjNb3zL(?h4D zeJK$%C$;PTyRoxX1iI@u;#4b$NAn&Q=S15Kpv?zhyJ zn)~(li(C_QG|qTl%*H~0>p!3PS(Z2fyS`X85??cL_WqJcPjukt=$|0}X(X@ww$0W0 z82%@DSF0zrho=O%Ke?icY#0AJ8Nl`@zw7-KU2s#& z!kHzy_W0f2L6pvTaMoZ+ezxu%Q4RKF7wZ3JlSwVNyL6ulh z^2IQ#p8uS6hE)Aw?^9{&)F9qZk=BCTJh3YB~PN0@Pgh zsNGefzMtt&+t;4+0Q2-~+_UzU2zl|x?)>CNr#*=^Ti%kdQ<3}j#Xa^QRbK!2eI@ao zWer>z)WemUE2zGY-<9?)p=Xc%XN<{$M)JX*4Uv5)N!>Ps1$P9L!yTW~p8M`WpITeu z^7l=v9+PC@?pMKrhpDGz@2B|m;Af=2q<#q4JfVlLpS>?*lu#dbN1|fjE+cHQbMTtS zM!9h1iUR*|6C8E4$zDV~qsr&?ThDls&!{n+A#->cl$LBAi=Z5p!26s9cN><2Y;g0s zNI@%Td!=-0q>c%_q+o45V1$#Gxiq)g)BGFOsd4MgYxD3*PP1Rr4v;6l)W^@m4zEAF z3I}e`Jf1n5+~TSTkBV+T(-hJN>4?L9+j4Xe2f8DZck<)Ii?9ENgpy7n|3ogYQ5y5N z#_F%$VuxI6%=|W{CfqT*%B2@UdUNEO{pGc$DE&0hb=@r%T9-GUswSToTeO=;yiar8 zwD`gT&QT`%eabo|%trdE^JNX5ITvhdN|kt3Y>!x9mM$I7h9>6Uts~Yk(AMd4YSJY6 z4g6|+f^`=m7GCbV=O=-NX3Bqe@#=!~jocRIi}?_i$y#;Rn|xBkBYqJjT9m8&DSS!6 z0kU?A8=u*ygiZ(If|iqRzDM%TSOZ> zNS7wgmHI%}cIF*7V3Sk0duH)}bxy39rH|G#AdgFWZE~A1ybQU}d+;#%RBrlKmQcUw zkf&pcWhm{nW=gad?4td|^Gi8Nsv0m_V%V!s`vB?6C-1|?m2moL?&38oZ9!n!*()nY(Rcy&m^S=A>7dOcTY3*1i-_?Ggn##u(H8& zt;Y*{ux6^P*tv~39VbGzTnM%XiMHk+Vol1x)>FPjCAibPbjNOOpM!?@ifvWXNP`DT zvdhlXy1W`6rElWcvPlcSDRLett#gCUg+tmJBQ}ueeqgDa5Iq-bXE$3j7C_3<7@?dW zq?0V}ndzw3!N9wtT?Y26(WEQzJiDk9PmhSr`@3pC7)-~Bhz5qj<7YhD?ez<)Y7D-RH_j(uWa-76I> z*nQ(!f6WV;PXfwo=FXDtc1n$BjgLG!R^{q=zp_S zZnfUL@25L$(B$p-k#}u2ARYVZqvINW91Gc$%Ec6efQ#avXNboWpn6iRjh-v^ZwFFl zn65CkdYIiZ#sC<#$%D~D&LG>TE+SiP3+ab5D;xgkKx&A?QST-@yfPXaw3B=kdj|sI zuhzL^r*X9ZCJr~a$NKd%g*frUnwrH0W|lCw;%wQv6gMngrPOnjdRkJ~VGFV6uWc#o(F+OfJ9d0E#SB#WGLjUUTUCU3Izi~eSiJ;8VYQms=KnnE!;xS9TR_T=FN0+!@qkM$}jX{;Ly~oMl%iK zLg~%xI&M$%nO*qvwj(QXUE$SlU6j8HpMAb+V%h@sEl>4~d`6s5-r7*+URm@_njI^8 zWC+*Kq=gk9WdU2u)?Al!!dN~n(V*KQ5BoN%ZV@8=y>!gx&D{^~IGM3d^};{$`7GEn z<|3mHvfnk+JQTH%d1g-S*g08{bolhJK85_34I34&s2hRYkpCUKbq;uc<7MMji%E|? z_au;e8v`n`)gBy9wxWGZjraE)ZX5{oRmz)^L+_lrZD*q`u>E#E+lEPTIMW!&qrpji zK|O8R^_3PVQ2*Ah<)#g6ey~Q-Wi#=9_TTUuJmd<6`*vO0Y)5%sjzWP_7xM3#y%TtM zn2B%RB)ypBU|}>v+*R|76ELld-)e8A++@;=es^o?A-nR>pyVbC<8HkRTo>$sRTae_ zzSxq#t7&V-R!f(>LGFimYLg6dh=EKbTh9@Zg@^nnj+}LIAAYU^4 zpME(#ue^74AM=%% zu9qDTta1exwil#a zzUuduKUS1Kn2h{*KWUdHBz&!Kc1mzY-`n5b2J;F4+wZG^4k3^09z8Z~cQUdAhh0*; z&QSiU>dw7J&u=nt?CPz?_Qfp7AK8=>MVvrskAGGt*%i_258DFepSJjKr8sE)qdoA% zl{`~+X$&>mvX&v~4o5*}+l#{#1B9-yFl4+)6i4$IZF2;U~_-<%FaIBCPJCTXoY@9Kn-TS=eW+eb2EoDP9CY%ak z-8|Vu-+4!2HGyLikln5|d7Sp}z6z1+FGo{9?r$mXh%Z*~o};?=@)ZYc-y@cm6zW1g z*(3ZW)nf49<|=1C;%;Ph)wkW)rVDub*#@>z>bdY=Jvc~u!}>eFPdO5=MAkNcXInol z2$$VzCi!$Vf2;vS*QyA`kPo`1=|U=FfCUfTx9_?-?T8BQll;Qs zbnjm{DD~*xYWT3e;MR7^75DEITi`A2gpq&VIsN@B4Ow%Ffx6?A&y{d}e|?c1R26P8 zv^Z&qL8)g_OtlTcMKN|Buz^ykSUx@#no_0V<@UJbT zKIzFSBhG;3()v$c@9Z&Qp4g%AC)C5i!8N4Jp$-EXLQcbf#X)z!+U*c!Yuqm;mVQo` zzITt}#QsH-Z~1o4sUK3Nz@;Q_^2|jAw7m+`&&6240;}#GP7fBY_SCtwita~4>H)tY z*crHvT5nR&p?jA7hQ9ZQ?7;BHW!Xc?+E9I`XA6&-1M;+~8l0d#aZpgC(K=TKwB8hS zGO)9Uu}#NU{i&6M!b9JGyn4^+__orFPnuUCstZ6C{>3esG2svpthXCZu8R zqTv>WX;+;8_Qt^JVHOq{zu}SNrTe;1YUdLA^9@&c6!?$I!ZCwvRi#=MPFo)55YbhI zmZ_tj=XhMezci2GLOpHw_bmx1R<=judN4Tlo`qJETRKLc6W?zB^1qV%I_nOUj=uWG z@<7it*5AtQ)CaYd^LzaZ>J=5<;y4>dd#6?HLjsSSDc9@2`&~ZqD(auBw=I>!mifna ziiXKzTv`CnP4>CE?=U}5aknihyITnga+_jdZ>`CE6%Rai{LAr>N_WtVx0|e6qYViz z>D&QaIw+aqy*+fRKIs17eAl~K6AHWH75SIAVc><|sw*B+&PH~eqvsOc&#g__`ipGg z?f#s3Ubgi7yWi|`ts_59y<{La>7wPEyLY5%XyKA%`*81l4D>x$`QhM_1<=_rzg{(e zHAWeT>lXJ?519X&tYzA=@G&B`-^tDk3z9wyh`HL~ROs@Y-^4?y394PSXU-a=cS-xj zuA|-qB^`f@*DjDYLZkI)!0TlNqmIrJ1sib{uHZlHwV+Xs(QB~ z=07>`otytws+IEGO*fUtW~{MpPi&rtKi#8lzHK=n>kjqyuJ#7sD39E9pzXSmIy}1c zHM_D<3su+bVfYx*e5a6bynH3~rtBL}m*h19)rLWnl)JXTIja0+l8!fRDI?1_S=W}3mG(r< z7$qdM^SJYjplahPhIi#k;AR-=93S@}-^NhrKl0%eDUSSh`lJPLhz)ieW?}M)zGF)Z zs3+(6&~AQ318`{F9&o3f_^sFF7T%sm{c_{4cAO>-gurZ-M`*MX>N+h}aNMQ?>}6_e zzIJ+It48A0;H8?_@Wgf9hg#}!XImY$mi)RwM;WbMB6^h1@o&sEaz+Vju{8UO(kS^M zJypod9ed9Pr{1(iM$U^5 zt)%;udc5RBu?+@C~@}Vy5Q4XNJMuO;I@MAMOoF~Z$S=nWQ$~i`V z|JvE1uY{aWXNNHMGW#l`IvC(x^2YnYGAER{y+KN7#u?%URkwR75a%SK+Vf-p0~Y26 zzKn~cT*H+WVrnW5kQ`hy)ACXq_20jCnY>Q@Li{Pt6U28_F9`Ht4z<)}?~Z%%#D?^dj} zfWX%a@8s6H!OW-HTfT?s-r>+ZmmSRn!Rf{p)^p<6T;xw;U6VyN(XZuoX5Y-iTXWnr zqg}|yKDPe%&`O#MHZ^yxHbm=9hvmzH4WU7XyEix51Jth{y5lO$0_8IcMb&;dqOUED zU}vo$*@!)CT1_9Pv&uqB*@^QJ#c|eZ_;VdzvrXBpycizrUG+XJm5E#{_IHOhlJBNZ zXwv+L2Ib*{E==l(qW_}%&#irF9^x-i``luPc{e!T{JyyeD<^~uuQGMeu-)~Sexp5_ z*v9O*I8Nu2vxl0O%(pr&;{_$RrGWH6UiNF7ETL}Uslhd*=ZSn9aqqZfiRq?y?ue1! zB6_Ez%*r_8qNqUB1|;8Chhp`Ec*+G`J)bCGVS!&>ZxA!vx*FMxbO$uu7Givg^T9M- z@?D=At*dSYWXov&!Ze^fj%MD*Qv-UC_0ih#4EbG^Ga2R2x6}Q-b23AKowy63A9tJc zxMKd@VJ^0#)_8Kur`vha4ye4c-fDgycN(f4i86Ze(i>02C`a-RC)dK8pITAdNoChR9lR)PQXmwet_PFNw7 z9>DHEy|mfCRnCffp+nS}PBY@5WRxwv>+pc~!5np=a=AK`tBG;@S)c~qUQ64i#VGf+ zBu03Lm@O`y$;|ZJ2Jof6CA6!+jq*Tp&$V4$QRw90PknK^N661u?3bWCLaupf^fW(l zGwr-Wj_E?HqxHs-W_#F^9Q4BQFTF=nt3nJPa=@eQ4~%sBZ6N-Lje8&Un&ln-_1XTW zI$B%kEH=Jw16iBT)uvP^;_jYX3}-r@%2khCyMUIMJ?q`^ejV*WUfp6pEkfLi_OXC7 zI}KsThtJr!!5NoK^~wHxV2g@3jzkWXTSKIlv0|>>O8m1W-Q+|m^+hY`XU%h^eZETA z%@@TousiH$=>N~H0LRCvVWI}8-R7uzhx&%OnvT4Q*g@QnsIYa%>{xJKV&^-y7qqv# zD^ewD$^=n?7Qt?7;`P^a`K&kB0!tf_G@B?--2QYhF~Y_iTLUR)8kuevWCkp>GoSs?cC-8>F4Cdg`fNI1tZ zplP7X<4cQH=Ca%N=!3x)APVKX0I`Ze;(^M z<~Ie+o*1Ex)r!E_`^ZctOpmzsO)oZI;G|r+)!M#rnwJ%L7tKcKLy?k8g@hdGKL#S3 zv_H$C&$m~4QSK}(*9hg6&J;8y}zjvpHjgbCS!yl&DkB zb?C!dT@_#}zUX*mV}G4S_fTUxTH*SU?1#guPFVBq$%<2Z9D%Vc<726tCMInhZd&=< z7E6z+**_(HVtd%#YbXCBzi5fnibb7n)Kl{?P;loGXj$jfu;>ld{ods6pE)841AJ*o z6Ib2vlKWB)Q)PEJDe(9+`deYm>I0p}a!D6dPLkP4{Fk>s1&?{1*8@q;8(;cqzu~k| zF*kg2HMAtNKa;rS2%0%>)>po#JWgu;o{$<@v2(IX>w8V8jG#gHIw#2A?n7L#OOlK|a1b=|*M>laY3&GO;mZ_4zY5!mfyCM>iXBYhW)Ihc{3t@*OG1?lo{q9Uyq#5!Y@g+tlwrBzsA z7BBb8+7hO&if+plu>?kQgt^s_5?Cv4+Zi?B0Mg|_JH1d;kvj##wL^IGUSHArB;_{QR-3d-!eooUufbFb8s z2b#V#7tDF~&-``)o@g)Q#C}J}ZoXCgrA!wh_j+}d7$~Fff8#~omsBAy)H_!|RTN(= zKACO$hj@)+$0onfJ`#=%=Xq(#K&hduIL83>u7O_1Aqj0zY!BJenJo*ICRyT%A+B(h zXYi+~mAb(#*=7Ehn^l`p*+9ysm+5-yn-Y@(|J!hv>oC*Y2 z;_z2)IboV7%6AQ@e&O{1$;ja|-4{vMD2)}lEbEGw)-L2Uuye;8KN~@wWDAh4=jgt5 zKnvR4QbNv_2qBxMJ%53LIQ5&nTQVhX02^Y>rM8W`!RNB1kEi-r=y^?Ra(<5m<>m(y z)(Y$3P)T*sUM@T0sXm!KzGoSBmf!gAJw1m4h7!4#?uz1Z3z_J6nzvec1aW;6<(P_+ zFHG|+$203wv|s51_HHr%&HN~fJ>#PTCu^N>cvGFAb+t0Gr8ORox+o4NzZz4QwCEwE z9okT5Hf`P;f8w^}A6*>4d9(2giSu}WTjLMnUnXa`3smWv;oBtN<8MF<>#jpp_}79yq~)KT)58q)C^MQSaLMmH2~kCqf%5 zJs?y4&=JQ_CTfQ4JG$$aC1~q7Zs;kdy~96~O^s(+$o2X0plTQSQFEL)B>Pq2N_)WZ z%3Y>-YyS~_cCba&nXe&kw6|*6@o39teka_z_3_!qTI8cXH!`v973sj3!cN#N&3j+UQ?UL zZ3EX8r>x%acp{cP-;iXb0qOFATXOEZ!#9x^DTXJk5Hr>#`F^$o_Se~!%f6d~e0RBs z8F8)xSGK;kxk0@dic`IxR$Cy~U|4*3rW5A5{?WK?Bm%8pdj9InI6(CFcgx&l4M0LK z=NGrK0Ulm5>a%7!`MIYWk(0g`<+|saS;S>ZO&?0=V$*^Qw`WOPr(Cf%r|I)AI}dzt zc~KNk7WH+Ht3U6VPaMr>yFD&1HbOQ-zq{BaPd#kjIfAYl*d|h9vV6Yfeh#Fm! zx>*jZI(b7fMOK1RVtk@VD)qT)%v>&D*#o=Af z3#<#00!ir1Vx?z3$vE-|*otd){-heTaK z*VBL5WgAV@*K#)FOr(9?;KAw_s<IgY+ic-Wv%mXRcO_8|;A7$EPJS+!*O0<}p_&s$(u}91Jcw7`_G*W+ zAL;qoV`mx!DKE=^a^go9?O{8X%Q?Gog12x&X=)M!vqo0Tscn$P@(ZVyJHA|ky%D}q z@k`Z!TRf`l9Jf16dMr+3-$FT2v(K`UpIy*|_x!-G5fcc%RBoCzpo4tMCu7xgsgGB9 zf$s6|hG6Y&U)gTXLPq-8b!?>?$TEDSlP%*4Z(oiG{gki+^uMdq;cN;Y*M@GCzGX(S z4dY|$-@Bovd;jiy(#gKCom_iQg8Br4HlHZl=>}W};_fzxI^yMzZ$@T2Ja9@SR3n;v zp$Ui5o(fREl;VO!nZtHWJnPG)ZhV`#C^LU{=y;n!^P3fR?>U&L?i3WOE$u;lq{?^J zOe}`*%V~B`1U*2vNc_W(NG~*-FMfC3GYh!nRWf}$WHpKyvg}=(K0bi`KBGB~aI-2jj^&k}_rY95~(V`7!|)2W^Awh$MrAU_pv1M~CmG(Uk`NQ1*(wOd5 zJlY-uV}34>SKD#ipJN5Smbp@$kmn9;Qye=pPp<;z@|=R^z?x~{@( z7j&84GZ9SpyQ=XHDYkt!_&{jwxib-FSn70qRrPW$sG7_4jUay@+b`ZjTlVl#zS93k zmX$1wnQrPaVY!1|vHf0`11yx~F8gt%g>nY^OAo)Ky`b;Yez83Ult+|0AaDKL2;%Z| za)mz+*QHkYt1Wp(eY<*HH!qWK_f%?Wu<1q?Fn_*}csZ&BH{aQ?nb~;a;-$HAE%)uQ zqlJ6%LO1Gl-Jp7JqSFL_#Iz*t@3lsb#m;8#yNToOzbxs(A>wfTep4$TNBU`-#b6ov zDHPYlKSgcoM-*!GlK-HI39w~m1eB?_h`IT!hyzGkgkTe7;j__d8*X?B06rIQ1tAJgvQ*KkWf?5mB{o?>b=|U;pLZs^S>` zR{e+dJ>v4|8uzo$8W5jTj(7PT2as!mV{hU_9jEGJN3{FrxxK+grHud?$+_NFS z=IwD=s3yP7kC*!jBxG=2Uv; zyWAB_w0?-!?8YZ2r+;qEZaeaGUQk)eC@ha2%!+D7f# zKl_-YZhl6C_;x02DYkQy(6hvYF?ml9nlVvCC&{Xqe2+Z5eLr@qn82>hOEyo_KCAf{ zCr53x9oko{wXdW#N~y=_r4lIyn5Z0>Cs1z>j1^1P=+ix}$>1O7;Y?55I#;<)a1r&l z7!PmDj?stlqNh9d2{1w0EAh$W7#&djz0z%?x;^zlANfyFPYw)~)Mn19=m6WNC6Njb z=HvZm;d2YpO!4Jf?NLi!@-^*AcyC)^3B%4(qKEhBLl)1Oux?2OlvL|2Is9lfE{t3^ zm@(syE|VkO`>l!Bbnw~J4{TQ8E1r2-y-F4`7CUgHTDc>e+e+=MfRE;Ug4Mr8!%Q*Y z$LrbjXSQ&V`|7u!q<5R!&#mh#p?#5#Qw2Mh4OG3lyT(UZ4(bOKY+^#KuzSvLSwpEC z?CkyNTt8}!0okXX)LgWKhGRLOg{o=)xKo2e=Di~XkBB?GVd_Dc_$PMuBcd=GR`}n8 zF;{3SnG?CxVF}S6Z`vIoJv8~-%Y`5R(fxf-R?R}5MJV#l`C7?ETZkHT&rDk62FQ_b z-Iy$gm7jz@=`JHZD?)y>hxiX;TMhlX+KnOTtn?-OcXsHXtMawpk#tM02@V^v(K?mu zQ7sFDJn>Fk>%mM}$~#=HE3w(R8l+`&Uda7XhdVvX(i&69|GeXgUE(|$WV^6JYS$_= zNV426ze33wD?K$eTR$40wNrx=-*;_fypT9I*yRW+F5bx#$quMesn4sWMjY6dCo*?p zh@07|+mVo9jP1!A9^2jWfYI}5*UtCSoLUm;v-BhRncM?HB^M}nF_-Th)JXM_iwz&QIWM+m64)VD%16)&1B>9taFAtnN!W1DlbQhmJP& zoQjCBhkM$?N4YD{^tZE!Cpv#s_ET5zEjV;;XiyQWE)>^DN+R_q_YDW1i6Ks~n(bY|_!a->ZN3PWd(Vw5(ft>` zkC=kB@VN+?LPuDd+2;O&-jC3~We;vrUeB0q%yi>7OHdaHR?EC%i-Se-0ngD4W2b}E ztXeIg+$-*K8VQ#^|jPHLd^i!4UGx;4aC?|<93Oc%Mj zU~oV2C!(CTb+Wx8KhuJquUXV%@K)lHbV1T;$h&;zmhC9rA)F&39C*$Al(Z&bn7f;W=A+CdN^uvYJAFXV=U|8-e<&%HTS{CK8V5Kf_d0u7VL|FL= z9I(bqsaqW`H`6_5+EZxVp`{SjEO%&47Tw$UJ8KVa|5cZF_}SVQZ4NNK=#4^AgbbuE z6Y@KnNB!Y8cJ3=^@3v}VzdoCE6#8Q+? z^}uhY4{D8+=+#dZ{rlxJ6Y`eqJ)p)ZiCdQ4by~!3h4HR>MnOYlmAY|6`PO_Bj2FH? zZp1VK+mQ!-omp;h+3Kyb<8!k7MQ|Kn-Qa?;yVG;tPSPl!+-Kab$RM(lO}JVIt=6p; zh2(vs$dbcfUAJ4tpz^!@{k6@Gc!Fy@M|lf4uP5+&c8~>rKdl-isiKW- ze00H%>9M+BaYq!(Ut6N)?<?YIFFe*}7q|m2)~3n(!a%`EO|#4}`T%4@ZEctWkmKMLS&yVfTus z@DmdR+AR+5^D_eN^vf|5_3j|qVScz{jDD_q*Yatq|EtyKRx^vv%L7~XF$Wm z)UqBo7f3Ix_5Vp$=`7nT7cYow0$Zh%YSsK%^LB2xy7FyekP#4;o1RJ5uFRhiX}bVp zeGGY;hinOrp6a~ot3AMK!=P)A=V9K>(KP~>=Hoy12TG1))e0Irwl5=H54QV+X)OQg ziB6lRHN2#$0@Cd!2Uj->g?W3^Q_ivQ*`c6+Lo{Nny)wD_(`6Ufo9855?Bj$D=YDGJ z*YSXvvR##z$%4)1Ur_j9tqH>8O1}N8B_Mi1xPhn{p_~U}zHg=~`lR+P#b(bbl2xR5 zL58gRav#%|t4P}+&&2Poqe2Eab|LmE|ClT&wk|qZ4R(a4d|I&mtUNsYcjs|RhbP77)4ofam2M+l8hKh?f2{mqnvli*)H;YYkWIw-gdzp-ul@oZmA9noOZk zVMnWIvLSHgsqYpWqUy;9Wlf8gXwwnrCj2tX9rJi0shXFJZln zMmmK9DH{1o7`b(ErjLUvq$>Hx^AS3R@qjbmOj8^DRxHveo1sXq_(qFobj28|*_N)m zfmRc@jxdGqT1I3W|9jyrk_ zQSpq3W0m5$ zd3&9d(SbYag^{%dKCxQC=BG+W>)q#v-`%2BBS@&7eyaiVKkjWvSniJF%1fk{S-L@~ zy{x&r451G?j99wI$XYM?^xug9b&yoKJAeKjcNF0_dj9E>4vc<&sjw-;0)pAK+)L(} zp~b8YFDVYFaQsS>v@l@SK|#JfsVXr2{+ROlB7JypV4kNwpDSw0r?1H9(?q+dQ5(*F zlE9YUxi0$46{6F&jF>o4r0hX~T}(4YPj_5R&TpU*@!|c{FJIn-=!$# z^6P2kCs-8CC=+Xvx53HkfG5H&j=*e9+PP0rhN53rhw?uCF#qD{_bl9vtcO{7St~5n z@wP_nGM^jteyT*hFI`H=-7CW*MHi?_uX&9GA5#a`o?#@WOF6;h_o_<^;u(pDHKo1<}0JDPUh=HO*XjU(Z1Rz{PaCj zUexrcYYC)Yo;Iv2u)|R|AN{9|E-1;v`$jU?74E+k3D(*}mWqDyi>r6K;Rfb9wTD04 zQ7=YU|7H+XjqOv)bmp*!GIcJSD_XQlC_Pg`QV(6Jl{Ovckf#xoXYuPPcfh5UO?5Vk zzzV#dluQwC>2-eQ-LV`v5IJg4`IF99&1ivlX(lMGnsN9+B@=P*o5dTtS~syXBZ9~} z!Ow4--u0eP_gzXNjW>kASV8yIa1g+%!HRKrLOA3|#|E|kV&H?C=tf0~vM?>~e9|%$ z!MHDb7tXOWV4G^h%G3`GlxCZF&#{IVN`*FT4xkDF^)p$v4s8@+IW?GP{zeiTPyNVvD}JrN2uF&F}wCHs9ri{WoS0-Q%I^ z_qg5HO@`H=_tZY+40l5Oo-m&uAZCK$I=2_@@}TP0>>JEoJ%&)T=oLXxP70a&SDiatg>nfZt66G_R}wR9?7!9u!R@Lj+V3F zUL4co)AfRRzwzyg~Ppx3tcr)_X)#siO3$Z;2GRLh|CSmo7E*y$G$2ueK#iz@nly zzrTP9euHP@9jQunbIP|Sqkrb%ZMl!<_OW2W)z^u$460bpcoFTBXb+L{N>!I?Y@slH zhx*evGYqJ|E_||u?pdbKcjd^hK-C9NUDi{?+_*{2!{?DBIt>bShLhzd?!NZz<@e=5 zoHZRaP2)kNQRrpEY-wn}>pI@CdnpR!@ScBaO!v>f_^S7iCzdZd;w>BEh%6C5xzajY zu&aumDS2syMGM_$uh%Pqz}}1=+%Sk%YDqQ$o)1JOAD_ z=mJB#Y*rr4(ge(@f9OPziK^|Zq?QKI>ajTZz^!2$V4ikRKN}(eidpv#Ry}v2=vm;l z)7ADcUwX}MQC>n#<}a^(!KDqB+kU^Vw{}Ff52I@F&Z-cWQ8!cPOOdab#VbpFOi)Db z%-YU6GmwNof!8Q15tk?|bST0EBpAYbKRS`5lIR)qnvfes%%&(!S0fmG^dl==)B>Uf zXBu4QOyQF>YeQ3v3;KR+JAO4?6{PL*dUDG&A^MvCp1Bil6s72PY3eb+Dq*u2#Q8bzVu%bMitcCe>MpP6DsOqNap~<2~-lEcWm+@ zWc%Uip!2D;(i)b0V!=+R!XFOXhGTS)`Hbr=FOAR*LF+$WXr+jz&Z%b{u}tUWPux04f!5Z zQ;p$~#o1cZ*P?W_aTFkXp)e4e2!}f3A=mGKn#jo3w?|@UE-u%a)(RhqY z;;$>G5SUoAwyivGkMZSF-OdZhQqkA6l&8@IHXNPk;qP3HEhUu)HhHPSnb)recav0X zs7%M#0R?#Vsj*uu(iV%pUl5F_UJXf&D|3E%cv95pj?rEfcl>YvrL~&2UYHqO@NQ{@ zBL-~Cia$m3OM6dhz_~|eP^9l7{B)lyuw}35=Cxv>-q05geNB=8qz4WbKh;CuV@^sR zM0wEA^gkETUFMK>^u@fbYgj0fS^lZ2Qy-YW*L|XUFiu{RdbM_y190^hsmu{V!1aqu zquWncoV**&Y7s^BvHDDSu#*Cq{PTM}P-BNeJ9YXm4VWVH0K1v# zWhDsM6T&LwcLB?|KM`7~?yzayV~fexRyfWf>C_R-!XNo}rThMnmD`uQs)h2!pf?IQXI@eyPN)^GRar~c%h>_r_MUV1R~o=ztA^*JP0nb2IezO81$~T* z>k^46X5z)+$?Hae6w$dEKXd&p3&3#K`Lhd1;?oQq4;<0KZquQrqJJjXaZ72$_5Tv? zK%4X7Tr?l6Y0^CvPgZO1z7y9P9YH?F%!sz@qS+bSDK}1cNfJ&Z0l& zpF>q#I_Np5`l?@fRY4ZKI<+zmQ^aX%-Y?Ts2ePUSJd9laKnJ?gUIjibvciGu^4^6l z1{hf|{;eQK9|rfxOvc}E!K!-|uVyCQF#W+A6yVi`RdH>}jlb-%r8wF38hU{068@x% zgoI0bVN$W1&ST#`r;;5VgebhD@Mhg>Yc##HW?-)*%{P~eO4c9Of>c*t?WOt7(A#Ps zYse>$TzoxB5$qaJCUNoZ;44BMB=@@=xZYRS-d=P!e$)X9I4v}#DT*YJ=-%owA%Zm? zSrO~jlf@NpwhY*5}6!bVS79H3NJ{L<8g{2^pt5 zPpW^FB?jH^ux-m!1;xFC4yRYEV?sp{H!oFm47AG1#*EWy+vQA{s5PP0_4axh#nK8g zr_h?~iZQhIzERAK(E_f&gTGvE8=}6TwCiZBJ6tWkczxXnp>%Hf5CupF6Qs^|`?0Cv zT3>OY_Y|p4J!@NdenbKVx;11@7MeontzY@+B@{*3T3vC(g(?jEn7do2$hy4B=#;@j z8^~6!HZ16N!=W}wp0$UK@HVGdu*V&$x)xPU=%}FT&9hFQ)T9ZKzNh)a@CA1O`_s#; zzPW?oO%*mZ(kG7H`;ae`ZwbLmRxLAn=K!Vk-bXjR)`qC!$CiCs?x^9vcmZ#*8;)rv zy1zRv4EYO|rT%%v#0@t0@0(C{h|`+|-cO7SP;`IWv)Atkd79Gy{u93uKCp-viJY^9 zFn{N?_fw=-@ENMz@?t`Mcm9oEEQ&tum&?D;WdTzslVksjA!Na)eVU?|baCui@)G&` z3Mj4Yu;-qy0SI+<2|m_e- zm+RKbSDb8-)!8lj;IRi}oZ57(nxb`D8Ea!@sqz7;tK(kiTVYG|)t~Z~C#^9Y$mBUt$_MCG@X)!cW046_g&>a`I>@Ar!=$bh-qc zLFDDT{kJ7-VO4ACgGbp^H6&qhHb38zEIw&7{oMd#CgZ8WdhQUb{olwSArQU_{4Kpn z&v(|!GaRQ0aUt~Lgx9`h9`G%t_KT1gSt;Dj7r)k|U(fmE{^5!;RK|`>RD7d(eDUKU zPXU0}d`E)wdhPMWiu_G$^F49P?7bj{8w=TDj{J;$_tl)u_egof)t0(KZP!@lS6xhX zzfsmWKr6TNjk+fZy)|Y%%(ll?0|bsPlVFiW=dkrEf&HUQFpON4XwR_4thu0~K2HX0 z|Gi1j@hzc!`J)bwQB>Hv+WC}bqboRd)LnnOh@#%9UfxFQ9pQd|lcocGXP&w}Ie38d z;IM$r61~w>jZ_hmb^4AIB#d3_y*JHQ0?{ZSGLuu;36@J{lT4fDAxHg<&YEU7gASL zrbD%`N^gPZ$Tb-V<@zigRzW(nY81ECx>@s>PVGiET1m`Xc;v^y^X&Hgf4Vf}t+S<|46 zs!nkaFO@pr@Ue|u+boQcbEkzuZ3Lkpb+|X%KXt>GC99cb8!2*na^vG}A=2$HZjqc> zYKShWb0;k2-Ko;B-6T=o5C+anT%Etn7?hcbJLPWEs$7O6@=1~uB)78f+_GL9;(8X9 z$QihR(bp%}A~{IEO$b~qa9bDHo;+0kS-up)lrFVdBre0!XDP?ti~f(TbB~KLf8TiL zgQiqEYN|Pv%5*;0RG&GYo;imwm}(FPv7u!mhh1S1>ktN04VyI)T4D#!62e5tVhyHg z5C$PD41S;Q|G)pd{2{M;n&-Z+`?}tj1W>6T{r7tpqc2t*I?|sF^>s=GA-&5RmDTr7 z@2r59U0?TChRXp!Pknxp0+BukN%VB+okQ3Cc_4@SCleihnK%=VLQ8O`DJ17nEc&ls z3^6tk&Qad!2bFmo^yQ~-c=m>3^jt^qgba^-^y<`O+~a;cRI8kG3}&-{)AIfWM?)fk zgVA;G_{So2SZ^34l%#@uyVR@TEIl=z=q>DGV$Fz zSWTAv;VWo|Xz4uq@*GbV*!$$h>L?9>NWB~1wpK#qa1T1pl#+>dZE$9uA7^orD$=%Q|%OJ89!HfY$+=P=%SY0;;_O&~;J#AOe5wc>8K!R56<2yZcPnL?-YGUHE+QR2=yD_bYEZ8zQ=bX0IoY%LS{$ z9NRYyQPG7aw}oAp1X36_{=3b5EH9! zbgfNAt+9W&EhZGB$jD0Uy(6%Kt>6fuGFt=)k>=})b^s`KsS_{e<)J}#;a{BlP@h^B z7G2qqh{jFZgS0QPiVa0RJ@}ydPxOzsi9~mc6}IZT_&@_Z&HO zT`U49Ggsr^`ZK_y>6?3#;fdMHvx&}5;seF^0uOI{5Ex=@Go7$Nl+of3+fP9Rqe>zX z7I;WNjm%)32`dUWR;>!@hLPyF!56nHAJW011)nm5F3`}9uu*YaKF6XukG25UHwh@& z(RFESq8Oanwc8~ZT71I}#9cX)!2m~YpDI`dEfif&i_R{lXQRK^uZ2#qG0>9rzo%rJ zfYr~FRK^|rY_x9c-E+>rlYz+kZ_^nE6RkiWMlUBafJONF&A}JWXt?J)`S}A7iD#b2 z-Fm7BRR4z-cb`lKwh0@4aw7;p!)X7;iLmmk;^n`V(jYwAb)YpPvTQ6!F&uH0lrble~orWQ~o;l>^ii9 z>HM#STpsC)rmoN~KF$}T=E`(W+cf|z-eRDA1TknCj)xIcLu)hj+3)!n^4?8b-$>O(K-Nt-g!&Z((g zZ)5Y(nQofkE1O{jXlqLO+?Es+e|}@b*=~p!$Rj3uEP~ZRhh8p?Zb(A6H9mL4M5hC( z=E~dFD{#-S=HFhH2CZrhGk&>{kP1TY3HBdtgcTW|@;=OtWP+{>vXh50V08hbWT6yB z#j4Ld)DOSH0YhyUMj=c=*9higaB$8qnfm_xh>dVhbxz)VWhuN53*W9o{C!a2WB!qS ze;1;0pZ(5~&oR-;nrFY&3?_i4(?Pn9>fpRg0}a5ue`e!WDeyn`^KFme5Y!+4 zs3h@07HYqs)rGu+)y)zAxJ`xoSI)G>y#In=MEqgy^gZfqi27~G+5&Y*n+X44@&++# zRn8f#nIr>WZvWZv;wGH?f8KiZZ8sf6$>o0^Dx;t-QQJL>%fvv_I_{CB8=^*jJx~16Cyu<3ng9ki>}=n4lL<(ETQ<$y z0d@M2qB!B}1hjW)*qO_ZVPxajJHjHUN7Zg;mhGK7SR`mWGUD<=c=tUvY1G0{xVLP0 zJodLe9MDXBg8>BI>LaBeSE)g zDh-TtshpY2l%V^o*rTQ#gjF3oSR0r6Kx^TaoojZ)QNWF3Y>&?M5M^lpdDrcKT)>fg zr!L)?FdqFf*!&{@0o+%{=1k(e%R?>SQ-|GFdV`AACDK`6H~_a_65;ZW4o<$#i0N)+ zquZTR6Q-`_qD~2%B`$(D=P}EpH~i%VqlT*@OR{UFpg89Ein}msWqGkADSz__fPEWx zmh%^^W?Yev-3ZYi6-TAlOZ?#btoqkQuRcWpjOmWG_%OWdJwN5kkA|qp;rXjKNNH%- zs3{pf$LQcAf1fGFV?0{=eo5q0Siv?r{r;t?@x|bVX$dFFNI|#E@%MO8&On_$*8CSL z`%3Qzn0B=4)58lKRJkeRbLab9bn4^3ZvWIm1%nOqZ*D_k(X0w-C;!F!5 z;${AY(wQ4!mB9Ck8o$SE;8h{7xwJ-|Rn^JVe|7$u#?ymi& z>V=hzKmT5~;12=V5tzGSUl9$^L*4DaFBpf4|D4ky2uBhFv=Km+r%ZHq)e3e0m@hvL1nV71Fh-;9?8&^2aw?$77a z(1KlyUlrcqp;Zy*CkHNu5sHsLCaP`$6w`Q?J!2X~5y$zDdN_iFcKuTPR^6Y6B9@j) zuRW<~`}`+cJFp0-Huuaqk5%G_%LCgPQ zsr!o2t#lN3^ru);Wg%LbJFjYtFBv_!?aC2<7y)QNfOQf?f9Yq>9#{1$9XM*weg5|= zM0%4mUo2V20=HJr)3L9^=QvQT3!xOFM`fPW#cKs9Sey2s{7E8OU2?vPq!$7I(A~IQ zkz$bY%Kb`AQXoX+<_26^CjgHB`mff{6M?eyDwE$X05rC%kAyyOZ}49jdPkRuiqCK8 z(YBCKQ;qri*7Fo_Zs{*08#h9I?BdOPx9_KeHZOngLql0;SCQ8CxReMwo_ewhq3*z1 zU|Q(z%0acMXM+COp9|2^j^6uo|0_z#NS~3Jgn*U{NVt^&qo#8TCuyKA%RgR3z3_~M z{`^|-yfKQ2N|BI{7qC=dd2?#*-ewPU4Q254{TArkF(PZoVyK7wK|Cc`ICOrvwV`UU z6AA4+u;?lJ0P4ouIEmjT5kLoce?f6K4lI1f+Qyn%j8^qbba+5tLD$TuU+!Jtft1gK zlN0|90zk!Jq{~A<#>HE4izHbf$!+6b6P^^KwN2r9@%>ro`qae8utRKA`FZ3^&K?R_ z|1~n?*++=L>YH$*j}N0UuWRmJmZyN)f4d`dCVK&=e-9)yj2#2WFOGOE+yw6|jD%Us zZ@nrqUmluvaUTHNj=jA$`vMExjXtuedW#gDaHW5-8?<~kL}V;$+nD6L)f81)O@d`#`N2MWz@Wbq6?Ld5>mp z$PlzZCmXiqvBIPZiAP>WL%_7NrkA1Ied(K7esQ< zhmepcSV`UT$Bz6zp*3}e*P0*i#i+!CkFGsPLoGF%2XdO@QOmho{$>g^7C0_HYoWlo z;O|-QX2SQ`4JYZ+15nTQFVFsMwlWS_WSN+NS^!L;+aq|X5IJyWS^lWhT$ER+Obs6% zhtAgp{kC>G8|w0=k@Mma(7M-MN1h@=8UK1sU0j@vHW=2dmXDGEF#f;H^D|js9eL9J zZFUZ->wK&}U+xAtvpzqWwFv54#cQk34aOqIJ~i!nWjeY{dHwjhUOKe&Pjy)XD@D`Z z>{~K)GansLHeMYSkc_&W3p{XYb1^_aULNPWLViNN;zo*kj3 z5|9@9=XWEFpeC+FEUTge@QDgze;k zoJ$8y5k@h)?FL2daz1F)N-!wCuQsA1CT^(dtEMI?{r87~R}syJ!%g&-WJc#`i+>{NXeC zU%VOU*3MVwTbGPOw=#G4YYNAsRTD(**VF01nZ$nhBXvAFJolHarNkvz~oYfa>XSxi(mrKw>% z^e?74x1BuVDnkiJ6UHw&DMANEe)7+_2qWYU>HDmFh~{d!>=&9CjoNqJcgQ#}!u+Ue z)Y^@sf$jTBM+1!1OV$Q_a)x@s@EmUYzJ)31_~!pQqT$@Dt2z?AX^ty8%hUY&djO1f zU3>!aXQZKZ2zJNw$N#HPxH9?t3K(sc93L?&garzoUzqoO4)iIMedRjfe9^Ki;BN2h zLWp2kIAT$n40O8A%GZ!NV2b`$O83xYpW7or&dHOv9!#Quot=#_kEGD5trd^5|HMYcUVR^sH&`%mW9}-{4MF8U zSO56YQ~=PbjOYd_tOl+>j16>wC<#hoR>WHvWh}{d)p)N-LAN;OO>c&% zkKugZoy`A5>5#PTcVNUfB`b!LzN-*C`E1%XZMFm@e&&V^euWhqZ`XgzO36hn3r^*J z`Wp`vF|!g^J@!Xm`WBTfi-FZ)l-50I3_hq_xpUV-A^@bw(f1zsk4Lp7le>3EXQ0kK z6V(?W(sb}p{obI^bhPb<@vCDm7J#LLCqDfG(WW<@IOmdKMDVldfKMtP)Q|Y>jomdL z&A}gbpBj|`W*oWM9Djy^R=(dc`%io#nn=kt{6k`+2LA@eNhRC^$an9G{!BnuxlnWd zTTO+?l)5GNJ9q#+dg9ilwh?IE)uG)Vzd^Lxy6uU-LS46-+E3j+1)y4T_lag-$PxIKud3FyL(O^(T)((UkV;@>LONo5wF+BWGs= z%m%m23%^2iF@1Zt>LQHf2A53P`jrOED?dNJ3-2(!_6X_CC-CPw-M;Rgb2h5WSoKqz zHx)g&`t9s@U!hMkgzNNyTYyHD+{+>qanKMpHzCMA9S%w zAH>5wp)PB}+h;Wp(P+PpVyz#F+Wz>>!1z`O#8rvxzDYS?wWs}wwF}P4>&{MozlRM7 z&#&K&x+Fp??jB6Mb20~19n?sS7#7O?cC;=l2F?{e2)(F>i;gI|RB^~^H)Fr0X7eOmO&dF331c9y2 zw>zIxC1~%sv^pnFPs%%`*F@dpbbLicEK(_1pCy8fw=Gzbyy;pvI5^62P)=La}($#6`qA3Lq6 zD+ZuC;@q1zqkw(M!{ujA3xGdoXziRg(dhoB{-VQo#-U5C1BW-SfbW}S=JT_s@_;yc zWzG|S0^0Z{GE51>TB=V)-%6L!fz$aT5=Ui%{F`Zl_c} z9@TI8$Hj0@0-QZ_yCaEQ)Y9{#Zz7DAChYjmuSlSR%Hp<&HQflv8Fw^zenJWwXKTBh z5EhFPLht@)fI8JbQ7^aSJ`|y`i+9dka|2dPUEiL06ru_T#(aI_*_{J;Uh6H3v|P06 zu;i*j$_5Vl{(!#O1>oiN)UytV0;Ej%N!dMPJc`l0yfy9TJhWFj!Qx@yqC01R=-Z*t z-!|{`$(_qHU?tZu+vpyO>gK(acRw7Bb}Z~|82XR_vc7t$iVa0*y(nbNBw-?;&41@? ze=Y%(@ds3#s{)YXe4NnXOhCh5-+P(ChEd(SV-5F?@X!VIm3?cKTy!yRdfb6^0<^Ql zur+Wi9oVn6&bPkJ1GrIwpp}^rwVt0ct@#cU-50)`zE2PD5cjq3YI1L$&(Z(&)O#=u z?eRZ+_J{;l)t3hGMPDG=`dUSe^gRpMpG+A=ipxfazmJXXf_GLhDXX9kBH}nxb|Z!3 zp)X>=03ctI!Pda!K-U4c8*T~NxQSIej0}84BwgK-?{~%Lwkl5uAia5IP55H z)35|Abq;>y#V!W(cKl%9K2wY;um5u4a!V?@dvfV2eWeuLTDVD`FM#`+^2a36)_k;f zNU+4&1?p{)=T_z37lEFjN5g_-9PsBq8urn00qpv_5vBc56y+nkMTLGYBPxcSSKpv=kP)k&94?(Fu?0~U8eDtUNg`^LC zJT$+>@5pMHgQ%Y;_RLzr1*`5$m@gZ~p(rty`ndT0`F%kLgZzFKpdPx?v}K#&e5}FT zxv;7jywjw-VZi6|{jUeR$1LZf9UF5uERBX$wNCu{7=!_4;3|*KPJmJD-#?h%-&1LIJE0#eaGHY$v>PJRQ56&xDcef~k3w8&vQk z=aC^d5umjn{#@g^;LG{Sug2-`ri;+p;||^nS1j=J;l#{(0ev>N_G2Q;6482gU)_l| z===L)+J#yktbP^F+|pk@8f_o$+V&Fe`xZ~^%(UP%(7+z^n;q&eIWq}~y@Gg@u)BeW z{R<+e#>`%K?I9cd{@bJ)l^ot-$4AAt?Sa18BX43Q)>w4umPhv~Y9Sgp@NtP#fdrjC zaYSWZ8xQS$HHRlu!RPy8GW+DM7<9j{QhE&6wz>KC-L*NNqz``xW7?@GzQd}zpu)8 zCIW5Ww#qNEve2;4*{=;Q6!2(fUqyo#toXQ4^69`Z^!+)&@lA)=U~b;=x(?_=tMpm% zbD6IY(ziHP|;6(;z~tmb>{T&PqpBn3{v%HPHW8P4v@{NTAphzHskb=u@~f5?6C^ z9ID<>a;FB~r4MeZ z!9Em%Ga)YLpN@z34p!p}+ePR@j$$#lR|bP`=(Y5+(=s&v#1Wqb6Q$^X6~`CMhj}=3 z)jy=59l_|0Pc8DpO%hQ1Zr0l=1+FM%&zv1EVFa9W%`@%$EFKslCuk=A1n;16uVi~& zxWIpghxJfu0eJMTcIuaO=*!&{%e`6)^@kHFZ_O7AQC#)R`FGAz&;d$`Xh&TyoB!K;3yL)6KL`ro%(D=HM0qaNF$+^pAXpmpOpMPlz_~&8Pn`?6z=RoVEy@qa}f56R^FME###tj*R-BB;c5QBk#3#K=!biSItc6W z7t{qWCdZ3T@jC_D;7DgT>(SB zPI3Rw-#R%teXesJs%E&sKf!;%*>8mtaKbrZoZNu3!U^l-1zdDaWG4)8wK!3n{DB+J z8F2~%?h0qQQyB2jIjfwoz|-QacESNKoQuYZ1iTe4TBkTLLg!*|A_E_bi_r?hRqjeqzXBCLBco>~40T$b_{v@H zgDjn|%GC_AExu}33&_FwXy2YR3=1*nd0uZ+#DpL_4cMGGk zbO9@ z!76t-b%G^W?XIAf;6gO+O6o*Kh}Io4Lv$eqcNKNACB*1nNtNS5P3~3HDT+|Ddo^{c zF4XR>rcSejI^1ii3S1b*V-2-b5r*}sqn7Ey$Q~MMxh0I^QBPIk!V!;c)aiJm$&+S5X<#zkp7ZPcF?QCiOd>QY^l!P8D%W{EO-4pP;)Xp`plH^^uOk?|$g~X>g3&95rojPTd7g!Y?-MDZ5Ww&7wB?_%0^ zMU33LgtkK$qw<#1c3NW8-U?a+E>`2Mr0r6~YP~CHyLGV!ZxwBiCD!O&Nz>xSn7pfK zdlh5M-qo~yx-oWdHEq9TjKjN@ro+WyMy#PVD&nvs>S#^6IPwS$t=SSs8BtHu)~Xn59??NNq8n=;VWb_kjCG7aX>B+%#;22ZOhLx_bkUCM$YdWA?SzF)@#&$p z;}Q{{UfQ3EM7hs>+DToa%EwGQWl2=~SZE!%B#n=ac3P37^%w+JEonwf z3f+WDH(@g9-HLQGCWn4Qmu|;U=r=9t4h*38;4(124Eila2G*BDzpcw4`y%u^mJEuo znBI%aM0|_scNLj(-xB&ix=fX?oPN)esrFUS@8hyGzDoK7MV8jLg8r{A%iyb`KeS{S zeJkl^T(-%#ivCEEZT79EKh|a2ebw|QmTZS_E!~34!T7DA_bGC)esy%KE{E)=q4!&I zD1P;H8!i{|+eUw?$d&sw(4XmYReoCfb4#w;Pe&iX!82U<=g#?^tYCLhaXBG#8EIKJL&Hf6zs?@`gLLbHzYDU`VpB07LkpuKExJr&^U6%Al-p0GL0Ofe^V5hM-J1!>x%3n9rPcTBF9K4h7%rO{M{JNr2y;i#c*i^ zWPc39wGUAI{TXg}D&il+a4)6G{lgd@jZ~FCmf_h)Rr})@UU-_upTzJkrD^@+7$X{K z27fZcr;ldzPhnv2bd!Gu!?%=f_RnGXHPY?=6voIty2BqZ{P7G-0D}=w%D@J27=eup zasa{@)yJR&h#5h6CK6E02rgyH14kfDF+)^$ABFN zyu#G{+^G$*6jKosDIk1C~)X2978X3ubd`BS4NWmkRQJsv`QUp7yi;>od zkVlyq>3s-gR1YHqFF;21GBQgA@=^C0S&ahKC^I9wPoN%UVdUV2no%}JZmCc^YJic~ zC^U?+GxGa{#!-U|3SMLyHN+?=6`4m3GYT6;_E8Q-QJ=^$$_b);#F!vACbd+I4f0~r z8pY%w43pj`rUdyj8F&d26vSkfO5{OdOje^r6@+E7`y}ch9Fv2WYJx~iZmCoo6vyN> zN)16|CcjT=3`$`lc$q0EgDEJLnS*ke!bX`rh{6>0$sDl2L5wfP1T&bD(qe2dhbe6= zCI=%-Szj?FSj;TOk3)isnd3^w$%9Ln;~U4Rg5}H!edE-@3T6p@ye3%5oLD+u8(hJh z)HvP{tYS{?8*dD*WXkaqOu<#mDWwz4!PU&EjT7v_YUZ@Q369`erUGAr30cD|EiJ)@ z)G^B%OUNM_W_e!;C8VCI#7{&*wlSxdPLziKW&(#BNbC!0drnR81gn?pL7^BO1HLyXM%eUlv_D6{jdF6RiTP8XoD$l@tin%0LVKBuN~g#}?=u%SPEm!LnM?Yns6#ExYW!49 zsEzq^=~Qj#iUH=*#;Jx-J9AmzRAcBMQ;nZy3LRqBluk2;4l|cGPP2zPm@E3GIYOOS zwRi<4%#F3OQ~`%s)~ZGYISj*E-KU_0`LovGOOdc3)-R={@~|-0uZ^XuFf40rU#U6_ z$Ew4ZX~IaXb){w6usGKG#xg?~nYE#>%ovu!(%{QYVHvEArRC}?r8CXp)vSGuGwtDO z*8aYkj__KR4qt&8y@u6TT7iY*ep6!wd9;Sr+*d&vUC+|vXCb4vu@02Zl8=U5gvME_ z(OTB;eY4b~b*vWrY|Usr>rm-z?dTTPAC0pOqYbRXaQPkG#xmgNm`1m=T1)4cM|ZG} zG|sV)HnNWP&2fxIS#5X~CZdydtW<@K=wcmjRFNZ0tP_1IN<9z^Ry8ItTT=C3=wwLUw!k85rZrve!eMUh;_Df zzByu;b*^!~J;K2{-#6b8;lxJql^CoW`|r|9EY^#Cp|O&T#jr2-RZ_72>`wdw1RKP@ zRJuTp4P#$!T%f{Y*;o1&sIfS97k;4zOJZLwU8u#zv9C2QG+@c>>wOE2*c7%2|C0%u z!R{{o$&Afm-)Q{Fj-{|~_Wk6*0(K9+3KPj--zu%bMsnD<8>`5X2>VW76(v&4?!_-c zB8%B~OBcx_OW6N3E>cCx+4uSusUsEa`}oD0NG1D0>0)hU1^eH|#fC@~`(fW=V`L@U zj9+4ktYSYZU1E-`Ww;DC@uSW-_PnO9eV)3R1>9VzbIX*jcQ@PY+P!HGO%Cu zEj329vF-R}rl@xI>(XWBs1Ej<#%1;>Bl~UNGDj539>l9L(VgsfrD|++7yEsqnjCFn zf9O+FqI=jw_!=a-m;JG{Mjm~i{a<5^D%#Bc)K{a9USVMm@SVW z4bgV?*S_V(=s~svzrqwf#Qs*g!W=!!{@%F49_?WN=v(24cH%e@YB4xBj&oTp7U#ur zX{sgTFdSEFEd}S#aU-lma6ugRvXydN7{{Y&r3#1Tcv@GgaX5|_VU-3);&_*>(&FMc zBbrtjaAb~;b(Im9!od($n{XK%-?G(aTn@*tX|)|k;f%DdcHjWVpRfjlXK(__)?o1* zPGHj-G9KZKvaX@v#hf6*F9^Pv6I}L-9ACl-Y5GNlmvcg`zo_vFP8i`=4PMC!FZ)%C zui%Vs`qh9}aU!h08u67JEMct)U&VM(>g9DG?F zmQcqbG}Vy_8V=D~M^Iry^)MW321c1RW=iuwFyZbK=X^ zYY8o!gr@Zdf`K#Ey52}=#W+G*91!cdPNjY3$({FYXg)6fD<{$yCn6M2K!{AEF zwqav9Txrubaty+iS+`MQ#N1-Sb|j{lJFaZIJf?&@zG=HEM$VmJ-L8&Na7zd~G%-r< z#IhaQm_TF+BgbKQtF1apoIh_3p%IA-;{8(AD31%{{o2&1 zio^2OS{v1II9?s0NfSrntt)HN#>Mg0H#HgJ$h-~KCSzO*PeW)n#bxj|mNlE>a(J7X zn(c8E-ezmFBM$KD33^OCgSVwjkB#T>wl?X>@d)oXtDX`s=4~S!K;nye+sh8f<4bru znhvPq<-DEN1L}AMuYqt-6R+g$Dm$o+ui))&I%tSj@%C5`8sjT@TEg$9_$uDsvfs_| z)x3R8zuV*0y#3bS9r3k19iau2u!h%I)`Cr_<25z4K;@3tY;B<=)bsR&LrB6l-hr}1 z@`MK7!KOp11TF7(>mhZ5j@Lr?LzAHA9V+`no6y4hqv;Prf`NC~`iC*0jb|VnHYK$4 zTFVZb6FPWDnhx6&jJ%`P!wv{eX(JdgV>@}r$_&`CUA*H>2J%=F?}XJr8Qa5aC$u7C zdwGABwaUld=bdb7RgE?CPFY*kV=cT6!V%3_8}D@45$)Ik-kGK&hOu_uU)CeWv4cD# z;izfs5btc+QS;bg-npiu_OTA$dFxTfSSLP8Xv2`*_6nU)j?)L8h;4wm_!EuR#`hXk;A{;)J{%B_;;-B zlteMVm+&W&Sj@j$_NP3tg#S;|pQ=PT|DN?vb)tfQpKwx>sN_E=JE={q;Q!ln(vYa) zKeV1SCRXyzgj1%(D*mIgQ|82K{^O=o_Cz)RiS?8tv6gQkbYPO!@cYU-ut{}%Yf}d~ zNyG2Ac2JV)`8L97BxxJ}Y1wIcQUm{4(`i+bmjB#(TAifh4-n31lJxu+WoNWWE&P{F zXADUO{wwPlV^SO6PWa1|)Xslh_Ln)Sga4-KFME=a|JM4KBMIdX5{#JSPX4LB4}<-jqDV|5kS1oIK3`-gMrc?BM^fo_8cWAx=aTlj4Rr zm!sGeFT|x8C8uBz*M5|e;*Yoy|3*@R5cl%G&c4AT) zNI-cfHkE?}Hg}R!5oA<yv+sntkS^A&rl8j0?|;z+GUaKtW5 z+8P93-i1x8LkP`XtLvp4NcGHeXexX^}DgSJi1cB#wAZ zlcq=F%dctET9Ab1YlbufGPeJkF|7?D6R(@n+L6Ta>*ll$B&qqjJ458=|f0C`Au{BFjClj)1K}?iu!Ll(wzi= z*n`P%6Hv>0uo+$gT5}IM10$gK_fRtY1q|XXBqK<`EWahs2otcHZ>ch{0(So`bp}qr zA>P(xkObWF+uDpc0k8SCA%iU7_un>VqzDk=9aBbzKu~_ioRK3CHs7&lPz0j>JB|z> z5EFYbnGAuXyce6v5lEYR$(e{i*56CX6bp)pcah9u!MO6f^2`#!_~yH+Ou1k}|6O&a zLQq2dN0X@(Of3ILn^_^4)clVjQze+(|Bo@VQXnVZGi6o@rj*|^XI2ZQHs7;nss+>f z?>RDS1q$MQOx7AfY59F@7Hqa?zE9552+I5KQy`OENqm50Z4*o{e<06l5X@+Ppvuw; zX7)c&XXykL#D6tedcmynf3;aHg4xag8nO(6IsN|{v)TkI;zLtbyI^klLvvP#U|#b> zdzMi!zyF~l3l&rn&6w;?!Gdx#HhV>vU}3YFoNW^P)NiI__Xw(pkC5zM!J_g<^6dM9 z#m$dY*=E6#{zvL;i=dkLSd(oN{9OK6n>`>{+Wgp%Z5J%-e{9Si6sU<$OxZ(%n(`;+ z>|w$3<|p=ShhRnj6Gyg_u$E}SJ+;7jJ2siilJ92=qo@m45GK5>oZP;9naBH)ToQnv5>$g#I#lmgGr$}zG zaC`Yvd2WetNApuvu3WgY|EW4xA#5N%)8s0JyUL$wb1Q_qo1YnSRl+^}&y2a1LM`#R zDYr_vxBR&|w_3Qb`MEt;E!^M#+>u)=)DZ_Td257?kY^Ac?tf{_ zYZDrXuS|LE!q)Ov=DZH!k>*$SJfrYv|0_oxDr_U#G5MXsW94>iewXlgvz?r85}xR{ zQ}TO+?Znqeey{M)^4IeG`@)mWuT}YG;i>-D>U@i^gZM_1Zxfy_f1}MG5T0p%W5|ao zy8buD{6V3S_|}v^Bs^RG)|@{qJlFizp6?Kz?|oLJ1OGDt{-Zgo!RUzf)1LqAUIH)D)bki}+qcA&IV*zt>XYMAw?% z8z^Ma_5SxpN{Yxt{9vMFh`P%^m?=4;8_gf=6pH9({|5&JhLRu|EDde z5dGWypP@h{df5M;v7l09CVny%REZvye=-+Tiyk+BvKOdDPx?PO3Tj0b;xMLgji|4D z7+Y8;vNjKs3pJwt{$WaCy~sxVj1+DYJuUw%FKiG!YyPY%)QX<>e^wXjL<7VxnnJzk zMfn$PVT_u|f2uNaYrORH(^c|shX$54lkIm&QND*U5uH7I*?5lKr1aicFde_&0A|7dT{RS{N#DRLZL@FX4WpgW_ip4=BcLB9n9ISMoLM;)8=-ua1<>FAAdks|~ z4kLMNq$9?0YAkNeKwbJe4e4F1{`k z+sJQpCkY_=`!d`lRHc6;!%ITb`zJCm61vU5fZ;D;kOBmZAPG|$Foh8&Vd(?rGO!Z1 zEue;hlW<6Z8yO@CR~fjM5hvm416vtn3EviYmXRVsNTa$L84`hV)FVcYM5rJ2nn96> zY@@z0fJ96R@?|n45@k>%lOvJpgA$pDL}m*rV2UNhq+kKFSTar-JcU^z8LtnX%altd z*n(@A3P}kmWFu24nWzle%dC)0(ucG%Rg%fJkh9E6iJTPL&8(74QHDNZR!gSpLtiu1 zl4-WkZ_HYWf)wV`uu7B{q`XC0VG)CbCVEpKRCyc8{cr6e(c$N){<2r?Br! z7V9JDvdxkuw#XW`MN&Z4lOcF8hZ)LHhRL`{nBW)Defl+lma z!;G{-VTB z;e<(l)#K;F3gopmd<_RDts@aOa!As3O2S@FoOHdO(8?i8H`oYgIVn;NiP+7_kZx2G zA8~S|oAkuj9Ex^*2lJTRnk4S*t6V9sg^XRn_DH_ zs~q!)TP@wEAM={4mhQKW`Npl4>PT_EyfxBBWn3h$PTHi8OXO*!&9=A#UcFRLiWl&< zNe?LFr|=r22lerDd0Of3w)h&JPTE3B*vQjM4=EG&@>-;S=o4Cb2I*m2!dYIM)Ib{B z&1;voD#t$Jbx4os$G+wnrAKXJzwuCM8;R`8@01=>k|X(D(&Kt^BHtuEVIvptd!+58 zL;=57`lm8+3je8hCD!mQ(hgG6M!rpYTA8$$KOjA$Pip1crGMFy&hiJP zMpAM&e@J>(nf!=9EIp@Be$97C&)br}@ttHSDa99Yll`qsiA20)7xXEK2u60%mQsND z%Q{J^0whRwNtrqY36ovcr_M#NvMaXK8U!cnBBgCaNV2QSw7p22?3zBU6(P&6+tSV= zDKZl&y&K7pbt}^!Avv-e`t;WbMRwDc{tW@L9#V#{fFZl3%!m|lWViJhi2_7+$Cgnb z5X*W=nF2wv?5;9%il9XHk3Ms*KrXvy%d8P7WcNu~8wE<)17+4;L51vJeO9YLC3|Sg zIxDD@nMv8*f-2b~W%eUMwd}Dz`?WwVdt%G}Ca9HJNIAa3HL^ZsPNc9-X4U5;3N^BR zTTX$nUS=cZ3WVEaPnEe-gblK1`rNrft?aoiw??Rw4UqCS3iYxV%Dlb87THUEUaQa` zdu7WzD_qegvy<|>h3&G}%KS&d4%r)h{%fI8_STmFO^C_{NfcjEr|g}Q5-IAEz1LF` zMJCw?8>K+hBO4+W2t>WIkII56qWiM{^aXQ8X4xlOL5;{F8zvQQ6xn2-m4$mn1F|pr z!d8)8_SIH+Rx~JckczrRL$YtmqDP`(*>`=>Ymr0t!&dZ7_~q}Z$2 zapH7`34lDLJK%Fbb7JELW)`)S%UNN+dVp6g9blP5V zT=9qlv{o^>*ykzjtT?3@6GQJ7XB7KRr#}+s6#E^ZzZO%9M?R&06N6&^7>2KeQ5-Ox z5h>vm2OeM~N|54FPydgyYyXSs`}!e-N(eg{0Gl3^=dbB_TFo+^ZFceeRN5Y3^TYM*GLMdi*RpE%;OzMT_3i9}0lcLU!P;&RB{+&7)LqSD>oH{MGw1#%Ee2a;g%3V_5Qld@YE}3sRS`&PCmTv{o7TPWMRS|KOyK8)_ ziL3f{H~O9@+F9>u^Q|M=LwnS|^+botJwv_?#MOO!CVUa1qqT>DUo&wH1^CTEd^BpO7P zlf)!?CAo$aNMiJn8%ZG~rZuIF6hUG^6g4TD#IB?ak>W_4KFS0sk;Jv88jw>+Jcw#e zPABmzsrKYdlAw=DAm@;T)-+FYJ_&+oEOIdkuB1uHr6f@wO-3#!iLL2b0PI+7G(sLA!Dpi0INxq%ei$Cw}^q!4SS0i~G~3Ng(o zEu^qYrah&V6yC=qP&!Bv)+|rTb5bP4Vo^FtQI#wyrHiz$k0qn@kfN>GS(IK<48)dG zw4~Tdb`52aw7-wtNEso;S##Pbqa+!`QB%f9@s*q*$~Y;Zk268hAtzdM4XC>0B#3KH z)gvcYa_y=51eO3@H4j(nm|G(k%wms^Vs=qcorkl37_ zPCivBwx?&3Pxpxl^c-@9wZxO2Pp*U{EP63nQ7MtqOUcSUiHu%OR$2RJ(JRPRkiVR+ zBA==Buc23y&-VE@($ABttpnQVb>tc-KuxbF*H#7$(HqF;`T{2C2>HBqpaG+qd;tnH zXS9$nRtDNLTFICC0tt)`a-Fr*lkuE<8IrOXo#ZQ(QYoX0e6>$1WAu>gt%I@{z2s|9 zkes0P@`cnXzGwnFhtg{ua$86#zvQu_O1WbA}; ziq<+di(Nq(fMVrr73EW9Yz@1b^0_azk$s*rXuZFUT}K&$_N&?Tl;O(#L+l31m%jZI zY=kmm9cRF4rhJ9s%sDNTZgEDF@^W;3I{D5REPABERN|}_?Mfuq$ zlW}?|W7hFmoLx4GWC}jdlP;zE zzJv*m4pj$}Xu#E_PJt86xq8&8ibQ*^K6RQVk-#;g>SB^SxyID#a1x7aLY<*Vl5)+c zGc`#vt|e6ulbpr1qRxVoRio%2`)%A!lW4RT&eTm6myhAsg{^@1AYp1Ih=0JPp7U>q}%f|sVg<<1bz!D}O(sFmK?N{bo`UDp^>7wT&`I5( z$dU@Ws2ep|GC>a&kIBvw^ins$*>ZuFx>=E3BN(J^(PTFYMyO7hBW;3FDgi#C7K~Ay z6-R~yg;5bDyl!8ztaJ(`Om$6lyU+pftW2#sj2m|RbxF>ME&%MzN< z+!VP|p&4zbCRZl3q`6~`W(lomyWpd8A(pmVakNHgN86)0+9-6Sd0_I|gaFMG&QlBV zG%rQokkE;?SCcm(1Zm!wd;`dp<^$)OL+&(RMZP`cLG#n(6CiII5mVp^5osj2fCW)$ zWJQ4#V$vv@0vW`mQ89&CkdQ`$3+0fQMpqQpK!G%drmztTp)oNlP1s<6W|=05Od5E&Zj}}F&3OqOoJ82 zq;M%sq&X&o%V}avNful|lfWf%SVi+!l+?i0v;a*>BYd6~h&kQ{*U_Z#aWz~|3sM{( zf*WYTn&T5NLJPr^8i<-{p>V0WsD&1$D76>0(!w>R1W^Yq0webnJ*P#&a+au*7Nw9& zMP0Og8o5l=LyN|gWr=!eF>sk&q@~3w%4$S|wEdd0M$rf@4s)VSG)j}fC)A=bTD;=K zkZ7EipgA!i(xE3}$_>Q2^dz|4T&zb=R+QU|_2~yRt>}l~Q*tquo}oBZBetU-)|_e-JJK^Tr`yB;Jqtdq7USvJ ziqk`4C;Ac1=?O7N&%sm}NL=Z;aD}Yk5^s7wrqWYFq!+-IED4of zsHl`mnDin|rA)%57h@Dz5+VH8BJbd;d)OX^o2DpF^*}RC)U6(<|XBmVYr_ zp{SDjm(rD*Dw%&dU4=Q5u3mBz0 z!spZhWArA)xuJk@`c2Kbi2xl2f;n#xsLQwopEnQGW87Apw-3~3+|isT1R61#F&8`o zjTv|03#>pB#y!OaX`mV7zUG1~(2~)DxtJAb#drW;lm}uN4;2?{0__-&G#48K9T}~d zOKpJwqYb{K4#YFs6_v06jy4bfsD7BD~-|+ zMi=I4n>2!&=7G86On&_CcA9kD7Wy zP(lu)7jw-sD4)>>UtfkZPq~hjK@HpeI=H^7O4pYYlF$mFRP7xvIA$rWI zO2j@ypE<1`A%qw)b!~2Wh8Q!ai*B((Oqes2x1=Fv%$fbSWFeMJJ)7HEAy&*;qTBKi zEOWNdiE^x$7B9WG)olWrd>M>Xmn;p-krD{=2eJF4M&3URJ1( zX)3xW4;3?)DDTyT1~QlS-)jsFVVc?8ZwrlJnv3qML!+4%%KJm1am;1?_a{OVnU*#! z24N}8<)Rkzuyp1MWs7}SCUa$f3n46rX=U@kGc2EJEqcHTD`sMp52RtGOq>1(vaoU{ z*5+YWSOwEo^iUqAV&aq!Yr?9TtNI@{hMi~H**t0st7F=W9;w6XnGVWFLtzcf)%}kq z!VspTO{+n8Gjolo)jYg~xmMX~AKuDb*WXGA?_dHpZJy!Jnd?Putng0e24$Nxyo+r)5+#>TlgrGAbPA0A7eTz9}k6( zGq?6Xo(R`rfi@im5xT5xq7L&2J(i2I!#+ZvwY|TC5Mjh}wRz$hVa(bgdcuk@VYw-v zNF&TxJNuu=A}m?%Hczu6tXR85PvsF<)^6p~ng~1Ap8lte5soYmn`dni0LxSKOdWw| zc`2U_ML4nc_CK4509oEP&kZ77Sw5oY=8^6!U*&WANDr1@|8qj5H;ZWV!ZVV{B8gtG zBB?C0@`W^#$)fbXkVSG?RGXJskwO+t^im!vX3>=|Ya#<#jQ*F6ks&Om&8xP^2o_89 zN*x)^Vk=(_MaHo>{jVk>6IooFPJ^fv7Ejb^9+i;J;wwAtqcT~7{!T(v4ohhB+A}Jj z1&Lm>qKa9t^0hRolqKqaEsH8=iEZ9wMOCmQqBrs=70X}wrY5SI70~~tG3q=k(B^Gh zR2@qydaI7AX9X$W4n;Mvg8Sc2L?NsYn=XTW&8$#Sm-)UHR+zHOeqSpqyuXXEuY(m~ zqxRhQoE0fjv-WkeqLgaszAo0jezk004=dWHJ8NGrD@N2U-=}58D!Xg;4YKz4cQ@`E zVa3_JYuh)dQmG6f3jk6N^-%ae(VJF)37)0x`lSDn{(R%D;WsiNdKKnp_ z4t1`lyb^v$K^ShoYU>NBTcbM1$-cn_hz$S9Y$b*F46ZeN@?N zALGH!>+dDRc(e0u`aENZ>;h3AD~8H0RQ5?@nCzndK3NQxU2LPtiV?Dpi8S&UF}p;m zsfh_>AMe*R#)Pm-ZTj0{BG__Kzd9zGU8d|Giiu;N=iT5sR?T+YB1)Z)RT* z4Vv$7VP8}Z+V5{=U+Nzu?C)UL*$jE^f6l%v8e;A5WM5GZN%wcLul5hg_V=*sZHBY< z_p+~vhUNRU?CZ+mn*D?98~wwL`$yOfHecHIkFpy@U)1}@*iFhWL;J_sH~YU#?APHS zHX{acx}0005%V}b&TZw0eVjh$PX7oY&WO`&^VKuXm~&V3l@({gxu^UpjWgri@Bb=` zv*fhce9MZn;ye(2lgD8>50&3);_Nt&`oA^CIdWQUzPH5zoHo&SbsV14uKYd}=frv3 z|9v73PgY&F^lpyowJh%DbDI;=Th<>nSRL)D~52=jF zdDZ_zCgXBCZT`!W2|2Gt|H)-y&Ku=_HL^g?+y4I=Wg(m{o1bm62##9xQ!R_;bSr-j z$>KQg`hQNy5)wH*He&|yDV+DBG4uFz&Ijd~eS9Y8WB(W-K8Mq5^UE_npVKG$#fmTH zXq3OC@ui&p{$H~Aa*o#KcUF7_XF&8@9 zj<4qoEB_3|H*mi6|CxwKI3qUW1_{laucC4Dgci;><+y!9E9ZOvI3b~fGio#8ned$R zLo~rk=;ZvToRB7Taenrr>+Io-*-U07^m2ZQCgllQ&Tr*pO~N4OPyb|N!U$*F=5JfV zC}%?SSDi4%nNKG>Ka;J!OEE4s&Q&l<+iTd1WS{>&^Bd#uX zidUjBce;2AJJEzYLp3ER(F|?YG9^CIlB2x1M#%(L_F6}HElT2i91(2Z88z$8ew$}lU%v;#JUzq?%eq*U56wO?gFi@ zbCNgL7(3l7iO5|jp3Y99au=zl2PHANi?!3^leksuUX z;jUHbI~-``uG8u}AL!r$SOc#E&$;Wx2J8c!+zl#&paWgpjaq~F13g?k)-d}(FL#sJ zua{UVMrr&mB8I zJH?8(OFX|U1PXTzI;sxC)c%GMP!ElNbZ?AU2WD3ah z#u^)@y7GL)#ulmWJYSWuL#hYQPiyR)>dhl!7kZ@PNJ5|V|i5Hcnig|R^qT19z9z(mRDK&)0#4c`6jo`7wi@Q@3qIqo9;^EXd9!I-) zGBuIM#hMtVrSN!S6N|KT9$#hRke104Xic2ca(F_lsaIM)4-%WQ(~5bp$}}jglqb@f z#;29@#MmX-X%#$)cu85Bis!FdQkz!I3(ziUN;}UB#4c@5tK&(9$n);#`T4=);Pk$te27bCVPJE-Nwsw`>`4)XSEEt(FF@Zzw`+7FKMWa4Gr2gi8v zs%66m$9W0bWs?VW_=#9c!*pGKlGxHBU5}rvvUEt-=O55oI;R`)Q?Sdu(vA74;^pjg z6MmX%c~H6;|Dbkxe7Yq+9lIhs-HLxmyrL`}%g<1)s7<%yAJ(pDN_XUEVpq1O1N-a-lelZr4eMrbZCdQN<67x$`nA$^u{Nq|o)1eT4Db}X_ zPy}Btw&^|;%`a2g3?GW)pU~P&9!lhwW3h%ADg2XStVKpT|C9>rkdetht;ITLg&S9NF9^J`VBhBF%Y=d`OPGZ6lHtexTEX8r}SoyFl6{za9Y!{Ju`C9R$F z;SPQs*52#zbN*$qJ^OGc|BA{!=x`VRs@6XKa1Xy8>yUl8mw!#{P=3FJi>3lu5Ld(%5M~}?mj%mZ&IxuK0MC9sa-vJSVw?h9St*e1-HbG7MXg2+bTzg zOnt!}t)p|Mk)Rp7#w*iUa96yBooOOKYi$H&nhEY}*TiR93Rb( zs!Lh_Dn#~CSKQ_i5Il1)(vMm2_9?LO=g0E4lH1p%&tT@3t?Z(NhL_rT0Zh~3hjT_+e4Z|Tmi7YwVm3}-h8zG$~hW+Q?T ztdrr9X2Dmnlf{u1!8etY!;x0Ocde82kq*Hqmf&^dx!?y{^5RIR;6D{1=t!5~rUsa6zbW!Wan53XGvVjawmaK%BZTIX zo!z<7LW`=M!?|(7Wdl1Wa}$M@w(f>UQ-sSU?iNSWg)6Gu9gb!SR}Q#4AI%Y3+3xZ> znlH4L>|!4+7GkP)1syFF+6?T9KUyxt+V0LiS|PNR>@GX165^_M*B-4Ft{T|gbo9K? z&UR1x(K?~MWKZ|edZ9zrp5dbn!qo$NCXXUQM_UiWyk_AViHAjAi*Rj~heKYgaNU51 zb6$rKu=VuHdoElr@nq+93O7`F2IX}LHx78l=k*BjwqDtJy~0frud+O?aC4PcZQh`8 z%Yau?-iXl2c5i#$sE{Dp+nqNibgtSvoHs7qIIJ0w2rd=tp6$|oq_4B9#16Q6Ghx!d|?=UYL$B)(<&SZH^Z zZ*9IEv}eG#Dc=$Du=Q)t2Ov+0Uw1wp@~ZM1&Ub?L4){&xgOIl^(XhZ3@{tfN3fv*z zDxyPy2jn+EbT06Qh_)oJ0wP3`kk|!Oh+IVqDqupC0aAPc7oysdvkQa}O+qd!2oOW` zDspW>AjB9THx-0HOj}BOK?KB-P`V4EA$ApIxF8PV3{WNu5+SZF)vz!H;z_6$h3OE# zit1392?++M&V@OU(3a*^m=8e`8oRIafEHg^4vB5)*@YF5L_#ksR6+h# z^xDE|C}4ozRCpc=v}Lpx)bTdrZTE}SIcS`_QS z$yHp3Vtx3)0N1(L2u`u(c@-POsS+N$*aS|i;sq6(!3PI;@x_*Kx-CDu*a|)*;g=O- z;fyMNZLuAEc!1wj>bORC|s1O81V=izGGfcBC)xJDAtT~ZI%Rs{@~G{ENu0wzlk_`GeP;qhkp zf+W!5cnf^7D$wD0D|~4n(D`@=TxTowI{qBKERnL0cfwbyq(R5K;Hv}D_~Siry=_qT z@m~0vB&h7T7QS8;RC|07zA+HgbbJJEunlfMJ_Sk|16nCuZT) zMQA~iMdd%{ENc2O2UFf-hS5yS|NqoBF|}K$ixxz!lxs3yOKjffHv5sBihAcG2vt3w4{tzE2&)QY?cZr>; zDPBjCIZ95qLcE){LrF{h(2-V780Ee$6-lfJGN24jF*S8^D&O^~gJG*$X zkEtmzyz3SBGy1WWka8Fj44I$9e}6Xpc-CnK;vKti@C{+1sSJN@pnvdhtT$qEE>gYG z&(d@cu;f?(<5Qae%n2QQC?^tj;Pnh6m*f_&M>;kpyf1rcj;_z$u=w?K)h^Jx!KBAY zyW3QV&*@*l3VKTib^pBAn-lDcu7+F1e>QZ631&W-?_Kc07af@`90-2*nU0L$Zd|Qj zun~Qi5oL+Fkp%$+XM0?4Sm6YNimG^L*Ex`A>JF z(n^nd>+%cj&^2~r{PwPf&FF~T;~B2=nJlE3`9Ay2x^?J-bq3{SN#kq4F1!pZx(K4H znd&B(L05Soo$pM}k9rYy0Ou%Xk2xA2q=UOSzH5QG5dYsje&%1b08DQEeqmkIR`j{2 z+&-O3I88!(s$@*Ox$T7Rd-tV^(%YYeh*HP5WgoH&9T~KX9*Y^JgS$`kG_;@EiH`J# z(9Vx31qk;#`DxN-Z**=~cI>&DQv$FRzub;ELSW;;6m9U)$|;C_;eln%K4jCiK)krp zCEl8cJRwt$eyB44jW;5u`6Va1B3-M{ulP$GH|+q-{Oz27t|5bG@457j zE!@WgzwPFyUK@5nw>_6{m^^nH2f5d8bIvIm-8MdF&xwLnc4#^0w2v zDJ;ZHx}dw|!bVhj7Qgr3Uwev<N;+#R-qN?KBhSnm zl&9o%bmsV?o74#=jBb@KMLfoTmm1Gw832ZTnR?V;rg*Pd@#}M``D&{1v9} z@zLMsJ)kTG5bh)<`l{Yc^ffxZ8_l`NSj3xALJi{VK@ah6-pu1fJ{?J=yo#kALQmuk zy&7ttaY5~?rI=G(9130H{>=MA zceKKEiISc1dcqHXM&IJnZhIyoobkmm;i)q^^XM;plTHDI)GgYzqAYeZD(*y}rmrZ> z5OLk#`nz_X`Y?dmoHX`*CJfeJN*;-emB|6_+cc*O0s+$C^J?w|J9AVD9-OOn+r2>J zB4o3OKqDKrIYWPR}6)xra zT!d@WBaHqz3q2>hM}ZC{(^+8esl3SR&o-bVJCs*`0dhX_Y40z^gpD7%3*hIS&_4_T zNdFX;*u`Om;=k$L=wGyt3q}vVHvRyuK>-3md(q>M+c!M&cVs8nGwsTe@kghks)wTkUn%fzByhuV z|Hb3~V7@^#>2m238SKejm_fGJL$|HRwsD(-kPst~vhrix4%Ce0zI}E-{$nDgp}!^v z*OncN-U^l&v#+%*FNK2m4)JZ0_thsc_x_x);(fw~1I(;G0u+T2hS=^bg+lcj~A)$_?&E zuf65u3Z~Q93!Xp5qoOj&*?CfJ!~@-RZ**^&vI|vmay0KQvIqv5r*qeiEy8)~;oavC zkF8My;4cS7$n`&7sNEv`PJ-^YRmgnF@Lglui2)#_AuTT5Z!Jiib@!@tuR07!T1wek zxxfc0-qo|Zi6_3dJCO|yZnsB?U6 z`ChO`Ip9HcYqSTbPPvXB%-xWe|Ai|)I!)-|y|2Lgv1G_zT zqMaV7K>-iq{ZL&6#7Q?Ik0{oHb!Jf?%W0@%u#CNZJT00F>K)r=dtrzC1E9k#{c^sb z;--^Fn9oB~^%wkLc5QI?d_Uw($k&C3o_VA0yXEQ=aC{RJ`IEQ$Qb8!X+6Ggz=J@?I zunxNR_R|lUFA$=RIm6$!0sO>us%$S`>tGK8Z`7TJHo)Y|OpIO*rl> zUo&E*Ghoa`+r285P_Ua`v|E z?2I(DwB*mR{j)?~1PJ4<4LL0Nr&W0we8i*_ zsCqG_kpran<|u^ggkZ3j;J@24ltVm+>j@V(p(K6EI}+rcvOGeM|L*N(i(FE3?Yv%Vq*)m1A#iT^4|un%ZAX z2D*~b&T@I?+8v0Q_!6Ncb;1v5vO1N1ej5oH->+n)I+?G@kqJL%*iw2^Se@L55%YJ@4TY4|RAi9JEE z1Ez%JMOGeTBF39F1s`)8qwyEpPd;O96oB9t!Q0V%6t3{Z154LaPYc0(ukj<6e>S5} zdF$c4_WKDMLfo7;v=58J&IYFR%V#JG$w(`^VbNA&6cTi{N^qg*>dLo0o%fZ4o^xsG z%|*|@xg*V2{+?7bP~uwseG|glv>nN*T-x}+X_4uBKqYMno1$0`j?GlOp7(5`0{{{r zs&9u79KU;_t>Y1{ehxm4_#=K^GKlD`zjpCT?kp5cfgQ_?Yi_$CJ3D_Z)l5Ta(kbPS>@DH!$o`}3B1og%jlGiRk`8zNYkgoUhp%82xb+P3ItG@uD-`5?xJ_AB5 z^vYfKE^3ShdOxhJ&fO_QsP$i1n{;tz`at>PxZC{a4v3NLzc>GV!QC~$|9%h8H~WyMr~-PpcY@fe>M3vF8OlWY6V4 z-)U=6ljw|lwz8#8i2TSNUu5y#+!Q!y(PH!pt-{-3EpfHiL1|t`?sxjJTOfknI_;OZ zY|W+xfZhV>Z1c~K;OoIZuTtlrV6!!?nlv(nggDKXDW2ZK*&5Xhf;CsS%5Zsk#+Jx0^*VO zV0jiDU7ZnRDfyE|MQ%Iqs_qz5H{y3K&HW=dL_$*fPq(ie*o&$mwQomNTd^-vF(c9? z+!SRyJ@@HP?`bi}p8JXTUwLTY;7aJ$ZJf#iH8TR|oT$UWdieRo1>V=&`CyM>`N*D= z0BQl)x>nx3Wv0!r=OMIXNM!8emz4Q?MsLy{)?Mn$+D1#v9ttrxvS zf!f%`PPXhQ3+eGl*)cU0mp>11ytDSFRJ3w9zv)QsM zfF5nl9p^VJq&4yMPPFvTZ{SQ?&6vSyA`-Y;&$ika_i8qf(|##ry%dKGE@LnP^8cY! zY}kKfoi{?T>+Or}Y3FcL^?-_vQ!|;hT=2t`gEJBinZF0hW8U0*W^D!n3oSO~{`f~q z1}E?HXFCd!Xy&wA_tLkbD4V01Y}=c<2SoPM%2KzoP6K;x%=}bV!9(8tTsbqRko5v^ z4Ne_mgl|V2cZ92ASD@~yqDHf#Igxn!SvhxJn|RH~^(g+ec0nB6XvE_Q^byPwdt(>W#PX!6gU2{=#MBw#^2%%>JdQ z`|gVb&bzQL;{stHkhr^$zIk#QqW^56_5JjJ7QF0va7f--0TNmwDpOgbA+7~u-Ox-m z1)o*@e^j_4 z5#Ow8OPo-osI!;9m+oPLV}cmqWF_vS9#CJRJ+jyU1}(ot{TZhJI}o9dZq&S52#o)- zc+22j+-d{VN$)$fq1FfSp3=ckTK&_G=ug)&8y@%}54T;epf153oC`z|^Xdp21xWqg zW!~-gP{gd>_K1DNfe3E#-Tjr_iQ6>~$l*Ryoxbe_W~G_6ZQO(!=#tLMx17VdV7}^r zXM_*xeT)RW`w7R1CBC12xp{*}?0my--`IYv2>4Q2w5?>Agy^5aNn?Ja@B&7ekqjOt+NeCBF*;&xDf&3D&l!6+}W9r@Pd3~`aMy>;vaQ(W^bAct(a z__`kj{I;>8({dF`I(mu+w?r+m|yx1M9Z*F$=K#WbBI8=^4g1g=as zI_ZvB+%2iZUPpuWCLe|K=8b40;Hn!@`PR5jL!dP2nqP{oH`tr~W6JLpsQL^S+0K$I zr66u+?7q0XP&Wb|Z*CdCXm$aqZ4|{O1+L!&uan66f?0TLt4Uf>=y;$DI*}`5eAlEE z7yKHN`~JKiD+6zmCz|?cbO%`P=aKaVu|r)?I=!MYQoaqbJH*(t`o=%C)ZMhr;RxRg z9Q~&AcbOxOy#$DyoM}7Lo{ZqW#khREjz&(#rD-GUp3}kT#Ivz^6R7^+y^hQeICl&+ ztdMsvziH>8d`7t9ol%Q14~#i>sY&vv(HLK7!kM=uhmVZ7R0mvmh2}qj!xK6ekKLmp zLZ2Z(uMRa}onh`fp2f&bE9;|6cJ`n#FtC_1_%*kRf#@@LWH^06Ut-uYy}5k{3=Sun zfU6Y$^hdnOa?PE+0?@b_dY2u91_byoI^G{Eyb-76w^D~!u6MJ zaqIPgvhSyLt2gaI&YPWHN+shKpsl~7{IKvfXV7Trm?(a?DeW}gp1#Z1*M^Uz-x<0{ zx@eD5C&HJ+xflKlkPhdt8_TFD*a54`n>n2pR3t2s4Y;W{4m4 zVt{D4Z7k7wt0vOG?N&M!`aAz+p+ov6=J~wDf^QAf^W6UY3p}H57c8oqM*$mO%|4m_ z9o31A>eBpQUubB<&)Ly?Kck2OY#zM3Adm)r3p?y0**0+%pg)^+H<{~)WUtNhk9~s+ znhWH$E#5-ANkST4XLVMmqdac#yH{`r2hD<5pLKM@qJHf>;=V?Hb{nXB`ge$E-Na`6 zv17blzsJ!2PvNBmvop&~-#c!uJ=%WBRe%(4_@Z%ifc1d5c4X(WZ#-~KX-SxR1U2e4 zw?f0;`$EXP+y~RX|3y{NNy9K6n52N*+>xQBFP5V*JlZF7fn2Z-@!}4;6>r~yZn^vl z+!#8#7UVSFpDFS|nKm}zkOYR zG{ocXEa^rS8n@{^B5*hFg~5&vK~u~R)VA=*rQ@@;O*;|qr3VW~<}RfT<9n88ag!1_ z2soemed_kFn6;)rOKv>;*BL;?^6)Tmr5XgRb|oMG)b57dQ`d11rJ|T$GJjJ4AeujQ zec!#rHVaM8`DYp||KUwSy0WD|%+5ES0S@IgpPe!0hqUjgYik40pwgtySKYA;_0n9U zxz{c^_{{{S8t3dgA^_8m1yhIX95%55)!$wH3}ZT?ga1@OOhVHDM5F5q9{2485z4w~ z?>DUhlJf4+}^5h3DGYg&h}u_J^o}nSnTS zBYf{QW#ipGH^g#V+|FI|=c8=JG19TW=n@T#Gc#OhI79viu#}e^s!>{i%>OPWKAM3} zojCeAO4;uX!bUNN99mF{e0W`^V)aZvB;r1G!F`Uy9vz@4$FuV7e6Tg-)XZwFD+(2o zRauu`&V>-mH?`f)0#*Y)^nK$Q6Qiu);6}oZzuMqcvh*)o-djDmVpn1Dy z%sjV*1V&#vHNCUzpH-#r8t#iV5r9?~RzEQ~j3V*iu}XCVb~h;MSZ(1qj=Fa7=g5^+ z8#y5MqMPy47?e_sIV8lr-mn!6`7}u%EJCH?^b_{)c|ir$)h5j>9_lLbdU|^9wqIBv zcvL}qd)he063>m>Vh2|;LA2z4h8MXv#uC75e|Wj|u)vF}q6`*};!@0jxH;Ec(qDTZ zdFGGMmQDn=UM3?w_mpeo1~{GpUT6HaWwDlqwwwBc zgbBB7vIFW0tozEo`XP7zGa^r+p-E$8NT+qlf{oyxNtJwS5Q+iy$&1#!^IQYEOmkcl zUPGt?G7E3bKCIb;EGjGX{KY|md2o0yv?`ng8hzdxwA>iwT5 zYBHF$-ujs_8L55ztRO8D)l)W|bcyBXhonD@uwUhk%Q6H4KQCK1W=92g|9D-z6vid$ z17>&3#?7LsV5x0g)ya=&J`i|1rM~^>6A1K7|2a@K7l-C;?OuK^-agwCNvGVlUAA~T zD!cUR{L(qpHOM{Yvqee9|AGnplNI-aF?9koP%rH$h-bgYQ4yqW!IE1aQIw5dtg5}T(G_eqnbtLZCMqn) z`m>2MFEKVD)TA%{M;shh0O{PM;-O}=8Be(B3Y_BK>Q;DJxAOn<1zk&iOo2Y&-Yf#H zm3giWUB^QneRJLZ;Jz#XKVX$+QDV+O$UCke&IZ2dv9Q02ml_`TKn`ws>fXmC8w1|S zhaTGfB7xxil|5fROe_b=Gj2-`=;09lK*Fu%Xy6XutgZ>)^Sr?a-4?t|GEQTRPi!Yn zonQF-X6g9#=)nd*G{So~WW2m+h=#n3xwiVR6&hdYpU6GwGKYyaP{uK)9zug3@!6{w zmAec0OnKlBrw8>L<*Qrtyr_O4M&7|yMY3MtrD=8Gb{T|pJ^rstKYTr!nS3e#rzfOfJ}4S;kTe4z1PxtBiJirOaXHiOuHMGi*sEB7-6(0bj{Gt zpM~jb|2)Lap6ghAenoD-i!b=dxW?pxLx~Zv%Qz|l=_DgMAD*9jwDDhlFYx!<+G`1H z^ykv;d*34{+D{ul7HbeNE*ZD>&`$@9Y1uKe@5vHa9egykt2t|YD(y$ zcO?$?(;dHGbnQChg}@Iw9?;rR#@nz~oA0Prxnfk zIq>fgX_{5|_PxlH#HBkH%{G_eBTnbvIWUYW%ZJ@7e%DW>QPC{)u1)S+w=HD1Taww}%haMber9@??;@xp z$MsFU^(fc>xQprAjxAKkh(7NMVPY#SWaE2ZZ}8! z#NS;>1cL|EFLg<1dR0}Z9>sZY0s-944*VAs@lE&w%Y%|F|GJmms&~Mf0Mw-ha;swhhMN+Q&4R1z7fM50J4L=pJK0ZA56IxvlQLp-7vhRc+l8tBTZZR4drf1V_8MXFZ;y7H=lH2f|;Q zSBc|^;p*}7Zevwb`!TTtKtb6zANh7AKh($h>qN>HDnFHoUO&1w*^zb~1cj)i%_6^Nif` zf52By)!uCu3wOie?sp{pS0S@8)FV|-<;r0*rs=QtkEkQObk?$n@Aby|IN$hR=AX@a zSB?_%PevwQrMMykSDuJauJ~kn!=;cKiz@hb(u?w^R+9qov@<1(@BiDVg+?TPh&QaG z7IF~TZ41})S=4Z(pgt;`DwV^;&grTyj!JVn4^V8DN)&a zjFy@2hv)56UFI18a}qMYT&#J^j+~aBocN>$zvQ0D=z@#4KMpQCm$ZF}nj%RoPTxBy z+@p_cvx@OR0AQt8)w1t}_Bmsi+uF|-N~&dX0VTr%X5MJAt@l>E!o{)t$CAADL};rehQ#5S>XcD?8X_y(XfSLB!TR6Ai}&+VRfeDGE6 zICY+;iW{D%Y5&!_2>r5R;J-3Iu81m>DAH2Pvw1o4;rrH~bmvYeaPtw2L%QFU>fl4J zd)`>g4nf>XSX*_Mtyv8hnk*RxYHcOixouPb*z>#*%jO+9CGq4DW8(Y0_5qE7Dd%r8 zHqB{64$CT!6Kz=a!GBJB#NGY~$;SHItxHWu-7%%qnC-F*==d_%hS@_5Jh9GNmjUl^ zW5BBUT^}=dW%wZ7bi0!W1YzmqSY9`)JYgW#&m0SE9PoxR3X3+2TP=}7itll@`(&LK zKB{GQ!pjQH-diGucEVbYPhM)t@A5(NHGg{FO~TJ~Ch1qM&vC|?rXBeMdJr2%wv2us z8kE3v^>@*8O;jab!hlOb>Up0HVt3A)-qKD|Z?e*LLpAVOxNJeLKBOyr*ac;AnM6hK>XIYbpO0y;{WP^^o0w z%I85?%Fxd%l=FA|vDk9d;_lX+fJ9t!npCvc`eCapAw9Bd;SR-JX%ydf+!^r|>APps zs56_1wGu|HueE(qvE&2JHS4Jgn+S>B!7e=J0XSLM-=a~Bl#VDDKj8Tse^j__{JMG_ zRX_nKG2!o1+`@QKj%!b`2=yutAt?9yqudDwHVWnt&3XVoMQ`0ocR0~vge^Y}%c;Q10rC}6@-#75@J-(^a0BCVfRo31pG&{IcapHBq%Boc~FY`m@GIZfyBq_Nja zS)gR@o0A)zcf+4LJ{Y|+%kaj1ew}&G3>weV|9ZxFrvWREE+JDkOS-fcCP1;fE ziyH*`TCa_Pt%gB)_Dxj79u3^FElYlB3yR(oKb=n}(gD?EUdu>11DyKVX?7KzJ8(nu z+D4xTaR7b|d}n5U%i0$i#bgAT6+wzQdrxr6EY1+;F4gEVAEPEI5!$Pp)~`JDLG)ia z^TsTYn!{~d9v>dwj%pnRMYmT{yM&3v^cmBwAkV>g{oJN?B&sQqJX3n)E zsA6?jl{q5dh-{i}Ki`;3mD@_l6H;5%=_2P!ZL-ts>t)S=CA*vKV zLA{bDDUxQ5*KT}qj&GQ}f|?ZvEe4D2(A~Dqlssq8X`<};Dd7K=%1 zrw!hFXt|%nW9q|cIqD^d2HbbHmg^t$M~AZ1LbdjjTU2?xvBW*o6C2qqJA1_e zyhye7KlIC72YW0X<%tc0_fd(HMv_}xvGDQ5KcyQ$5@_i2ey7W^{fg!qX5vnb)~qr0`pdo_y)&hO|2nXK%SHG|Mu@bP-ie z5pTEK1fr5W2QSS*&@bNq^jOVS)<=x=$eRYU)Eh#C*sc(=u0bM zlu$rQ!Se|moKeoGPgyA__$D<{f@oNG`-$P1002F^GBh2jT--#`{2?y8GH0w}_SW=~ z7O+2Hz)L$j;)4!9WQ6x1r~-+#l{coJi1^_$Irce|yQI{>nec3^`inPWNDSvN8Ls3L zk}17S%4!Z+%z#C@CJ&x2@bOW;BI^L8y|+B9T%MXGNcTwe+x+jh1-kwt^OyCH-E}Q= zi@&@@?ZQ5|lC|c^IS;~Bk60Sw-t}wQ7x$LwoO?%>RUVaYClbV6kqPtK$SPhqb7PuO z4+|V@QOCKokCpM{)3HRVq{{;A3CTF+DrHO3v$}Ip=HmPeoUyr&y*iA#$VHDj@yuQD zi#zHzttm)}&;b!9_im4?K!gPvsJeB%U~Y(={_V!KvnoGq5M|DeZA^9O4>R%Rn(+f? zIR=hOrYSqJK^;g?90vEEsHLIzEFPauM+1&;U^0(-(F>nFe-U@6ewGSuHBQ->r7{5- z9A%ODHO!E-5iG7)JvGWRLrhyNUGILOc5u=aiR7TC8~t#C{gbASL^((>OX|cyB{@H= zz5GmMxs0r5K1zGY+OYXxPQ48m?*{>?HIm%qTd1OlQ-!NiSgt~8+nv11Yce2%bD9|* z3zMEMv>T(-JCygjVpjR{9Xl1`cnN=J6yPQXDh&EMJ~Ravr}m_T<{1e~#Qx)RL0|=d z0JHXcmoA4Iqs9%;-XPxSO}C6;J$9_dA5jiy1@T=5y_|liSozTBwSkCJ&SU@QXil&~ zg=;`VVIIgmLzh1)j7Wh6fOggRlUj%e!iL+@6Sai_c^XyRT`6#~MXa*tHfAKi2`^bU z*7om?GkRRj6dm#m%I<`PE4qdLlGyUn9VuOQKn~+CU*LsL0jO%doZ&zcdD&QxmcKo~ zi(?(8zDhSYJQYu$BUUexy~6Zzz>aZ=!hBd1R`PIzwSWh9ONe2To4<_PE-7? zw?lox0QNQi>5A-BtveRhSr$w^1dzR*nMbwOAP{%oDa<4E;e4m*UTr9v2FK~pgIGH? z$YGL1=GlGX0cdj4=*WY5GME=KYf~RCa4ums{3nw5$d=r$Eb$io6KmRHM;AJ%q~ZtT(1skr2D|o z9$`^?EH@W05vK`o(7)iB)C$cVPY_$%M|VRFWj4W?`)bq`xm}_4g)%yV>)S+*@BXX)YN~-37PIX7p!ZSH^DCchpr!)Q$RT3lYw+ zqAb;^VWZC(CJ7H<3i3lqp-W$j18_o_-s48*ZNNGzJTvY7mFbIK88*ai{{XzIq-qQO z;Aw>WH)tHGS|zy$U4oO=ypw^GT6piwErQQ;{)X_09}x_kAf;9Q=DZNVBu?oYI{AOh z5LUMJh*Tl*Y%NFffBnc0j|_I!-re+nCCcfT*iIfN)VHRh^4L~byEPj;?(A6ag8#Y% zZb{bxX)2kg%1G&yC$d@mC{M4WYID$)DDhSg%1r5>APJjK=xV#){$POZuD%@k?=lpdVkzA7y*|ch{DtK4pAlNH z1F`YDQ)RX>km@&r7XOxdXA_~9N@t5-=YZvOhIg@g1LKw8)s&doua5EdS4pis3&3Mn zTK^HUU$#bG8~(_IH3DN~7bhOve~^JKJ5R>1oTh5A(2rWy)E=L8!6Wv4bRKgXAczk2 zY0G!Y+n|nW6JayM- zHbRBtZR-UuQC)-xafkLIE+Ga^rpqUa{-lcX5N5~4QL{ZlVVvh0&T^6owfi9RxK64( zo*X;ZTz(TS64U;ze4fWVP?qiT`U*ohh(_y=vtB-@kJUc^-JNh$SK~14_l>aSt6zQ4 zlz6rC4uv;#g4ytLMoXX%E*v^to00=otr6w($sh|ZhOrCD&sRM#k--%O`PU5~|eJA-3GafiW?e5Q)+JKX08r9aXU-rj^2g0g! zi%27x12$LY-ROyvIXBVTSRhC}4G!KPEn|+CUY(4Y^o8fAS+`84eNn{9#pyYr8>zbd z^vEuTPqRJ)*D(EE45%=G)urd~)H>}jz&=0ozi_CNxo9M=Tqf2|0PU#F)e3)`_nffL zjc=R0?~V(6HdbccqKdN+wOnsrsU`W~%zB|KrW*laMjJf)sT<{kzuiF3GE^asY5s8t zRT&FF^ZqBNsXM8w;E8E@VOG8~5bghCDa zd=H%S^BLm{wIE2)Ka5?{xLL(O$o>1=xGA$M8+~!z?zdiVRj^ZP$+HWp`rxoPzHidh z-T}NYXV)+HT3H}3)0*|p-fZy4*&lZ72xtZSQ1*nInAJBrd@Gshmh1+SQ*;S9p*U9L zfza{2Y=UegRlM5UIQTTf8;{(xcC8<#?%zW1o8nzhymLbt?0G@QUhzS3&iQEj=;nSW zH0nF|^;!)Orz0`#-(zVyD08jwAv8czdo+Prot{tzJ}2O9b;Jh>jFFlV)>2+M#I$W& zU!wpx&WMDPxRMY0%9x^%mpprBO0nho@yq+gMFg}WcVuwb>! z*aLt-;7OBv?k7h?%a_rpp97qUEPQ2!UkjnluF;LL)c6Vdgps?-MG-?pS=y5EerWQ_ zC*mgmPSIQLQ0#8~&*j&UjxjCDS%`xd81y~;ZkwJc>|`h8zljGi$@pOUt>yY^L29uC z(J`_2?&arBD3gba`oWP*W3jiBr6L5}@Q#8V(K2#md6{ZvdLl1L2L*rTzw=Z(K9Bz1 z`TPChkN||+Cn{93Sb1Xp5a~4S_|-t&JF9@$f{n1g;U~MQPUvqVV2I+hORosQp)9 zztap9EN%A4%hlWL(YEj9%O^I2E6M6*cBtfUe@uJ!{6CgMz>+cQ!YXEpX(+VYGGx*c zL~Yi#%l(VaX81RoSFGp{sx>Rozp5pz7i5ZZmpRX$sRII6!0+#%)jAbadBl8HP8A4% zCdF1>O$A?kpk~i8iZW?vw8;s$DJ9~D=rTHKe+Ef!2dWIxm-(xN!!`Y8)Z8E*s=Zr0 zW^5CHlCwNM329N&xoP3Uxz0>~d{FmorPrGjn%5A)n=iW6b$Ft<0aMSxPXPP-V^$45 z@K|6TbFuY9jwBf&EK?c(;j|wZA5 z&w$^O|5SyVH3&q=xGC>|Y#@pr@mICu)rGuPnHOFdU#^4Aq$Vdh70BleTIyjTZn@w| ziLM{J&Vb6nbfDktBbPC*v(+mYK>#ts_hm^^pX|gV3VFE`U#Yi62?x0`v6LgwUuJV# zTYe)6kqP0H$Jf1mQMK`v)31tP7hA`L7=_mn%Gg_QZs9H!q8&=xq_J|s4Arqojz?Z1 zqn%Awe#27O4vfPU96tJyrorW=(0v-uY_VCEh?e3*s<8@Pd$lL6iOCI#S-Q{W9jAal z#q8R}@P&=4=>4g`njUnBWmz9y-)PSDz^4ZklsX>(*a&w%8N_3!gD94QKf-Ur4WKiW ze;HQCY9r22hYZah{6KFS5%zA`Z9aHy?Lsvp1x{+hjJ$tRiaFYxvt?Ls80wUaHIJK0 zVWKG|_ecb@BQ;T;KrU!1qf#B0T>WCBvx&l*#HpN?{@aN9kB1)hBPB2C ztv50n6ywdFHGMEuedaW!^|XKrME9fI@=v8e#C&H+r$iDKC3pX8P0=IyM6f4>9kIVL@M{eXH#m-yGNNmytC~BM8$PQ2H z2{E{!GOHSrzG&v0Ow7asl9xOvdG7J{K_6VU^HOFObT$#Ajhj!$6)>=LUclEE>?@I%3F9rCdk^!aGUxNgadwt|G)rl%i5ViM9WF!B2pv=nQXwNjbSi824 zzEhutZbj$M3ts1`aCv#OrC!M$MY!<$fqoMPRf2=?lwDzJ-Q$Pj5}m}pcv6SChy;i8 zEWHl}5Pcz9G{gQP;5&t)YSU?Na>l*^SytzjNTygn$2ar;-7PD`nk(Q>svzEy`G=aa{$~ z;IglPLJpiTIx~0d;X9@@w0M83Buk|j)SAKn^dr0WSfTLo!48q=?U386V|n6^W%{Ax zFV^p2PJ&{_b9?M@RV4@PGFElLM~}pik|URQjNSm%x~2FWhrK$OosyauoI3`bFl}GN z{pN5fNKk}>);oHc2Ws8_^z_4TkYEb~+dJ4PT8QZ%dc(s3uSAJ{ciwmdC&p4d`9UR6 zPrtExc98~MVd$wEuWJQ_cmu6n8hM(4U7D8nK zxW&(~#BVFqr)*4frEJ&Tu^?CLngfZjuh$G#C}K|>uuW)3zf~L50X)B(g|$=zP`9pq zkXI3@Q_=@t)(?t#q0>FM!pA#r4c*8ov_y*|0NEHyebTtB2Pt4tPNr*gyA>+LeSc?r zVF`M>yt-xT1F(gn%-$vz5~6kQceb$lD~lQBd+73=)FwWH;=R>9=><2o$xHC)JxCV5 zpSCKg%c=(Ai8Fp#MmYd@WGG!|v;J;$qGI)$$tuKCfSyE4=z5u#P& zY{|hCCzOy{m}HSk-f=Vfo!RS4hG?SbPtt!E;EtzWeZ^}L?138u{rmOr8XCDC@c0GePJ@ z5c|Bg_SGmAZ6lVI=VvwVyQ4Q0>ZjfxnDXD@`L)R`m7^Fu-EyCq|w7h%)TF=w4XG{9E)+T{TWla6uCaBMbp!;47= z*7kS8ap{vXPCQZTfj%1rXB9V)pk8&IO`3s-E&lnzd-jqCdHGt8-cB1oVu#vyD0NML zA<;L*>Mk{g>*M3zyogzD5r~-Kd^>(RC&{A1J4XXmXUO!<*p_Q0X(EPBb#x;8bs&J! zjhZ6^UtjP>36E<`g>KDic+rJzP8_o9@j)~(-cq*{60Qxzm=N9WbDKV*+ZH^F|3Oxh z;jQ$PlBbF|JVR0Y=WP-KbHp}}B<1+y7aA72Zub=)v(s$Zo0q1YQCqM{?@}psatm=? zQ`Mk8-W`|JCNxZq0q~jR^%Tia^1&DVdRF^BL0B!j*kL7n*%OsS?BhYxWTu|pcJyId zlRs_fL`l!s25hw#{8VfLWWGsQr$BW@joW9p*l^i9E;s{{WR{U5Qgeo1yf z0(gzi{BsD#OuzbW7m6DpnY{VjUU3MKmV3@qtM|B~>-FyxxZc1&^e=1ejto)8W(E13 zvJ!Bhwa+)dPl4WS=2u+{a;VypbML%#C0(zHQxhIoZeuU8VXnI zd;@14?;%H=p~m`0VlAYwj2YMU93{Ije_NaglW5~Fg7s@bDf2%Dmf5XqyBbaX$xVMB zD|j!i%^$ipjCh&vZg6EI`p(zXv7X(9L<%e?G_@d9%+f@yvH@|oCT7wz$I)#5Wlbg8hB(m?1vH(j=R14OZ8MFuxw~*OsX{Y z-?Ba=P8^oEd~@%HpKd-_U{~0f)i3HTS(-*qTx;1jf7}_z;TovWKoHsSo5xh_T`;ZV zc=X4e@Jjf@ZU@1w8aQG9o7ivs)b%_>*^eka?_EZC(Os%PfXp0u^Y4l;%rdZ=-wNg5 zZE76{k=mcWcjHbU#LrzZM{q(CfJ66%X;RS_%jHXje6CiI<)iWaZa>}Ug^XPGwcm9i z3-LIS&yQMyU6AjSO0MB+P*c@bd>fLS@s*H^pR(WL&%E(=d6j>a`=7Eo}TS8|&At!XF@0>z;X_ zc&o)0(T_7LvPwgNk#OdDWQ?8xW+YJ9kDHS9L^j_RmD+wy?A%_TLKIUKrHN$!&a9PK z0~9^CGyM5+DknQp#WcIM=(iOXOUj6Tx&_Xv=y8L^m+O6T0`FR_!FWg|eNQR^?_ET= zGS)r!>vOmOwH1|NizTKgw4>9Zo(p28w!Za_smuOI`s=BNE@lyBS`N)>!dBf5)9Ib( z=?bIv#K@&<8HVwqIKJX~zkxBFOH=Q_j*UA5u}k!`9JXG#W~$L~7Iilru}t9avV+Xj zK7JyOiBFcQ6o?txPI4^*K=*D~!>pTN7>H6{I(4M!Lhle^J=6Pu0dg4oewCp&IeC}q zvOK0( z11Y;*n$_D0t@1>|aCdNMz=EEK@D^t~6Y}XY{hzXEmb#+xHEW&<2ZPntCS`ANw1WpK z@uojf{YiaI5dCRQb2sm6A!gH{e?4Neck5Z^TaA7YNzm|y=(t1tsECD8(UGB3N_TtR z>~75*tO_SFghMb+_q-_YZp?zpRTgPG$2vi-0CQdcrYl70$oLsO;2dCFiFa8i(n+ zM&s9vYXeYWompto8CGCsWiI^TvgxHEvkj@H=iZPMYJ$p^0Xq&&RBAqVBZm(#+)C8> zJ2&_ovD~ZKkro!{oba)>oh#h{;!Ev*sU?3QAO?lr%{yt>12kOQUdB2BwVc4sXKyCt zHL!;ko#OuG|FxXn!6bvTGDvX$g}GbIWGiom-y=`YNPZwyxSxCR`2*DOZFu`vNPylsYROlxD5_3 zE#d2*1LPrtl!E`92TrF|borM&WUJbcbxQ*;mGHCm_E(;zLCIoPTXE-hJUH4f9{;rA zIE1V)rR#5X1p;uTNTlnaK6Q2nQDR;7&0p9FB{Rn!i`xuD@_g5xnzz-iNa0INNzh#q zZH4}+MEAQHSmnY$%H~!QIZZmL6?63YV21_a?ajN%%GgW$;tgjpG%@X6@y{1{2$L{+ zTHOmLY;W6+ zS?hq2)@Czb8R{R9eLh@YO!IL-nJjt}`WaLUm`Kw6>ImC2L%euuwju8g+<=RelNO95 zXN;Yk?Y+OvN<%q~xqX}lOm*--y$f6y;;AoKiEFDC_I@=Uxbg#6&aNtI=~m+UqsR9; zLwwQV#g5^T?ck5g&in9-+rSDJKKAVpeZK}2yw&IIY`MR^@UJ(@bMq(QJm;gI90h{T z_{s>k(gzx7!Z)=xachb&aOUfr0XI27hX#Q*I(2a{pyzsl#J(p~VmmP}v0?b~Uw_2< zYO10r3Hn7{Ufubg{J{u6p$-OB@jJnd9Wq;7%uDpa_iU&C_{@{e$a2wHsTXe^=SH%c zm)!#U+$9+26wXubhetUqRJ^+w;PM!qs);0617^q!|o+oPH3+ZN%TykL{CIjiwmCqxNXP7Zqxd?RV@ zz;wsM&(7PSID3QIpBlg<(-xgFek_)mBgI6o34d@%wUuT zC+4VO&H$#)otNM*v+~I4$@}j@mJ)SO>pxJdqzJ6pcHLS-02PKTZxNmVZHp+GGi>*n zGeViqTkM$3Nme8@r@iTqni?w#w)k z_tjIcCM|#gsnKxW#`?z<)7}`}xV%6u+D3@6Z}MV`*@jbxD`s?P)DP0cSbNon6;?;I zp5u{8$bq3Ey2)3G?8t=xB*1yEXqtalT{O`x_ds1;)d0OOGIfvIM}4@3VCoqfZnAK| zJTl@LwIm#vp)7w?{$-CTZnkk*^;BB{`t7(&`Aw#&^$~?`Ik4l^aSAB4<*GP z_>uC~`d1cJyWA1ulVws|EOagjdoXPl!>%Yf%Jb?b6*yuupL`y~ZS}=%jEgoO^{ESN z1m!qw`gFelD*1Gm>S|3Exs&PZKH2=SN4KfU2UGpw*H*4pE^TjcLAc|=wenY_PN-me zOj^d*92-f>4I6wa0>!b8Tgj@YPP_-A-HFq9FBs)2&T?${)HCT87BQZI_SZ8#<6 z>4(~i{1!Qcsjjy4Z?TLYlh5r?WmotUY%BtH;>%j6Z@JF{L8D<`em;bh-)zoJP5-kq z!N;#KjWLpdKX(Xxk&-K9iw-kZhI~ZH*p_Y16{r*GkD8Z;Dim%~)p_VzOq)w=j(I?@ zVVc9Vbq4Gb)1r38eLs8ju-kf+qYKI^IxC<2gwG}u#N_HO@6~iXVSPJp>mU^}LBvEvZ|Pqre6w+z`owv-Zk6_rRzk0WO29NL zy!`+aoPB@qYm}Y#MavJFmEzc-4^P{`<+}cd0Py`#n`%VKm?(Cxve3;+3W@EN){iRy zj353{Nc`R%Rp3EXewW!&J!A>P!n!zDNd|6D`-jqjqrWX~C*Lvc^39TyL0yP+XR z$GvNMPE5HV;qg%6ADzaa@cOew`+YQW#;XBk?w?7=NN|hYk&spz&bu@GO8hAqeC3|m z*D6f;;MD%oDs3sUweZbgHGjt`P`b0E#;rriek`|e;gaPs8}#fu;)>WomY_v>-i3+3 z-EfZ6%cwmGo79nbLXREyhfH@MjzdA+*UIc6vYK7_q&*}`V57K*mrWlbQ!L?!Ot1o(^+PIn$ zOTEHM$izIg>$Qab)RURUZ_ZGY1n89ll?T>E@5I`Xod=wpRWwTJlZztF3sD9rp|LJA zLWl_nl#<2fyMvylcw(>1P`5s47-HpYdhRm;VBfW1l8gd9u#nO{`>0g}aYkNG+7kn? zYo?}0u}xbK*Ulz||3A#Fzy6bt{vY4?XDMGp@Zr-s5a{OuJBMjgq%i z|68{T2$!P4B$#Z z?*UtQ%zs$p;$nmn;<969W5|Y&wu&12(oHlx^P+n;lR>t$qV?HhLt*?Vw=J*DHWyM| z-JUTox^)09T%0;-KSQcGiRpnGcPcRO*zp4vEN%cUSa+l-v==y`n?YY#%yt4mZ!VJC z)Mv~@UW-aD{8y{V30F;*J>s0E zF9UZA+1*~OCt(@GOOd|}wdq&V?UU*v0h7iJIXb{Xhr0|t{;#851cD;=fuO5;V3hPTF(GMij&_?Z+a9) zHmC6Lx3q9}nc()Y6LxOA@2npDWr#7vzRKX~D?o7s&Nb=br(MpLQRj34&5q#pTvflR444G$*x z?82$V%Wscykp2;O=hTB{LasQ^#6IE}Go)&|nAMM$^uvyba&at|bCc*5OF~RB^5Nnq zSN!PawQ4>^h#hX(#i{PrzL??@l&xL~NABz3h-F7CwD(`o94R>lIrgQ(k3{oJkPF`) zPd@#bbXAXhmho2K0GW}HGK z4jbPjIJo}V1oN_>3KXFii%-f=*1 zTwINCs0eV!V#Rd}qU~^FYV+Rk)BBCEio5FMM*{w#uHUIJrNR*x&5HY2j>8s;u|#=4 zW@Di2i9N|n9S{#Vk8a~Uby)#3>nHsDehe}p-N5O2OqE6;QXAg(dySMM^sXE=vf62~ z2EJLmE#1|&66iWPN`$KQN?yv)_+ zHF=YIRD|whK2Z6P0A75Dbn?$4YoIsgwZ-1CJq2QORv`G-87N2TqlqC-=eGyol@JHl zNQGUH&=UOJ&!S!C$o|XLJ7Q-czXnILr+zjfa7cF_vq8!K)o3kmzn%;=Op|ZmEj$ei zwV7VMkh#|%Z7STg|GYi4=h6i%KU}8Waz-3^2d+$tIl=TMnpsx3K#&7k*=TODIIj=U z)$qZD?yEgO1J3DoMht*taHr#1oWqQW*Q<3i%eQ`8T$XU)8(lIr} zQH9Z=!!driE$;3vl~E`^Ia1fsgG78VZSL#3M;CySOir`TKKpDpcKA(O&ld-mXgNF8 z*vZ2P2gc>HZdd?|w6;!xdv#Tl2gakiB3dp>dg9e4bh$emC3a{xN*IX$yA-AdW8zMH z(5mYL{sJdoJ4|~bD)^VioRpE(qv4CbJ`RwrR49VFDyPM-SN+}Nu8q2j{UzWqy4^Hh z(6q-3I|lV+WF3VnO54-YChDjm{!#e;!iWROq0YG57JL_%#0+uQ{F!O$SqY*^B_g!y zKPQ}ZXWz%wOtQSW@XGbS7yo(U)TIy01MZ}c;KGUc6Bq8la5?zq?8iuju64AuNvQ*C z{P3jyotL>VOp!=G;oE#C8wNQ>zIXa`50re?bKk2Ap{1K8eyOva7t97^Cf!0KIl zBl?EnbV@9_M5;i4&CnUEOwTBQ^&=P3zeIaDqVxAsUYw~1-wgAVqebUdc&METEHJt@V(! z9chc3YA4_GLF^`;?a~(@Uc?2X-+F6(8`%Y!ZUQl7u?Lg7nZZA@Y$)U#<&?UJbS zM>1ZbCB15pmg&zwgl)d}*9N1frTIBu#_B36wlqWy z%x~wL;X`snCao{~vWpL5;n#I!m4upk`P9*4wrAbZjoeLrTvp_?hqmK|t5*ULQ&Z#l zwg~_t4z!q}q_gJOY0LI6GVYKqWzO$Q<~1JCZGT*Iihen@ynf!np`qz6X0A=CB9 z77*_hTY9};k#}ppEYL{jk`FfO_kH6fs(FO;Xhn1dEq&5f~h7GE8E(JTgc!L7yI9)q|H94cwnla;t{|_lTXilMf0sOwm95*h8fn^ zul})NikBzm6ey35(4+o~rhf_zzv^+u4%z6pug^3Rfve>HmG%5wDU8qHtA3_b0f96- zQDRr3n<+NB(S-s;Nh?J3U%^^;K@XI}&Bewh2a^zpb>ht*dmqCnp&ZADze0u%&={if zX=&)pgFm; zfM;Z+4y~{h3L-0ub<4M^0SDop7wxZpNa22i`JA#uYLFDIve%~AVnq`Nq-GbmC@5PH zobPr7sk=fvOkSP$F$O^{YfZuXcnz@UJor^I#Yd%a5oeahB_G}M#!a=kUxI?6Ew)6W zJn_lcF05!-b5>xFxlmw4GNNR(DHxsEwO!xnXlY-;8hG=Y^`wxmSX zGX=uz!nL%VlYsu@)>Iij;B!QE-n)(#MUnkLeCLSYr8m5=q~^OEC0mgS8l~-AuE;wn zJnD3NIYVJgiWrEjy8Q+wuXXn|(|>uunbYi@dBM%+i5Gewp*uG#>UYA4H$8h`?}O{$P`EYO zWf7zfS6E&m(6zbG9mhV;DyERC9`kZx^n;lIH1a08U?~qAFdKfd^JpCKMbl~i{HJ4~ zCkkyg%UD(RMmSZp&UtJNO!}~~|CVs6hla%zYp1yFwt>$vs#W#t>x*6}`RA6*k}fFj z=`v2%;}ggp`=4_EXuIeDF@ECI-m7()ZrJQT_ew+tcrIA$bhp3x9e`^cUhB8K2FJ^Y zT6}+!h92s$nk{`RNIe9dg8v=gS0+HF&n5*=!47h7jNk-~Jl>iO$b0bZip zWs{=fTWKtLiLTgqA98oC<=rnYO`||890CY4!mnp2wAKOXJzeU(A_2POJo`)a^+Pr| zx7<6~R|rasj>kv4UulD}O=3sQUeayLs=d|y$}Z?HYUWN&IvuY;f2Emes9)iWN7}~p zt@rSOi~X(0BIn=$n7mi*l1~ppNx`i2wL)AM%oYFgCtvM?xiOPhlYA7jERnt5Ov{)H z)q#be_swi7mG?ybKi}#tyn~R+AsS)ZeIo!{-a8ld!;;F+MMx$r_sP9*!}Er(7#$nP z0Sf2M)92Mi{P9S}ioAO>$;i#zaFZzKlg96tgSmI}kSq>SqNejCBoLbn%1v+E4f7RS z|K6~>aX$bv)V3`cC9%QOy7aI9{Rs{}D4Iv{*}5}I5OI#2NnmHQas=_deQB%{4xO3m zdy#8j=-4v)pv3mQ)YC%5OyS$49%*SLbL_}6C6NS1^U?a>at`iBG!gZdKU65P(_W>C z?m6#-4HCwN`8y!wjPSg&-4wS2HJ~s`U6aDE^>R;QGH$=N#u=A)-)vEE)CaFk-LIUhn)vX8(Zh!b(jQ;>yxfOF(G1UvM^`ot zLj`o&nE&@tRxg}5BhgS{MU90ltyOkuRX`q(&}WFaNwR%EJ2B0`(cA%v*SIx3^d_kX z%=u5mFc@~$Uv5_O8l1V*GcIPCsTyc8AaVOaf_j}s>{~P1D^}x!DHLhhf7gMLu~hE4 zOzhXkMuPA5&mSZ`=>l1`Bi*rns8Z?0P;oLj3n9yx!=mg4GzT%ywb})dwi0a~d&lcC zmdNIF>C`VNP+bmu?reQ^)g6!Q`#QBYj>@uyNI3FpYNwzMj*R^(&F2j1#$BUFVQ9_~ z7iQGoYx)HmOUiaHgF-DIq~dAGeBFoyQ&K!_cL#fYaeDt(Y0ra@3Kd@dsI_JDK{MBG zT{U%s=F_6^3*XuE9Z~+qzh7gvP^sL6|B_O!lY%QUI=OQ$l^hkGoup*{-~=5v3=DiP z*ZRNDXGgP)YhM7aN!`Y5hk!>&l^@GnE2xKq554y;iUK5(V5Vr6yH*N4eZ;!_fgPfG zaO#tE|9R*KbS^A;qzN_No1gjE^k7%nze-wV(NAH~kHP(f&WDzzS z@byPCndeS~oS?#ph#)FU_11BB9QwUWS9>S*z5w`?+ShlKFwj4xLQ9#`)KV4D{EH}!TFTJZZ~Mx#A??FPZ*TfmbQBO3;q^f$UQlmG}#U}A4hpqxE2 z+b8VY><$QrF5q>3U-yCCXk`9U(c0dP;7MMs9sOvO?1*b!vP5JK!U($XX3D`}1qH-x zW^{Fik!MNdSRAptn+GF?JBkifZ-&6v=KOwneYp!}8@BFmV`rIZPr3 z!@f`WxmD(URzd<990Th+ske*>;Ua&@_*_4nvo>6|JOWm`wd}(|g4Gl8O|ZD#dkC*% zd9r=H9y*E@61GhT>A{0^(-&$)9{J#Yz4Y3umd}tdM*P-DZ;JqG`%1QC2z}T4 zQ}5-3?G_ClwH$|mbab`VCy!afpxuAOWwj`FSsEmO)Mh)TT^43Y|J9m?dRgFzuO!_# z-J8Y0Yo+dUJNw6L&@i$0@JkRDx9BxbZHBF>sx&P&L=9#CvT4yfXdy`+;maJ#!c=KY$1FB)jJ zgzu644t1}?V)mVakB%su*5PAr1QirfY4!2>StkO0{HPgr<{sNgZYRo`W?iS+{dd zz&Y5nd&6fgwQ_jj>vsdw1Ve^g=}bWZ+XXi}BfRQB*RNoO&a&#kN$0;GoRJ5u=x4a4 z4%DjxTqAD_ro9kzf&6&)8|p1?V&-?xPvs(KWLe7L)AJGpxr{sN$5I@{kj&Hmgx1sI z0EnwH@%iBH0GwU^{7~*KvQ01U<*%{Fjb_NGr1;Up6{t_wWua4U9Hze63X;*&ZmIln`*&}Mm#1U* zN~VHS`1OSglzrsXZ3(84xAkj5q+$06_7(tmPSZYch*=N?V4<0ki#!SeyNT%XqVe0? zEKzI4y$n57$ib}D2C+9z?!soZau>_*QQHIwy9W+<%YFum$VhBw4Iw97U`)Gl9FqS5 zV*JQSD&V|IJm_n6#}&n|sd6xaZ-@}J=@Fef{9RBjU*eDQdg@1*cDC@VW^qXdxeTdE zXk3Sgleu27d9Sw#&i=z{bt4-9&-nvCeXi=tPLhWmN$vAcZ?yUuG9Ff*Agi!!CY@M|wg#~Ye}hOPXcO@}`pV2pk1 zjX(Bh*<#_RFH4(Dpk@@PJnZnuelPs&?cb5q%b=mO&z$I{*H(fg}@3iQV&SGE!^u~!(cdu)-BhWf%-DcmB9)7GJD z#{%$(_x>pMH-LR9UzW_Iz}9JGt$)1hI+>#7oOXuYs7EM{Yo4`11JcvTecK5~d27_& zdaInXlk^aDn{92;@YKf{-(2mNEFkw2HaL@C)6@$ctslE_lP_M3{_{aA*Icn5uC9=% z_~)t)l~69qNpH9)1PQLBALiUgVAfSBl%758Zw#PW!{; z=Y)GZgqyQDP)5HCtJYCrAOUgmij`9h8zN5S)8Cns!vLgr9O^193P5eYOiInR0h@%? z4n-@qi=mNYoyJ{cOA`Hp!i~905zvom+UV|-IWG-bB38I#cT@*rMWHgt^b z*e--OEibb%^FR?$Lm8-=-{g%m!UI{YW2xtSh~VnqjvIy@a9jDtlp~qsKuGHpA zekg9uKMg5W$b~n>wm0g~0#Jv&jlauwAko9W4JJ)3Y2b+uPD&vnq_NPfVaG1@Y!gI1 zEN!IR15kvLN)h2`7Q)?DkN=(ugv1f2OI+hSWrZazejM@HY6%=VE5G~6-tYF9rv`h; z#epuLYty(*3tZtjCwZm5{)815-u3>?2_ttDTdHcblt@*Vr*lRQ+~T!?ek0MU%mB6* z(nm#zV)>?ndE1?F$EhnLY^hXpUc$7^eYO>Q<1>m0Kie-uYhG|G+sEz(WyJ6|lIK@~ zTac{6cW&wti~>&$F}`pf{`AXgw&U+0@+Lq3=kLG+uQ-)^)Y?GB1FJ5SO5IW?sjU3@ z+rL{sf)g99{`cV@X^fm%Ok*e*ZCf=Wf(vdA%Xen{HA7qhwv?JH^6+)_! zq86^Ie6ej_#TMQhe|)CmgHeQOEaK<4L_pB@*_RRyiZ#4gYyP0A1ri&J-fpKMFp-=* zw~%(|_$Ev0EBW3aEe9`*XK=y*CF?Y;G3#tdQj$IniS+w)=^H^BASHkNDVX`|QBpr= zy2}z9z?7%2JUgXjY>R8IyPKblC9m<))^VJt-&|&(b55@$XZXl>`Kd|w#F<^)R}BP6 z2@c5=@IB~Rr+$6vxhML&nBWv~2Xcooo9EExvcZO*9jMU(nP za5#fofDqp5&`*kz zBQ3-$Z{N)$(36*+X*p&KSXRylt^JdqyijrN_`$o4z^QB3Zm`b+*6i91`NhW~0`VHVr(neZ&rCg9|$kMM%av;mWIwqrT!r9_{bgzClZ)c-!p>DBq?piEF9&Gs;n?%vnH zURe+12k#TO>*uL~?L1PRm~?wo?tlS7IOmrLdOh-phGfG2?7i(rNIB>n8@JWy>*Gej zP6Z|of?pGIq-*nGRx}&-ulfE@IGTkfD~-}Yp1940jd|e!;o9};`|kaD(7t^ye3+$3 zfI*WR_B_ko<&W}b52lW$64>U~Ux`e-hkbCgH8Wiz6~19~iL%`Ww(h8M=W*b4B8iTO zY2QofG)99jo_0PFCcr@wzjg0G%tM27=_2b`bpT9By4a*0ov;-dhc+_!S18+sXun4f zegaC7e!RfE)F(TjT89O0zOxZBygX@Q>}((a$B7qr{ks zm$TAn`Xq=jBe!y@E2;uukAqF(7#VJGT>Dd7T|O%;*1jAqQv{%g9pmedqul|hQTk{N zxHtely}bX?54JvgRI@lq>Ej|EztWOi`dQx^pG)JfUYP@?=78vY%DN>d%u}53M(q^Q zCbmX6&lk9$p2sn}E7^!w#M_(`@UhJU&-MPwG|VSV4bzXhBn$WgxUbpjM5;eBO$|D- zj~rR#fzh_bci%NKC8)f zE&?QT`-FiZ2HOnmaA_Qt zvX#5}C$!EUGts-w`uQS0vB94Ut?(o-zvGPYhq%nLvSRY?p0>y#{l9!$ zNq~y_Ki;!<-)4uO8!~0Kn=;@Wi7m_>DIc zxYKg4@1Yvqf+^75gDX;ZpcQf%t>}DvSsP23)LOO>jWH7#7Y@Al#J0*Yd!)nQLWOTX zx8swz4tBR-Xf@v_e*`kOQI_5trj>1?ZZx?SfGtMTC9Rs^@I*CxvzA+%AX3^otus9Ey|vaL<+Gtd z*!X;zXa7Z_%jx_O&07fm;?t;LLOPL%-26ILKkH_k(5?LZ_j!lm*6l04*Yp+UWij4D zzCoo#Q}A~`DKVCJm2Qj~+qpI# zk|In5ojhI(JE(eNw)I0NZ^XbaZh5-;VxNy2s>%4pa^Mlcw-Q{|z93U%2Xyq6)<|=L z>YoyI*glpH7|c-#W&i7NB& zkGzljU?E1)*_0l5MU8K%9Cm5`IM-*KXH*DGk?FhzS)#&*h)H?k^ZoPSy)ekfRMzXwdXVQMdr-=E`vOeejzbnnaOLfvPS_lMmk_VM_zpVHq_Du_A`hb;sI z!Y&S)QpAl5voZ*0U04+Vj~5n7nlZ;+v1K*I{2TJWW`+wF1k&j;KGM@sptSZ)6;v3K z1=u0yGOJg@5Ddk?>0JDp4$;4Q_p!Y_zl~7im!wKdTXMD{&CSNW&LPkab&PWM{doy( zq|kryuT=ufG3x=7Eup3a@Z8y0a%xe}6~BD$aQ~wX6xBWtj-}S`RYx+ijb|0lZ~$q! zq$v!|8t7yEB%8s(Nv;#3H6B!Kj!V--Vq}Z+B~kF+Xx5w73W2|)B&W99X_atUMLtvi zz9+ljfBT1aTfG4uO|RScKCyAi#VH;K79-q`Vl;=Nv2#WYImX>O2^kyAtb zLR+I#BVp*NdVy&>;}JrncT~SB3aD;`tVg1XF|e{GVl&?;Ll?%I|AU^>=7$4r-k_)l z5nyWXi=o+M{~+Ay^o*U&84&Q*&S~Rd3s2-8b7Dm<0y^~NYr0+;_JK&L3i*GHB}U9b zhH17R_Q1Z(>!I8su3!~Ko1K`a{f+fQmErsCIP;0o`dH_qc)y*9Y8zY2^Nk#4M=P|u zQJeSB9hIl-Nl@ECjKWMBIJ~w|P)R}UbA}cimlU;mfd>Z-5!K|=C(ey9DBY$pSlBA! zj8~7Bj4nq)L>(C{WAiYVM@T_D=b1f0H{u@_G87WY_g`W$!%9xy z+dGw@dTvXW`@^nbgX*}4dHxH6(qZoC%^Z=3mRL6@q~dKP41P(ed;aS;9D8)ifp4Gn z9jJ56zDyOGd~impMcdN+MaehGG?z=q*F0xH$@p+Kx?>Y`==5TXTR$MXjr?Q@q%OgY z9~ruQSA}&uqCc#NZmNg#_Tt47$7*~aK4ihy*(L=VGup*Z!R&_y1MtDOyYB=l5(S`- zMEtGNd}l0hVQ0eP2s|tsOFTUgcy)6(tH0(>fFPdMv-{m<&oEE4EEQyUC>C6CD}NL& zG?fHkDVH8r9Zm8DVVaz6!^wYXfTxLH>_3}LATcF-d^1OG($W3T=amJ25`-n=IE$tv zVK-D#^)gJSn5(IZHX{A?$?}{SPO4f;OglHXKOfQqSIsy-=QwIGb~=C#m5cBm3F)*Py&CxL{E= zCI`E$&X)C7RzaFZXN)_6cZRG2yJ~SZz(*xmey!%fR@|{WbY-TG&_u$jLLuKL0oX=+ zU%HYoAd*2_&;EQ2Mpk0HpFXEmk-;7}N$_)+@g)PKc}v9n?+LJ|FwM!J$lD$Uj)6#a_7n^Jp+)^)b@h*S zqqL6kb-_o#!H)ivx`mUc!xxe8-amzDk&1v($`772dAQL5ORi>C3V8ukmD9X)RX4~D znYAs1L2m&}=-Y3$UfwX_fMZOLb!@N%6=X-9kN79>GFLG$iLmzvWXk;c7yjIK7ks&P zvVV9KTHG3u?SDS#`l9WZES)~^K#@?AZ_M}KD+d%qeSl202m%VZ^&1m|WQ%_N{bX(( z4D~)|@;RQ?rwsIP_?^3C1ssn39GuCuK=(VSsT_G}ihc;uynLUfs;#44a=rypFTXO|k-q4x@`h5dtsmnf-8cgtpv zU6{7Mda&v!fgY_M_F)gFo1-zs4m;mXFh`^}ot3pM`w!%!RJFv|mtd?#i=ThrKNstZ zTW)1Nnhb()_uVIT?pBHu_UOLxDoPme_30^AvOg3jB*DZKl=T}JVlKaa4e#0PhQB`E zl2Lq+3V=1fdRAB6!36m|OJQ|e4|$?4-e@l!+zX5+ZcV>=Ox__$n|m>7@90cManxb0 zoW0~`QM&5#)mE0?Ks48D70Vtc2($D#QxOHBoNefH;!_h|HUzi7N;As%ORpW03H)uQ z=1-Q9q$hZ8)ZPwDUKlCT8TETM06pzZvH$MM$8gN5cg+5pdI*4=8SS`tOt=l|V0wOE z>H*ZegL~(lk2sp6M6H6)7cd-1ay3#mn{5?g-becYdNuUeTacTGgF$ z^WKK{?rgs^GX?3xMU}a<3Jg92-sW65Zcc!O_pDD>$%y%3mQ|ZzYqB9E)sco*mss9A zAwTq*gL^_Fl${6~e<>lc92LI00J?TnPP#mAyMzBKteZOWy#E%iBFl*Ecp zt(UGHhgL__%-LUayDwfoaMo&|2?l!fMcoRidF+l(lgwvotzoAWT{FnLX-N^vV&Nr? z;`7oV3Vb8-YG_p<0FyRe=Kd2*g5&zvcyvP$!wZ`P?3Jqc2L~@DT1w&O32o#NbtO2? z2?%#G&wn~u9(G4>?F9p#e}FQ%M!I36WQGiC5O&E@DuR$X-?YjkDagPQKg(OT7!dR$ zotv!rZ;Cwe8?BIgVN*b>G19G;lm-ZG+jHTtw#NWS)1t(g!y33?Pf63-db5vYGXvTi z&zNzEe0pGKZcFi7HbGB%fYf&=PX=%m0o0aJ+7V7`BRLtcE zqJa803S$FAE+H+5Ge4lO$Gj#zq6Sfv^K7d8GAv`@h*R))b3z6H&qs$M$z=%eTjqnfaC&LD#x|3I2z@C%#$X5=pXawN6AOR_}e}w$g@54;E zUxF|Cs}a}n>@eY?Mf!5aOX~irLBBk^i&{K4qM`VEVHX>;o(e8)!o z&}oJCAAdJOMdWpKw*8@MsFL@|)4tkP^>&~2k!uzg&ike@pJj) zN+{4u(9!aj!Bn*MrkPjCVW>9y_8%|L0r~1z*)QdO@5vDx!3ONIpT8*?$JrH)KHLv5 zcX-Xq_uIeF(a1`28GZuQW;mIZ&B&L6eI)r)cW@I8MA8YGvd$9?JmaKeEEo;0Sywx+ zk%AxE;*sDT-k0~T2M(v%W?Fddb89@-QdW3o9CGNdWVQf_3*Oj%;jCqaI5FmPSeOs` zc+(E29NiQqKp3Vtc%qszVQ+L=vC?}j5hx}e-w!L2T0aHiuGbs2hxN?9D4Npz;M^++=OyKV11@`&QG`U3 zx!G@+QHIAF8y%*Dv(IJ6j*}hWp?*NhzV@B6Eq?19Xoz^MAxkWi{k#LM7^rdQgxAzP z7=hFuWph~g0A{I?-(DxZAjc`vlvJzse{tT5nO`G?ogQG$$550mfQzR#Ig8-2c%6(8Eis%6U#?%}q41-`eSyW&&^rGjnM!6QC(H zzFf8s%Ai30Y_52}1OBp0{$I}j5)4s|%ZEaas}TEAl0poWa(z%?xw6AQ5Ari%x?h=~ za`Fa0H0~^RkN%U`sVpN|epu6z0)8X3GeV9;du*1*8F$(Y47~Pcjj3jk{e`K~OxNP0 z9t7g>aQnyA6!6qAV^?_5wX_4r$R1DnWdYV!4l9NI?qgoK*!{O!g&eGN?psF%-I_V3 zJHNTAETsf3YWSv+x2~sDac-$F|BwS5mYA|w)r0Xmm^W@PS0$A2S#NLi77%~yhh^8^ zv@OO0MxP*7$NJve7s>Z%O{d3G72&qFFp1>^b{He7dMev%GC9%_vVz{F70M1Y&9|m) z@&R1<>6y|O7Re3&S@~?+6?_^-&86`F0MO z6Zx|;aqLMi!v#ycQrIpb0X0|9+{Ty#QC`^gHLKr?8JJ7;Tkm>yxYicM^pPeuHiL-? zebDQOf&9+^lz&M??;M{290&Yt{@B$AWTg1Bn-cv$3$jq*?A7A43`E<+HWtQCaJBix z3N9E~DPZ(Sl)Bqsn}^1S+oT^l?mvf6Ie9=dFY=ES{&|4kOv(61GZ@Qz)9 zW2U|A-EBkzS)^{A=$0o;uHfAJu+(Jmvm#7+N7k2|7;!+TaQ&C_ug=1khi)aW|EEQm zk~!R6I79xkn-ylAms%lNEL!w$HXhGiw&Dj5Dz9ntm%S_jrF;AcCc?JP|ybAgnTs)1T?C(?yOo zCkmIFVAS)bc=PP80|BVAUG$yPt27 zP@~xJQm3-iHPZudc;u6W(=EVh5Iy*lld{PHmmPI>au$ITY`^lZDBuNf%Ss|Sj;cRX zgmzUsjQWRc26*|Y(3Wkrgil#iX^{Nk@!bf89b);D1`rC5ma_A+S|4m}D3Yxg1(oN# z!vV)n+(FnQblGAh3ywXiE40lh=fR&jM}#NH1aG5Dpf~8AvKtEN|Frz>4p^q)3POncf4(!kOGgny?A z!YA^*sCeWiF`W(9ZhhLZ?2NW-^*epY31VW)kHgzk*1#Q%GPp zo6tiG#v@)mfq<5rA2YzR1^#mH7p*{ciUlsIr?IHlK-`Xn;Bbo5{I8 z?JNOsF=SQ9+*~6;gPK;irKtB@z7JNDN;h-1vVxPSG}&dFGpvDS;z|WJ#S&ez$&|&$ zwJ=ZITqebD9=z!YwL?SyJxU^Xw^-~Y5bt|aToyC1 z0pPpiZTcQUTsS-W{pv-s9j5uebN$RrZa1KxVo_|6v9`rs5%S(iZUk+;kmqZisI7c{J-kHWJ;yx?>T=HdJ~U2;4xzpM&VK)mLHOQ1&gPmQgs80KTxD3BJPr2* z$$kI18$lEtWuH8uK=r~&N*bT8QOP07w2Ng*62HIs;kKrAgY7TDRbgc6Z#H*74Zo3T zr^%)hYc`%PBpVxt1|j~lYN~}GVWItEyL-g&wm*tF$|tg*O#lcDX-_gNV8O~co32+G zBLsCyMveEA4V{AbhL|*Ie1VdhW^}tXtil%0&73d!E1^PRE`5Kdw(NfjZzfyMOE9CvJGC(z7o*1iF(N zQLD=q+#YCMsMpfV>x4O6zUaxiod+}!gO%MkUYh{hn=-=|!pv~F&mpGdqeR}R?K;Kp z=H-vl^*)%3A@X`Ty2SUK^;Cv|=q51fP z9#j+&+({Sg`rWY1N8Vn^6XXDKI-(5E?^AI@Bda&kbESS8^=qj&Yz~( zB~d+grQEos7J$ZHEmWIQ01@eq?^WJCZH+%aH=Y0Z9@;cvCn@ed@lF^UuW-4@llOR2 zbv?xu!_C}KK%Q;j7775o7nhRv%&Y?w9EI(1k8J>Ppo*JZ?o89h^7-dVcecR;v>_qZsV}@ zGeajMUY+obhcu>QIvqw!b;68(mPbLSp~;-|c~x_C2rM{KJ-^zf1A0SC(mP}@AmxlC z*VRm9CJ-uz-$A;Y`Eq?R@7bC1Zw+Ry2J}pmW12at28eX!bgnWdH^fYzkBjf-IfQY@ z`mTK?=gFzkG+dV!b|ujVH=YgpE>r-I=e}V&Z@$n4&+TKV+&Q#qRLDnb-x1D`bv6pT zKN~i15rztj{X26Wmv|$Gje3uKdI5W69BNwS6xojTUe`ypoPsw`nf)*EwKSwUp{Kc< zn+bQ*<9gLwvd%c;BoY6C{j-3tm){!P=g8xV=$oG1H=qzSnC&FF3Ae*u*yq#SDHk0m zEy!4~>(5L8(zd+C7k(W|7Dt(%9is2u@co|+x3=_=xp-(~k@JFvWnNf>n|r(8YY2u( zi%sbA89$`0H+kz}6%-Pt;hg_@2M1vO#0U0cF9}nQ>C_8fHcOe|6dU`^OphUI?V!%f zvLE!vo)_!6L;p8;Nm`D&l(7lcf$9JG6sQ7EOPYQ3%G45e8@n8y-@r#`0;rawMePEx zNysX zFa9k;i$L{^_P@xgi?yeGUftD(4kzg@`>tItypcs@bt_w{QPUv(0)PEjbF2eSio3z( zGs*{&oq%maC%Wu{km{9-NmFZ(KKD`A*Y`KM;FkR%J>xmhJm#qDnY&o{Apa1RQ%)}l z`H=93PDwTFfE6Cs^wet-#dA`#iBA8o^*HuUZT@1g1%w|9^Mh|i)~5$5`@f@CwNP?)KgE)yK`{J!xh30DZa(gK7I#&KAE0v{{b#ho6!jG z<>Njm(_;8k_fzOwVNrDINEwd-nzP$46+KIV4-j6LW8u)Ii+O*DFvS`Xm;}wA{o)l9 zYg8Egz*@S1U@waq9(_b1nPO_wxjr2wco^9n^G=~-zKFB2Z6-<&5+RjDik#eD5cjC@ zUej+5${Q+8!2JEtNgz~&V2+ipTEh4398dZpO0~doB1fOzy#^Ai3mItvWiSoZj1DKC|;9|FMjhrT~OM8?A}0Hf@5UKT+% z_y8L8%=;0#fjDRFW6$fI(7Go2A6NYP-U?SQwnpzT2bm8&iDVsf;pr}%axJsJI-UZQ zZM9tc7c4TqST?*V*T97shlQ7=dm6vA!3JIl3UBW~#tPApH;V1^Lub2L_N<8zb5Bxe z$|+p|JyfA<7|U3JI4`M!t`V-N0!N$% z@>~OO_u9pTGH$Sfp@p4}zRbME9VJHAH7Y4UQs_DO)^_k)AYP7}FLEeWfPSWycS^iW z9FUn@i$v;MIGbBG?{B{gL%@^H54>*uj)!;ph{aT`lz%unUEXMM@JR)eHhq@vH!Ai2~nJ|22OAqPFUiY0-sM@ z_5dSlN|OW2RvCDsqt=@KBFqLTksdeDZFekoe|~qR6UbBOE(w>V+`sKcQBD3w;?)cZ zx0rn`MUtehzhU4~cn zAxKf4nAP=lo4_D+15!QF3z1vw5NjkUWzR5_TlB)?nZoK0Xe?k&H|-)ABrQI@d-L-> zZ|LC;+dFhCL65l*@K;@HGi*S`RhKJ+n>}fU+oQK3s^WMF5ei z%$2q+5Hp`^sfKBICmPW#VV4kwN~%lm-DH`#4;oMX?d9Y~@T%})+mmBnyRrAbolC~7 z#1P!$KmUT;BaY~E{jYZddkJ&r{dzn5_keLXr$tZtwl6SkZ~G>lO1f!2KDYvO zo!I}edSiwiIzIBgm3JQgMHt_l$)C5LDAoM$!ue1@#K&uyy@L~+P^9OVJaeHnd%DTG z;14cWtug7o(1Om=3}G2IcP8K$ir2+5ugkQ&&4`mqD&01*0|-YlJGy_>$dX?Q(Jwag zZgg7&Yn7od{v}MSKn@Toc=0;0+XoG1sn5zU0VtHDWXVxBse$T+0{rv;0c6r}_l8C! z)deHtrNXC0Wd1EQr3@cQ*5A8u)W3;r4>dv!LAoDg(CR9L9ayCHIYz^zi>~xl?S9p= zJtFn1y0pHXRD?S2$DHHa6`LTO)Vf7ZS|5@hwPiqV+-u)19Jy?-U`B!O3XSEqNZm8T zE!IB-v>y-BzA>p(qR|Fz}6J%mJ;ml$SAKO3qYa<;_5M#33Iy1o#_tfuFKdw*k z;rh1bsdEdXn@2;ERAZ5eWO9L@jd zP)zX&FsO96s5{t6{F~F~BUH}z*x{%bg?Da;0KPEo0P8+kOpwZTmS?}hfwi*eec-_I zaZ8k0`?uw+1e7OZNi+BC{M|7vGdx?o2Uw$c@T&Ch&8E1V)?FP#0eedR@&%2&ESS;% zp3nC0gqdMJ^_@u6lsg{Of@STGL0HoD{@WMM5gSCBTzAJ;hx}8D#w@?YykWoxzx8J# ztvds+zC^6t#XtfCLyjDqM;}AnO#)tiin0>Y{gcHL=?9fmPW6djX&^+!e}n$;U4&~T zAEo@AtI8h1a>FD1nqWjmPaZc|sIdV1{+v?-i5?VSt1>qPW;`*m$N6hb7MPVRAVPB( zrw*mN>;y08cn;P%GFG7_kA8bCBI1N=47^j>3V``Xo@rXh%IJv!O} z+|IJ-f{3@^t5AT1=v`|13(R2ub+s)}NCYY}(TJD!9UtuQ=PsLvru&M<=+vVUtdr+7 z5Gns>>O;Tk0xNpHe%HlRQG1lMTgvn4uN8wF4!bc>)44Y0>`}7c2Ab2%k3$ha zfp$s1Vp-b;%&5<1VR?bSY|-~0{W;PuFrQ@2?+Q)I^TiSqw~lbzK`B+)8b0I5HFSE}n5O2m0=NfN(xD!|QoC>`)Kv4eB>3ZK=mF}36x$cur z9@)T}7LyzronH)X7x(v(pjv{Sk;FJxI{KCaojY=uJfshy0r}wy`1qc5*eT&1aP z?4n$Ck3CSP>v-`X<@GL_aa-QxYOOEMJgUYu?F#4TZ-t7Nh@d}WW@hp&kRu?NlB%0x z9Ve}^!!9kW9UX+GqP!(p`DtNNG)E*}I0UkxTnJmdBZToq=M4 zhkUaS{?DN@Uf2uI85&-sQ+}w8mBYqD40s;-O(R;{w}82W`2Nk` zdWlKoELE1{jfE}xy{5gF8JZzzBhBpkB$^RKR;J zyhWOE90QP1{ho<%2T=@SuHs;>8CUdr+ukhY3lJwtHlMFNy{3Sww(owrJrttPBJFmO z^$mN3<>%-RmkA9dQ`N3q`l1D<2c zN8+_mYu@y$?z2!j#pW(NJ}~NoC6^EXEWP}oO4noEMEB^lKURx00mnyxsL^W6;=4f0Dh+DLTs z?*rhUUFY#q^@1MpseI(_Pi<>A!X*O-^UL;=pIFiQPMR=|>CsT{KR1qmVS=6?>-#rJ z2u!!lYJd1`gK70#dzrh#X<1ya6Be05B^(hc)A8abHGJ`NZ9|gO5R?R{F``XulK}dU z&bw1*2r^yGvhACo*E(pq?5a4^D&PvJa^SM{JUe=*)ilKv5BuM1j&XCptPH@l3+XB6 zMNL3@I>MD$_EH19s7H;2ANmrQL5^CE<%M}uY?S&YqmCaWD*0!AxJ}>GLkX(7bF5){ z(A5S$M6-9E1>mnryIw0?05?9GUa7A6y-)7AhCVZJLkS-4laaW^TsJUcJ|56svVe#4 z5#V@ySCfHuG0DDivLgtE^|qNGRKVTFD1&5kO^!^FrC)F^~N6seqA|u5$>$m$W4AP zD>_bSs#8*s0X9sZu2>EKpbOIH3g7Nj3pG#qzL>oCCtT6i=jXY^bch7CAzN`E?-&SU zN6z?<7O!#AMHL1`3AlT`;=KFdk2w^$t$lHSoA$x9`df+U*CvP{UfthvHkSC~N|w~eN()5P$!`e^ zd8lTFV?vP$o&cE>^+>kgC9p81-)H*xU~v+?E7)Hl>lS>bl*+EhlRGF z`)_ehf2M72jcj?YBW4F+2r@CuzV90fz&T^lU2BM}CPnYtV>9rsW;em8OVpR!0fq9o z{<)+L3jxTauvwwV14cS1@q3x+79A{K)^>Zi5{dxcPZBN-qMj(Fc5%1Z5Nyz(la7AK z7#dUpuxg<)%l9dGMThsRR~BgR!a2PDPRF(b^U#X%CO=Km5cx2!KKjuDHSU~{0VS7J z4!Jxm{@2h$Xwew;r8g3mbZ|*mnS}WbV)h>v%~fBN?vG@*43{x37dvb_ z?FEihw4T3mYAnwku|%saJKtx(&~d>TiL&jmWM**dO&1cOf_t)$ZB_iFKYEyH^eTIi z(2!lf+ijfh1Y``sPa69!LT>Xr3!#154u!nn-l~uRAW~@X>`jYvepu)=`!3!s&@Yhw zs~I^aV2*;y`8Sg_2)5c!{;p?_X_(#NZ6S9POkL=t>(O3o@^WZ8bK~}2PAiyNP!F)l z2!2!{%8yepdTskGw%j+7FT~Siapx+fE$aN&xWOd(L8`J(2 z*ebC?k(tCf(ek*48_&I?JZ={uF4oB8P}N|MC_1@Et)=l34EaMQR|D)Ee9-1>@yiW6 z;M?~gN3CIsDOzEvYwz;}l!-2LO()L0bO)?g*OPO4zT!Gdb2#?Mx&Dkj_82{pS4$yY z^c=Z$o7yFLES9OWJIDr*%<>nyisbbm5l*$AJ{d^>We>SrLv4y7elxY7mh=W*i_2Us zo_V$dbGi=RtTlo1bylcNxAa{g;=Y>pehV|S;X>!-fx+y7R+k4`SI!ZOU3xqY9!%2rI<)$C8@gw6U+s!G(XVY}=-lv+aKdwsr^bckh%hy$((TJTE`#gJ zk64|*NpLVLBVT=Z+r4u7(X0e^r@~T#)ub(fiu`?hXhod8Ir-oXZkXG zechAcK-{vPso0GR$X$$`vpKo9Jkb6;+76|I&{AwKq%Hr_f_;*2qCZAafch-a>8^5t z!yD7Tj=f)OCpXB@ro(lg3chkjnMpcR`Cf3#83#!g zd^xwdizo;(d0yD9UaYuNL%?^b=-t54g0R@a>^S#tem|nhldr(8KgD@*S z!YlIQ8El)7IaO3zx0Q%}(xQ*9=9;5%e6ToKnUr9dqfVCEYM(aD4W_T;C;3=9sJNP6@bvK%BwOnlzI1u z%mJV$@zCCEWQ~=6ZG`O|4!Sb`2bfFm^GYobCSZj12zeYs!MS;HZ`4z?G|4zj5OX{(2wlu9v_LW*lfN#8ky#YzoyGCoV-Fp0PYh$Nt636~ z7HQYpXLqwy5Q_)RfD}QD&Wt7he)~pqf*M!aRO%8Ticxyvp#R~NKTff;y%dv0b`qq4 zWUWN;h%DOMxy;(s2E^cRAwwtjfUj~_P2ERZ2EgjqwtwIDV~ZW$z`KWXQU!3vO!Lag z7fR-+Z+gWssfgIjQ8Q`u!2Pl%O7_*2H?@bTkN;MGT~J`dZ?a+v$n3;uo#W4AwzCqx zxJpy&zN`}ru&85p5-Md@uoJ1AwI-IrYDDX}G|o3-HuVPL_Xe}gyT8AynpRFwMK~crGhyr5!#MoT~16INtk=2;FNGxmadFvE( z{InhlPx)yOb#X(%A9~}qTtfxeK!w9@x2syhE(O}VbVuty%MLi`lLd0A0I|)u+MDW6 z_x-T0^wV3nfZ787J0?pnE;*8LRh`55!G5^tw86h?gBHe!lf_Q%>;vLvWkxry?|0Ni z6;)Saj-^7Q=VP*EHq~JRs+`#M*lHEpN8>O2{zV_b<|MI~r}_x|pmN#UZk%L;R&zDI zMm2%QFsIUDoy;SP)Caz173%{9uF)iQdFk>F@cT4-SA20D)a*fmosp-KoUo^+-fZ#( z2#0Pl=ks(7?a-r@t|yr#u+%hV`Qy@Hh#OjdcBzr)qUw%JdbXGRyvLdY9#OwZvk50O zGh0rnMF%W6<6egw1i=Gq?3H_&b-993FpBxHup zYz$_PCzhO@^cQ`p*5iqC5@Od|bwD*vSIy{&eFgo5l3i&-ov#W2*@IpO_++`jd@pWL zxvPh8(lOINx}=5FLv@%@ws-`QtsMGGxatJwZBe zlrR0=84o1G$Ir`-dHdBJ@<_2x_DT@tXwMEdcu1tZK_wI|`~9U}8#t*jSr;v2F2`Gyi34`R5JkS(=!w`&lJ z>R6?!e}q)dd!JSHN;&x7S-bE!k_g`laOyW4ae@6d_P@Vs-+{=%f3t5+*~u4;9IN`F zq6=I=Gjg)foQ^M=dL3G-aUI~n-1M=g3}GMaVz+uF_6>Y5BfLp`!L4(`|jof=QI4i5YyNHL{v5rgBk zAAYy{!AkHdT)aWK2q=Crl!B(|o3eq`s_y(17`93XtYIu&40FYAS z{Dml6;8@Z(7v%}_hr8hLW1S|#^P*6`yU9PBIAIxxdb!TnXg0#L8D9%_I&lYjM*Zfa z{$0d({f6@I?lS19O8!&c?7E3)p0WFulX*poxM#q;butSa^QP4U7boIe(X>JNK$Qm# z{6F$N?`a(3-GRrPV=A)TiFt0JS?7ikA2%$&GjwZR3f!~IeRIPC*ZpyIjgMs8Cpfr$ z_^qMss4fzjD1Rz$53!%RME?9ZQwFISNR6{c64dXS-NK1;4coCpL5|V=k6>O#&AWYk z#{#DvPJNSDnn3}hsk7WyR?7rk@&07uwl${bo-sLKWffxr1Jk4ihVQ;d5%ud_e_=k4UZQ`yx8=WRSM zR%r!!KcqLg=@0OlP?USHK@hoH4EBSBvgo;N#r%Tfvi&&_qxo%S-pozAP%i7yWcJ1J1ZeC)l@ArF?!Jbaulm`SxgY}vv>^p`wBCe%iuaX4ms0Ld zWOQerv+-BJV&1$?cW!=dhtIX$6{+umm%J`-Q?O{hKT_-yHfw4ohK=19-U;oma=@9~ z=5^rewFq7@{UL|P&$^-TOVy`EWC?YekJnFG8cW1cP?1?pu$8>@pT=;E=zkbN8 zs<{RSM5g9&A2-MYyZx`c>wHNxb4i>BJs+AH;Qa{-ui9ORoXuFMU9ybR!<@~(*3Vdy zJ2%rR6E<0}8@Qo64nuz{cf+0V2$mm<{N@6iUD(UDS^wvFx)@%r4$FgO__nXb#{w^x z+FLIq>S&7k(mt<6et{fEvW$;;vRf3}_p9b;2NBhPUq-L4Kix4izSu9y`G<9|AyjKJ?$Sz! zx&4rXfX)XEiVZAaheLbi6rGMPac=IHb^|Dj zl4R4^pXhv11j*7@n}q))%YXvo{7pAU6++o9(LMDYf4p(yE}c;`YhtACqeg#PebW(t z9)F*(g%4V8nGbg?Vp}}$V8VC)wEIxJEP8s&{t)#-zrW}z46(xph;zFCVSFGEMKiWK zRYySq)Hh`)dt1Q>qYY9v&pODJ;MfzQyQONy8=tg&88li4nVmLOr~X?WCiSePv5zDf zkhWVIS>Hy0C4T=sCgId&%54(0hW1C7wjCeHjM(0~8Mu4&g!^0>mUUJ*?8YY7kuO`J zz&UtX@6z&Jkju;r-$^M`04WUr{bnJiR7I4t!Rhu5CTl2pR^2Q4*V@5WeqBRnY$h@H z*q=YV?)eixq-1`|q=B&93NwAGl40S49eT$;#+nfCdc8jNaa58EHca8lO*#i3^3C}v zKF$Vk#F4C!7kv%&0Zx<26K>W)bLxh}4{8X7wZn<#888V%^3lszdMaR?LdrfiVWBr} zko07W<**6BFC?6NEkyHzf!LQ_j?T^y?!uh^vn*MLv~X(gWXMJsK>9g*nSVSGfVD3C zx6Sb;#4;RfR5U?ehg_v`pRYUmb(^YHs)QmKU4 zelk;wKg-q*S$XRiJXlZYM`ajKQg`b*q3UDTCjE93_0CsMGV?T0V7N`|sv=qeCRuk% zPVZWZFY1&3-Jtys=E~gMXPdl4+re8c=%W1n5g=0T&C&ac9jB6k7T;p!oZj@Vjbnnn#_lRLt0>xd*rNp{i+e`ASBd%jmHC6lAgXqo&H1;)#2s7B-N zG3ytE$iKYkpVFONW4w68-D$-ig4=_~R&%Syu=zt>e>jvzme@oKUzQBYjNXE^rWcR; z1OgaCvdZS*6$wQAriGJtw*tvcDMYx&=rCNF;|3?)UPHVo$*gT~8{dg4xfNv(t3W8W ziF{>|mmG*1Z+09z`imIp(eodtY6qC&vmt?9+lVB#y72{1A}t8BT5cunDudc(x{6e# z2weBJq)j?9ry&FPym?vF1PqQ}L9Cl=9vzXPhEwn-9zRE9#o`{9Nm&sOAzO9~HIy?j zw~&5+>7Wi4CXq-?Oz=PdZs+T&Pi%F=NA|0R7gq25|Nl?I&_REh>*kpETx%9>rjW^orYh9+n zPv$I?<-Ru=BL0fk=A8i&#D68A!t;LK6ir+6o*-KR7la;?IAQj!#s?pNQ5t`n%L@Kh zZ~eDbKdtE z_q^`wN(Tjv7cYZ$7DITRGAovF`lJ_PF)8_1BA^A|vT9|SA;k$?Z>J-t;yd-=J?q6? zOt=>*xNt(~s47hyep`{@VfrF7;8{Gw_oI!w;J;?9SFeUVS3v)=^CDtR4dIDp?5qY& zoj_6MS_b0<1BfN4H05cNoveB&Y2s)|vGPiDUpkjhiLwf`>z#9_+YQOuXPwJPpySOwQ!j zLRaWfF6FX|eE>S9m9p8v^4LD=g_~t-=2f0J{r2_o*nKdSL>bx5bLDWRBpSP%?7J*o zAfiR;l&6OM^F<6RGHSkhO?%bTsci?=e^*R+BQ2fxUu7*#Au;NU*iBM5utGo2Z`-ox zEBHX+HQGLLR{-74U%x#*3WWl9?kB=Q<4#CqsY zet$YvxPB|7p&6?rW92uH7nOYYTkFv4hGTySpIdAMjBQ`_NW4jmIc_yKWXez_09M?Y z55>8Jy>J8D^BZdRa6~mWDr|`V<^$~i*34Skpb^P1(LUInx4gG+ku2_$qY6SGLo1p4 zq>AlOykgrn_C0p6Ql#W}clSR5j9X=ySo}By(Ic!8S~^BD*g5i`KWPW_K`Ulh&j)?A zMl%k;KvX6L5VFCOr3dGLG2z)}Rn=qtFeZ_+DQ|xzAYG>(2e5rj+6}d{GcoBj9}!<1 z>YP-|oCheg&I=hu_=7PbKXU3{om7J7=l^4CA8l!m`W!tPF5ZDO4yQ+}?c*}T)Bd}v z^rV5*=MH1dZlp3SuWIp{~6DlA)J_`{eB9Tn*EJ;`VUd%sD>)cI(YKPtOue71NB z3=l<5=|sp%j1iU`V%l7F8wQGz;^&gbvI@xHdH~a(GXxJtSActu6HKX7AL)Os$%Gz? z)?U9>ktRPBXA&?j><>*&x#|Fw@-(I9tj9p+bt6eN>!2rY}6_R2T!{7YK)K?Z9YA264;@Dl&eW&z+3Y|*6wI0T-^dF{|QDbC} z!Au=>Ooi>Ai=-jSFI#W(YcwTpR$R!mm!U3}`~B6Dx{;_J+_+`3;OXs%nT}e$zq<%c z+PF9S*}iLlB5IHKE#XJdAx!WUtD1ecwAbK#if8&5H2cu%h;HO9Da;tqGqCkMG-BGq z^MvJ;ccT<_fKy+FWG{lEpBy|1%8oM+r?yN%$T7d};&(OK5Y_6dH_SvrkyoZ?YW=;0 zJ21Hq*Q7E4p?#KNZHaLJLN0bH;Vz8?Ffi9D{XhgLLGypxqsDO00xCjj^Q?C+r5mCj zuI?S>I&g9y4VOsOro#j*8*_%S6dzbk6`gN6ac#c%mSl-`+e--U3pK2`%69>9irDfRV#HQ9VzxVB0 zD0OC(>+L;u50sFr6FMun{!W(a?0j1!V}XkNnxjQ^a;)H5+4yA(4S=5Li4gYanN)Bw zGJ+A2JjaYsf*#4fDNF(0DCHlo1peTJ;``Ev{)#xleo)O!ZBF^A;u(t@tfyO`;MV;! z_Dp$}D_VIcG@5kSV=uLt;>|sA+Y7%pVzYN^vVjvd$4QF)m<)3b!{vK^2PhGD=Dtsw zMS?djE*($kd=70#CA+Roiq$TdA*K2(DG)X!;Y!h3d?Yt=n-4v$(E@*vZ1t|*m~V&X zmuvX- zMDAIIkuUU$V2I)D3GKHb!tnGc=FrKO(XD9kAL#byb@N0*66HeU9O2gb_+;HciY10KXQiG*3ZEbr(hvb*Q;bOVzCO* zbvr5yuwgyp?;%CVLnV1Oo|*m4jXUyMHazKqOsZIB#xOMi)Kb+b^6IX{1MLfSoz5QdMioo4%^!IQDIgYdLDFkOa~zTB z9q1ni-}+ak=8yAHADq40-62U76h5cC!jF0kxgy~^I$fk01t3<|6BwA~tA`eQ?0T^; zvkVx8vDc5<*WS@k=X885$2y@(rFG_1EEkU>LT5&Mmlp_Fl+ITny)_sqOi{1>XPDQ> zPfhP%wN~%e!qiv|jbmAanLtxdEG;IuQMee-?C@3s1f_to84i8}4uqY~uj+ds1Cre1 z9HK>6!h>(l4L{sMwA4qWwX)#4pjK89lS?${LVo(u)IBZ_Xauv)_@ke90pRAL;>CZ@ ztLV7#R7n!wLAW$jvUtylwZA~W@5-^!xBfqX?T&mBoo6o-M1{(ia_$wYz|+|HEh`Qk z@xboor{p&iO6@_-hf5D?1fZ!kx|)_I^iB$v4mWYM`s0k-1LMiskTircKW$>vvP32! z_Vx1maFqRaEp2kSg79%WtKx5a#8jy%(kH^ULA7?!b?0v8TQFEh=6$`Nx3Te~=-QLhOymlSBPR-#)+^3gdqn)eVq`Yk4(U|)4kK?}{xb*74vj+%z zS4aFW^%o$2yc|RQnAy5^m5R!iT(T(Act-T{@4?dTfIEqf?mfe5=!2qSbz?{E;Ms6e zwioMJFLclK*#`MPkV2fDPGgNPqGOh)7s%T$5PQbd-m`i}!XFv9zWBRO3V!GO)tvLk zU=(6bRQHgRH7RK`HPNc8FzbpJ8gWrhW|Cn6f52X~^-UAi0!N&>7NYPEj95?u*)N<8 zgcde_%c1TeCggOuOf)~K)A5C1_Lncd5_GBzXOdKA7&fAYaHo0I45E8@rm(yTLKzg6 zXy>N@JKu8Lz>h1#1ut&q*(1dTsTB3@axI@slLaQTEGe8|kb#YsEV^a4_qGG7w_A`i z;UZWRLlz#k=F9*`-axP-wrIw=wcW>;F|maKus(a9%vhzOzd*v^FdV>+aKmLY~+ni|daHbB8 zvfj>n;*7g^7|--*5mq>gtTVL2N5_TTYEr9Y==mmgk{!4NfS7xUVWY@%sE%LcRuMW< z4Bh>Q$IGo=sKEbh*OOk&VR6RnDSE!Ef{?QrM6Tynu-c)i4S7L5vGB_F8T%q_tYAj% z=22J6I-<9n=v>|xz89+4hfmc1$pO<*5xLX!u8tm>+3RgmmI;wq%ZW3o!l88RIQjbt zPZWIO#}z@5{dauu*J<^{SiI_1fqb>frb4P1| zdSt)>1*&^k-~=^;PudYbcL2OR*7iEzuMiEKQhol{Mri=5^zjW%i}ph?`9S*JH$mWQ zBQE5hx;y8MmSVR5Eqn>}A&PykaCG-I2bBJkBqr4cq2;~R4J#k7+2Ii`nf?vYTcIXe zIl;lGGo*yvtB-Vjdk&;Slyni3;n`?}a~Y1FU*8P~*8yub>#EmgIDAW1v&k}$8Io({ zofOadAdAnVyG}1_!Ej%WK!Ur@+Q#r=4N3UYirGq96sn)oGCkMWJ>eGHFx$8 znR5~RHj?~Yoi71fvAMjZIu|0mkzvC;URZy+jaRwDpdv(B2?2~Q56Mb6eJXmFf%^{l zA;IPhY7ekhm+i}ZtsDpYsBm9_We1x*8rhi5Wc-JJmnSv9GxnuXhY8wrLu$x+P7f@S zvt^7l^Jy|ZUE-;6^%b~ih8W@U->1RCKHF`k*g!yBC!Ut=RhOWl^{o8%o5B#9IVhFo z-U$o9+`W&tj}$=?vE-Hbuc}c4hkfPPaA1fK9!WG6;HK~lr0Rda z|G72Lh~?doXVTu|h+&8^yH`HnXV`1CSDAP-vf z2CAp7Z@xpv<`1t+nJ7b6xB5pcK1I+8C2CHX3zLE4E+`}*+->h3lrU;?GU*@jv|ql* zcC{{fqp>{B|E`|{TRy+_YFHD*eC*2i7XxEp4xIW@u$3dy#}_+CH??}y$iuDSzV@Bt zz#du5u>SUbQ5qcYxvSMb|Js4&_OjCRViSXywOB^+N+|@kGj5#IC*_HLDMMavzpS}5 z5>8{42`q+RdcRwDr<#==4ANR%*sKlA=#p=g(=SNVP{LA3%CbPFaK$nh7=S-urxG1~ksj(>mX?&YBk!S%+ zo8+)2TyolFT|T+pGB|0g%@UJTqZiq)eQ1z z7UAK@Lt$)%5U6moY?zJGUfWImqJ7OFCzy_Nd=Ca(y}uK@;QZk`5^SM#)XV()Qpy*I zF&aLWJw9#Xh!f`=LuAsxiwE?{j-BcBz?p`fJ%0n>LVwh`E?lwXkLS;I{GB0D!TnN8 z_SYD@!t{$o^$xcqFdBs3EO@XSa>POWe|bQHwEZY}d2vfj4q8TA6g^vGqwJ1+s55T!nC% zK8ndBL-S-%_kDGjs1B-p^d{59!3tg~sfX`UORo>AydHOo=_j;|sO;MXn4Au}p`ci1 zQuI+Od`7r{#WQ8v7x^u!RqXL5YQ-bs=WhMV*^PKLo2;&}HY$=Qj@IUL1Oxr?^~)TR zO3>4wcl(dGN|SJP^=+e-c!F@TAYQwFjKK%zxM!`g&o@4&&Zic}UiJVs?ZPcJI`Vtr z%6{EP-?xp6j@vXxc)H3VFZlR4q>-5e>I?EmRG!`^Fuv(A+sp&EfGX!V{tF|M0Li5G z{dG6@ZwF<@KYy;xXv)G0)6FEUL~2~{iuiFK@gf@V{7{k~e-D1-f!u>FYkfq)yTt9p z7qXl^5Iyr6&z3kzc*%9jluDbN3!Zvr*{CH$f&k2bL1MHc388N7rN^p22?SC^+L;Au z&7HV2rrTpdnTYfTB$%ABw+FV+NmkhSjVKPBk7~&c*9R$)^Rh2kenaPaBkP5+tV3S7 z`@iu%hW}Geahr!s#YMZIM=1HbGaE?8fo`jqL-7b##WbYdV;G)RcXSI(lIipVYTjbboAnCBs zo#5GKVTES6b=MZzWWkl7@AlO1zm`>29Q9pZeEZq zlPRzr6*-A*25aG!G_`TRKUlKS9i}O)qzi&nG_SADF|L9~3i71vFoTOTb(dHA@*hxp zNl4zv_I5@QE`eWCbpFb9Gqm!a+laFn{BoNAj8B)29g>kd9C1(|z@f2@EMZkEyHNOJ zmX5R`XpTJJ)-U>+g@#kYjPD*TgeDX90MEA$&R;&5!&a}V;Yjk;}EB0e` zNd53m8xC21Yul^3D}brm6}v`b0$)--Nu6t0_r-B*aiXrL30vGC`I^D=fhj^a*(CUj zNE<|{kF?emEI)f;;qBWfBAgl!U?phO>VB;^MsCGQwMWkquyzLaO%Pw#;kZya;{^{-t6Sm917-ZQ91_?aa_*nx^q^`VVhWsB?@Wk}PJTNXxnQ3rlgNw~w(*Bsg2`2H9vhKfig}t5r z5b^msj|Zw((O~=bn*h+K|IL;cV?l^~D2-7!72r11y|iOYGA3w{VlcS-7}4N<+u&m;<-`Pa%Sp#+|c>HLB609hC8`MB?MAc)yhQx(68jSIePkLFr>Klf4wCkEi0wZ^S95gh@XucY!* zd0!C%N3Gi?b9^MkA2;uG2@ozGek=vu{VmD1uaenN?vIi7$%J+TylHB!c=LPv;X+4QjKUAC(HD4)~$jUA}(~{sb~= zYAffxq#r%5SZ+(1zQJ>1YNhL1$~gfyUwlt!Xn^-M+<(buUs**W1aat#D_CBa2;Eth zMn8ENOY=;%N&;@xW$XA%c#(pb9z9rY?&=w#t>FayAA-bO)O`F~M(E*hOzLAgBBQWvxT-;5$z4rKZ+UX3dKEiU+9z;9k{E|fb zbTk_B-$OG(@apZWTrk`1t-oo@^aFs1o+Dk-7-qKz!P<>s!)|~GrW9%)cK=>2iJGUv z?z0o)Cy#Q+Z4Q_PAlY`+=Na{omRckz`z{IkpoU=MBXlW14!}_z1@T`Ffoa{- z&85{=?++mVg5;)72oBh{8(yf=)WMPrpP1*5bHns{X=jn$>tcYV->TJbU?qestBq#aYy?~IMEU$k^ zK16|O@vXCi&>}2WaJjlnhlq%M6Xk%;M@^LelrLvWm;wL;?k_RQ@-UrT-4|ZGLj%ZI zLGsYS+Pj2?@%d-!&zD!Pe4%QMMP(R=nxh2W`(n4~Kf%(I3ibxZc==Df%IOTc_-HfSMhgk^B$+xFoEQAoLLd#IRLWZV?Q&}yOtF11)$xN6?2x>7 z>F&4t(EAUKTsz7~pg?@4L*25Vo-eukx+T60lGH1$+3$5D&pd-p7czjbHmUf z^I-k-$zzgQ&EK*m3O!8_s=A~3-tgaEY2c~-IcO8a+T({9tVxspksm-aWp~bpknbk8 zh=Gwkq5Lsvce-<&!=XY5-6phva1${AMbst zaAN01Pa@(eJi;pI$|Hc~OHV{Id4t`l|LY{OI(>i<%PNS1e(? z&i<)*SOgRw7sg0mDdGV8oVVz{aQ(e2N?EXCqyHgpZSPcoi<6cY<}a*IZdL`0N9FDn zQq!|AMcqQ0-Xb3e0d9rw)hV2XOqSJxZJys7OhXV^l*-0;Zs5!yjPXjf89by-5* z==ot5@29QP@4!Mgcs=d$hSG`RpI>xtMJ7V9S+3*n+T)NG? z%IO>|cJro6zXPCECYPqb=9&xOr`R8h`LDTvj!xtj=iYFr;mk*EtZupQj0&elmTzAK z|BmJCi@Xzf5%(r@MLvL%~b-5YJPK zukzmA#6m|jvpO@QH+#WcJS=XX=G6vXq`z!|P1`|LlrmWPU0^Ou1&lU!5{A zFrm0>J@?ApkivC7pU4?#2{Pn!o)Fs1jVYX#=v)^;)i$Re60jfUT6izZ+bo#Ead%!{ z5-p6=!n}K6!d-j9it?&SJ zAXuX#+!p+rq#s9nA2TnC)AKkgSwRG+?u%7N6~=T?;SKfU;vFDJQ0$Ul&HTm%Nmeqt zZ`IrefXsBZy3c|m>S%H_X3GPz39Ji4ZDfyO?4PG*=M~xi$`E5jhk~5uh1Q(`=bN`N zoeY?T?B!<>haLfkq-E-lktKczr#S?dLb-kz;a_Mb&+<8q^IqGjBqN{VO^tG8|&pf8+p_V|AU-eS9-)j z?V#3Jy2TETH}*I0J8ox!9)34_J$D)K^p}6^mHD*I2Mu9wYlRJjHf)Y&qYMk(1Cc*| z_$IXza4`}Sl^cKErsJuKrtKy-0W$sYWr&mWRwsO<;NQ$eLCDDz2K$7Dkse+ecrBIA zC=Iy)gFCIh;1)kxKa>CGdIkiMRECqL$=~(yg(&S8*Y0W?YCTJT*uCAx7vTz4d-l7_ zd!<3U5nJ(i)~s_N{373JrG_A?rOm~Bm#ERfg5gVc=a(RQrWXGyaxZbm-H~G@t}fS*>8pAbPp;#Yfxzh&mvVbw>Y@Bhc!R^E@+Y^w${C zuPu9jJMkZ!sw~aVqE9y);lwPxu=WwK7uBAI7j>=~qL&R<=MJ_Xvn7wzoM4W}O+GV8In;Tm*$&J+SkJvMKvN&_$yLEn1tKxOkwE&JoV+HXd+v zm-nBPz4nlfp8SzK%gqSi;?DeTcDmINlOLE&G2bUrG_z^eA%;jN%*{qk+)gI4lVbfF z53jX5p$OUW^Z92Nkd_UUm(bHn1|oFo|2C~=$G-k@%VW3Q(kxW+<-wlUkU>8&Nw&REZ1 zTWkC?F~u2VVt0naRs$FA_)sofOR$d4_~md&_JEFi6Z-A2T?8UYxoj1#t=A~%tonPF z!*j3@-2+B?j{%65@srKx^B-8wh;?da>#Q~oN>nVW>Vp7#TEeCGg|QPlO5YdGYXx)= zZRxqKLfbqsgL{RJ;3n3sZPbvU#g5}QAovh%=V3X{2&bVkpgLDW*9SFbD=#;m0^A38 z_@!Roi-2|Ezp*yCRRogy^2a%mhacMDo}yP=Vou<<7EXs1wHybr%;EXi!ned!SnQP1 zKbfz4u&bAzdOZN4C>JLg8cp{Fplzm;ft{Rys;kyBu-Ys}+;Tk5E64uy#@`LV(7Y zE@AXlC)o&73Quhw8d^|Aq{$Z_22T(dAY|6KAk-3C{ZH+_IrzZur2T%eDgXsalvaGL z-k+Gfr8AVQ`$cQ&(xjv5{kmTC2TPLk2c<%nTk-_(3FQD-$xbj3qBP_6-m$B@zmNU zq1hkshl(eg`tC#+;R=688pkuztSm*`rbqGqI^@jnKS`+`CT7v;w~D-PoOeU3KWN+q z5(GmHceeWF^Z^4L^fU8s<_fX-?Ba4pJIFpL^n&O6`)$PB3kusX(|X(+<=;&0dMpSb z*I3bsbCoLINI2$>jGhXyc3U=&AG|P1!@~C!uWXTlsV{0_-uue^VsK5S z7S%o`fn5E1-Pnt#0T2cJsMWoCM8Xw!-@N#KdoJ;jhypFP0%1Dh_1M$**B-jb&U`mN zJ$+$>Mwp)~r=BI+cv||BYn#sam zgQJG-f6)D*eVe>`W$@0$4qc2EX*Yb8Kzlr5=@*l`nkkkWka>FQC>*omSASK+MU3!_ zlEGh-7DCa1DtyL1xRrt;zPPo$&?L!9P%QrjopD=&mSWT;zBb?x;igC!GjR(r*>cNm9oxuN@qM)`jE zLzphMrc-5<=Ynl@c{<9U17Lx>o!|J%ehQl5+a>hd9BS6|-EG>wVjgJsqw}rGr{E8p zPEYZCShhu9l)LZNv4LqgW;fosHUxr!^7%WMQy~TzS)ctSUhR#KBpcqeXn-<6^LXT{ z?l~Y`x!Pl!^@#9L&C=fSH4nUzWE59vi8{eWG&FLy?b?hUj%_L_-QNlY2$Rj}JDIN1 zvD{{lb9%=K9`cO=Pfb2|xMOOTWNZ?2c_`_eQZ&9dB%Gf5S>9D10^FsGGTZEGV8V2( z`nQ}<5YF0MKf3wCV>^s=Yc{`22D?nx=omZ3A&lFMNkM^Wqy`G5SLomrL##Jmtt`{r zaupOdsPi?&JIhi%5kpenw#tJ%F8$Qpqub@gJ?MyGm2F_}aLbM+K}y1^&mtv=5#R59 z(te*Ca`l9m0jKZ^pNSocBnkB#M?n z_u_x2j4`d?)qZJH7|&v0w0Ku=mK$~dXMg^W5s2p%U&I9L_oYX;}mB!mYGzyodzz-t*tHe)tmLtxl7B$5sBh zqQ2B$6)FV)iXkrz)?H*$-;2_|Y^ndC1S7FoYsyc^p$H#XP*QfBApjTL|8^DMP1%9R zu9(z}{(`eQ!+ulgqog#FY|d*BErxjf>G5jv!@o4#;-3>R;y`FRt@()4zU6yk>-Q}R z+HnAEWr${F$|sFIO@X-Ps=zK-g|-jg(% zz%8~H^e^qSL2*=-inC{dCxOh|*ZIW!i!I)JLm|3T3Ak=|eBv*YzCgo+k)bOb1O(&W z$>8N5{y_h;c!jK>1eSuIs@R~I?TYA3q}G00(&=54jCH$?F&k&Ra=Glg=0Rv?ecROW zWGe(pye@j)+y7q-MlsR{j#R7TL56dBvTYDdQ8T0Fv;z3ydsjByvEWpNz?mG|8%>eW zLKW(7w-^P3VfZq-n%&0fi$aV4*mS=Ie?Rqt;gi~g1$rN6q^bB5dLrVtBUKNY($SYV zH_K5+Kptnb(^yoDT-H9JL1fhTZh>DePj0V2T8&oSm=!SaK3>4L?`e3n_TAS?3h|cG; zH^X1god?ET#c%Jc%)tcV%JVT+G+A_=!Q!-3Ju<5cPbD^`aV5a-Uy#^$`4L`b{_e z>T3w)EDVgluKd40GM=_8==DNAySl0W*ocUrB}V?BQz0yV+8q>XJf^v5$& z!zd1r9IphoRvh7T#ay*g0qytUTiQvH_CgU3*!euS-KGqv52a5sv?cktAcl<+M~vr4 zp^_9WtCja#=Dbi)+pKyGVY*98t?ud^(gX&;N8ajlgsx7?mNnj%a3`cSe<^*}ETrnI z4t@9d_3QwqomI8<#Q$%7)aC!;A6cTDFL_zpwIKioQPfxWMOAT*h+ONocecRr(aEfE zgv#KG(&TpS%?<^S=SV*LsGdGQlBpTVS@*=pO%z%6TaNrsMpu>ohN?!xg&*N1$LnO4^1ZxCW>Ok z4%W9$k<1}kztcV<^Nt7A<6KAQJ)|~OEqa>kmuFu9fZScY_ju2@z0$xZlH=eJXZq6x zpWm&%ZNCy22Q+8mcHPVpu_-N{bws2d8A^IE%VK-H5zgIzuF;r469=@Id`isrMXQ$N zp7r-&jHi^TZYP1-c6Deuq%MXyL}^-DdkZ|Bkx83$XWw>`CD6c|Cb;zj8)CBq>**aT zFf~zc$UBzJ=#7V(>Ykghkk0R z6VOY39g3974f@bkJ>_551B05*QRMByw~g`1EuGFP8^D9k=LlCgeep(2bAzSN_dp=T z-MDhxW?CK5(j|}X5hN7Mx0p|e@*4W!y?@rnEA9iN=!(hlrc_I3T=>xKaz=yjLnDgx zbG|*t4*R1%DTj>xR?sgi++TH5ol6XL2H)?;{|Hhno?C2_=Eqrwm@?+GT>v9sI~lF-+1On&ID+z3#(lIkkH!T;ZK)*U8|`XoGEF~=aW1UVq3 zJ>B;9x*Mu96zpS6fS~9|WLv-wFy4v$TpdcOKypkOv=O{mvC|EwJ+2duh}8BZ2MgFv z#y5l+eEb}M1%zdz@gR#ou5fs7B;Nryx^VlJ&-{~;7zH%0#>WvU z?W^C;tv1X4cy?Q^-A)Ta#(L;!z3}#rp!TYNyz5;G2}tH{1_t*R(F1VlH3eg5R}zF7 zpleNEZz~yQdvU~S&g{14m7lz^M6|?PeE2wvP(8k*j!^zj; ziL{q^=7Kso>}KfKpk+h7D|&Iaj++;j4EV^i)kg>*ZW;C874x}mvDS->Er=Idm*3j* zyfn?}0?%8m{j+?#w6ZzN~&uiA%EejHZ z{0-gve693A_(Zyk`{elWfYyd?@fWf(KA2syP{^M{2}#2iY%R;U++qR)-!h!)F3L(( z?Btre7hb-%#D1+SDJgbP+EUF6y=JNIkN${m-y!|T9?H>@C-b|0E$l$mhg0TH@)FNm zDz$wBSAi3z(-;2pU;}LHjJLjLQ>zQAnSEI-^u9I5yDKZO) zZ?WD4Pw7&huMHC|n*=3u(tCLWBzn?*lvDdytroufyTi8D6f@LXUZ<#zJQY}>a1Ux< z?Cd0Ci}V+wVG>|Zu>|Xma9vT9-H3LME+fJBd@ZoVWE6( z7%Dlg0FQ}q!OMGPL^daw+oS&>&lbhX7h$Jt!YFay-Z{+jzziYv8Lxyo0SpH$RwuzZMU*jXZyt>>UxZk%>TPJw>EeOG8uqg*0|(e97Y&=KJ? zZtA&D3eF9?EF8!3rkvmff++Z;8hwi_# z0Em#0%zv4ba0*V75O_Q*N`}5Imv80<7|WFpKVLuJwOCJ z3L#4GCVm;A7R%ze-&sVWM)nS$duH#3nh$O!8}P%{2dRI*J1t>`omZ>RH9R1V%ae=t z|2Vh|z2t32h6GfU05w8QuyHcTF-5`$Hb?sA{=eq*9Qph=Vu*g04_yV#K-WE zolbbXDy?Cn0_HX7Jnl>NbU!Rvc_TCP2r&&)M@=LRh}dE7RR2hSH;786QWfo4zz540| zu!m+jUn2~atH|o1-^+)fm(sHJ^izhTbhH}j^Zwl)O`zmc{hjt$U)U4-`6v1@4*@>R z-m_MTJq2j1&c)KND*?Uu{jDFgj%~KbR@*In<99>JGW}}Oo2?o4NcwYC#wJ1Nd$Ziy z7|;CK7bW!epW`khS!huDzG(6OT(LmWcWztq0jmvpt&I6lFd2A#U+!xdRBB_OJT{4Q zxzS~Zqk4v8mhS?FnN~J?E99^)61J{tQ?(;tpxEU4-|%=r~O6_xAev;{--wKV^n70A(KwdY*eQ5oUW1T~OUO0Dl#6wQTa$8vKr<&F1b73#W3)|uppovM{IBZ8 zR(~wF!78drgeWFV=M0Ci)BqdMV|u}I9srFKu9WD`rJLZHh*xihnu6@@lbZ0e;L}r;fve%3Tl<}!n467T7%psJJdPe{j;uu^oc}Ckj-R|PEkjy z;s^H8Mj--c=X98r0L3!(^61}}#-R>9U;2d{&hC!5A4;#dvk~=6&WktRTXP2>$vkJ{ z=b6xLiejRM8*&Dqq9-2TxqcbL$YoA`L}27`RV4q@@#@Y%0$$XsJNWV77}VakyKtI;D)I*eZpu)V}_rYrMgs5_=B|FmKql*ZW^Y!4QifM-;4=ry@N!yGAmJDpdwK?^!@ zKQwlqy8s};<~uZ*7FW0rgVnjuGG$DW*1Hga6GVN4(xz!blCAegpOHVG=`k=Q3ESF& zR{S~eNEy4)*-qbnY6HyTCt;+`1{8*Qy<6gsVM2b+%uw-VbSgFeuqz4W%YZ+Y`eZfwD{~p-b$6}8~;nFi)QX+R7?m{V?Pi?N`LNRhcpXB?`l5TXi*I4V3cjUsrG^@Xk|a z%Dy*TI%|xATq&YKL13e(q8>-23SCf(MnIU$E^wS9LzCnlk{~|8BNidIwpK@8xMoU{ z(uXQ&Xv@hcPza`I9POQZQ|N@vRh(bD5~w$d-xki|g+)_DPAD8z5rRi4JGia0T)+>> z7q@hs`Uf7PP$=->%XOIWes#W!XMh0ih%g>6)GqbK)WO|L9xnWLf z^(W1bg20o|)?hRDFI)zqca-e-DNf1@D{Q;QHq1@Pmp~`8)Bd#>T8i4|d`AItOk;xVdRhI0xm@-mlKylxPslzu7-j* zEA8hQV7!D|Zlq6&Fj+1idMEh7w3*hzNT%FQifefA;RPcr^6UK;?NT)aE zDw!!!QYbN=Czxm`fVD)eX$lhDDgOuefBLFo1z+n88&3dElKkZHN#k}ugeNyX9hQKk zhW}Ti@D?Q>6cor9desN^RkHHsW{$tIs8diSh-3nAeG0qxi!k3iyK$kt)!pp_P|2^0@I{fWrjDOM1dtL^`|j=XoHxdway(u$K_UAUpLg^= zXhhNO3@Z>Tl=g<`n9ogrcd~N6Mdf02;5FRov=@ zwmB*-Z4X^P1NGUsd$O;1bxd%l2G>?mUqWb```zRr6H5nFS?IofvlNL>ghHu3m74sL zj>Y#;qjhc)bG_6Y>tC{!KonBJbt-Wfid<>^pEAg6?g)3tzg>GvKnkUYzKHjbX~^W& zsoG%&(yA1tZ@=))VFfp|!+cEbGa*E$@ToalZVw&Vi+>%JQ3S88@MgWEWY8BQc}1GS z5K+l1Jt!aVC!m8`HCGp-EkPNLT5P(5-<)oPy4tsW=?m69_L^*3UwWI5gcpaen>_hU z)F$06Zq4okop_~HvT^$fNLT02CWc-*>y3P`Z~8P&FrM!|VwMxPkB+Sv_Q>y8fnV;t zwTJr1Ob_Kt&U`G1C6pRPlg&xK&Il<~o!`n^K{+f*UVpTFVye^;uid(a`IG>9R5<9H zb1hsDmnM$?Hoj#B-Y9>;l6TR{6WO<_j-`qbTV2na;gM7ZeT~D?r?q;Cj!%kj`hpUu zoa)@6=E@TgtlryER-4Z|;En`It=9rjYKj{I=8ChhnCU-N(gTR%&9t9d-L3b2Xf^ck zt@vE1>#>}yF0oGHibWB%uD?Xgkm+=+xj zNF&TR6E=543_;#=?$4qekxHlc%|1g9NYKKhHCJyQ3Ycy znnnhA=)e61%9`{&VWE9PmU&?IH`hnySe-_h=iVt?I}KjPP17&JgBe2&~PLbvuuS&wSZ z4k?8jQmjWW3D^DwjUwx&-zip5a;~;K-SQg7wd)l;8VolPq5Ngpn=T`UFia>TYZ>oN zc+<|SX{la<7O1oI*+V`#c$C4)wY|=l{qW%7FV;h4q(&-bD)6^=a+)(vjrTufe;-Oq zVvBjyU{9FO3XI-cOXvWt>L1P7au^cTP=O?yOHj&m-WW1BG<8~Wdz0tYvQ5aB3j_m$@lTD3;(F2=X)?MP@fWKIN{}Z>% z8||~++Vx*61TPjMJN9)Rf@y5`Q+HQiz%o_emV3VfChBDH`*Owv0Hrb+*WZ%QfX@kIMm_^^ZBnfjWq!o=+l~=y#G}&Z zb19F2IctrU+g(f1!wj$PhY7I}E+KE4$4~^O{6arF>bnOL_NDvfnJVdR4w$~Ne@c!W zV7dR}=*r`v{JyXxWKWcA*_W|2m^4|heV2V-217_vDMS(qB?(C)isUOvNR+tQk}aen z5>lxo*^}yb-rv7{GV{)R?>+aN=XuU^3a$jF%$xaO)31C6-9p6l!-u>EVJGB$aoqgJ zTY0NQKKX{`bZ#S)2R`Aj)OD;196_nHlvvVAs3>-ENSSN6wHpuztlOdyWN(Z%)fIht zc!RhDLN5KPmzlIg9*^zhHeMrocO&w9SHcA(oB;-rG#XA4x^8UvnALL#i)quj>24f2q-9$ykik!(qeZ{qbuAq$8&w zL=Dm3hmtw1osNAW1j-lF^tAsw?T!nNUeh>PM-mgJ45@7DNV-l(Y&U4)S8o8^vFybD z;BCDfIyLi^v1$dZ10=qRM&_@1;drgwTGm2sNRkDM=e7J815nMa+%&@^IQWTM-iup< zdP6InUzDwzm@*^GSjbn~Z-dCnS|ndiK)c&cockT5Me_X1 z75#8Eki~DY&KmTW1Y~1M@1i+-pdn6B&S{BCgF+|%@_qx(3>%dCZI_NyADlh&?*-Rf z-q4T>hqEfEt)aW2V{}A75qZK$_*J>VW^fjzqUDg&VMq+ATQ>dhIb~blLcQ*r-LPrO z4>8b%_THtn%YX(d&#Uu=;CAqXoneeo(0MiavZkMD#W^t z{!k1Guk-~zPVq4sepGlQ;ad&pxOGyg4IDOhh{Iv|3%W^S-%8Octxo;((-l>o^=QmJ zO1h&-30aV2*z(XEF?DVma1sUR)1${GwqL;nH+>%4nnZ_ni)@Sek=m<)I~Zc_)lY(L zI`(+I!jU0_O$_oJJvs@D#Mu1p`Qxy4`94%ECX@hPR<8GUZ^{{eRLRNPTNesfEV-dB zXUADDEX#!cl}$j7NeMF7qN_P0R#*GW2_*Plswict+En~QET{S5izzbe}}N9=_+ zE?;BhVu7E-(ZfFZ|H27V_E!Aa^cC)!87a09=l2MeiqBa--liO7 zM-@x78o#9FfhdvD=S*%HKz+>2mW4zda@2iujS`}L3(ouzpw17MZr$N$>0 zRJ`#}+l7jOMR-eJew?EG;`Yatj?Pc}*$H86_6EL-h68@MCTm9VBQYFVIk8JxyNZS$ zX=aQ2Bm>X%@z$ku@l9@cXq4$yoHNu@C1F?ebfNo}H92l#-4G-geI#Did{#p&cBHXa zR+C7o=VmUOm=0^;p}KJ0nf=6&py|WaAvWq}m|ruk{o_73i)$C(a#uS!VMBupiFeLJ z8e&5dTASkp8!l+JbI~QTJez$dmtJqN#MWE2Kkl!9my+8$%WeJ07coDKjJ-Jyb#iCs zQ!dJVOZ*^hwX$X&T53?lXOaS*A=+-B)(^s z|7!EWk&o+acL(?th6i6a(R z|ADdZSpp$yU_2pUP!1|Vnt|2(vAe{wPH}nPTA21hOMD;pJ|l(+mf2{F9o7Zovn>XP zu6RL6YGZJhzSL-gGfWu$%{vi9*7(G&!1sC02?VF!0fulldU5T8+B6x|J{;iUIrV3Il zxcZNsoAbjS2N+&PsX%Ordn-NhYX=AZ^rWC|az9KXnjf?nkNfG1rJm_b9=;C+mEy*= z>&LfRp~c?e6T9XJX0~qe`9>pI4;1MeZnlDmscC@|hW8s3z{Frq;`R2Y1ol z>|{qx|4f^1gtF!}vxvz=i9gaGc&2hvn*b{sGz(dhTHI0EhtXN5Y={bTJfVR(7L>YdwR!6V)!gZz=Ql?jl4$g)0Xan=dcKEC#&xQtl*oR_Mx3u}638I4$Ylanb za3W|$^-avairAF$v-t=IT)(U9CRq-)bR=#hwlqZ`=EoP~QPBt;_3eA#a8Lz4qj5W1 z^dmAI4e@mEaEbwf)K1B~f~m(7kNos63JC&{_ahtWrph05gkHWMy5S53mYme3=x;#4 zOm(Jj#FB3b&8|i12c#P+n0F}tsBK(gLd8cOh;uc zV$D=XC?w?W=*6$dd!lu>xofnOaO5-hhRq8u`(Ri52eb<@aGtg9=l=Y6+7RjQo>I=U#;z*EGY12LoN#=iqEMh%9v1d-$Su6%(;MZ-5|Zr2D3uL-p{J14U6T%2GxY z-q?d9#~mGT@6_j{TpA=%$CTH9=cK#i*V~8W`@D#&b=sxcy_*f<2G_#A>zyP!Yf7~i z>D0?YI$q7~YP~K*?8z(z=2*TGZ$x=mW2N$p_=$xI_x1?zE1;@I*?OOG0L(KCPBN|= z)3A-K=ly#jL``Nm#Vr27i;hb7o*4<9fMdqWyIAYY?~gk#U$OYCPDs|4uLeY@{Byyh z%*G+M4+)X-yz&NrJwG&c!d3iu5Uk-|-{;@Y+_A?l&41q?ECK@5)=DFe(asZp4cvP) z{1uQ9VP`(dIOl5NnSiqD`z^3SiXT=k+KW1&vlr%Ts`f(~dtCJKOU8#*_(g0|an1jY zwNa26r-L6EcerO7ZFm8kWB+S%J%-DO%HLCp0YH?3Nh(p#%Mi)0Uao=dM4>`+Irfo~lv(C5k}RjxT9W z7U(NoZ|#vA6wRoY{^IxtW*nw!IIQOh7f8;o-cAU-7f=vOu+vL0iEXZuko`!sQ`S6Ft9Tpfis&CM4w7SuL7>8mZ@iRrY@ypw z@>g0c0X9*|zivjffpOb`sYIbh80SFfmqqj&M<{euot(0*f5;ga#2 z;Da11zgE1owZV5IowB!R!QW7-&d9EOfN=!RxOpC*Cb~2(yM)Q~Z$YG)1F9mqMEX1+ z!n~_B8bptI93Scgh;N=&6rvm&G(^SXe9Q0tBawKi(o1`~Eswci)Z26HraL3#L$@SC zds~@oF}3$$n%F5K(j|^YN?tv#kCUNi`NagJ*n%mw1(&jDh@9zkM|=kubgU)SeK&y# zQ>CddWFjPqVH}>N1($|KU|4l~{-eVbz-t7k`o@|`o;cl$Qc|uB7j9Nm(0r+x1>*R8 z`QFuSe^|+N+QuRhmjPJeY0xTBhG3Z<8CPuRtbx1OQ(4DP5#n=8y2#;*1PeUZ{cqu6 zKLKgthj-rh%P1l4e!s(toTQuEDf9#TH2Iqx5PA36p2wb~IBAMZ`Pb1KT{N^v+gxk* z4>q1l_vz_l{TfI>#Uf5m9j0A{9bi!li~(9TLB(xVIR+rk(;DZHfIku&a1h$=OxmPG zY3w-jh~0*cW+E?z800}g(5Odm&2RO^2B{op!jlP_eJHf%k<%w5B$ZnIK<$P0B6+Jt zXspu%Xo696$u(5&(xJ$a-$1(nwAdN$Vc&m{D1!SUHtvae;EQ5)HC;0$iI#!XHa50~ ze4uyI!^PB<;TADXDj)o8VuSehUouqPK-h=#9A&q9p@1X5&5tqIL1d$IahiV&bVhxw zZ)d&!z+R~7OMGh`1zl{}Z?@l@Aw3tNP>*&szG-km+LErG?jsNgDl2x4V$%R*I&AEp zz)CXQMk&-4obH>GN2${VdCtcHvmKg4+Ml-gBRSC;!@onYhI#Y5xl{}EFvqv*cZXjC z&NaT4v37NfB;w^-v731UPcqq;D)0#$<09u1)#N{uIJQ%4ej1EDW0Oa=z#sB@bisS^ZB`DPBvsKLhB7!u#}`Di zf*fJ|&4p}6CgH>w)2U16*;2sO$k45KZdIAtBu=4_8A*| zw5sZ`s64=F={ppjee~79F5^-0vmc4tR%FL6H_Jc=NTcSCF4F;-nSNjAyz!J17JG60 zoM}3V0UGLP%dMFm?l`oX>r&D~qIOEpIe2~RCKWt0?^tEeO{4-I+A*&ldz<5^i}rqE zRlt|zX-a&1qU4N128{T)o&sh|bzr?oKD^+D4s6yfj#IG)Qfuz!u5T(%PH3?t>g&NJ zfHePdZZF?p;g07rQusKIL!$N3FhVHZ0}lP0D-CVT1Rmvg@=MWEZF@YJI47b7ZOD`Z zzdsmAz-UW{`^`~|BLvdU;Oq3X>KYYmnZHh`4}$D|YP;#zovJQ~cKW?)&tq0FZ_7Fm z;`9mZ&Py|K$klw<9b*I6(i36;k+qtfZtoze?x5h`DVjVJa=4^k;E0nYl&Wj01AI@m z0(&#@K*97rfr#|UuIyYmse^K3<66##66AiOdSr;$v>Q5`5_jQtHE5ftkG8*H-+ct| z`30G?_2qluyXEY|>0x*DQTlWkTT%dVNa8HTm)ox!;GMD8)O#K)(KpbThF{e~oub<>hZV zTa{vlUu_#-Y8E1J+`_yN#^WSWOyivt;@bd-xBjMobQ3WP9BVspUqTFSN6Q^YnX*5d zVgGYd@&9B=Pj*tIvOYg)A$cJI!3d4M>dHGn#2}KTnw}@>5xdKIa^}CJO4i7Ybaq;0PY1l90 z@?NcDnf}OOwPPae81OrDr&=~VnDj+F&~%*dd?_Swm|H%Q_j@|_)WXKaFz7+gwL`!wM==5YBa&J6k>>%8^IBbK0m zK?fFT5h2RB!&ta(V2mL9%J$#5Yvu=yho`Q@&H0L`lIz&5?+@~;A2?U0KCKeNnv0xRfwcK=(|H&-mjUH4Cw=&&bOEUSU*4IK@5ZZ`g{yicB@|Jula zb=3lwmLA>hWlpM=rd0kp)h^8H6asy;sJG;Sr<0@Ver{_sRczbvXWDH$(%1km1} z)>|%7*X^-z&AVL&!?xg6lD%R5N#_!@CU&ZlA038tpx^naU2CW_{u=S;Tt^R_k?u#& z%M*^8;Px%;ih7rbgg#7v%Z}~d$g|aKq%a@a=^3+>`bCwz@o4d_%||kcm=%&2Ez9Bt zBQ6cpdTE27P7Sr#diP_jvXOE43RiQMj~kZ14afTupx=Xxh&z zgI2wtBwzaufED?9vVYo0AW%MN@t&iyTqt>`@y~|H|6qDV#vzv(T zVdz5X9U6=*`+TwY`7?l3s0`y*J6C%sD1BY0*kM8dBGQk7%L+~W0XXZT%MM|}gmV1Z z-uyYb9ID(LSwH@Tlq*kN-|&2@E#3*m6{PJ>kN+$K=@;!bpVF0jK`c~y>2Pl%&=gdk z&5svonz&V-EZi`w0(3~&al?)063KY|O-Oyli<9}(f2J#1IV@n0`qSa}^~Z#*c!=BX zwPHj^l*|7yagRgXayoM^>pgUGeNa8S=TR9j4!1@;4vH|^XnFQx}5L{)FCf#-)+9?;`TRx+(ej`P{J|cMByE9xU%hyP7YwOq{df6l9RxIxVeowNLJ}QoX$y;qZUkuo zC3@q_+=1<$*jTlP{CJQ!o4a3fTw|}KW92OIg}obzLi6qJm-#~{01uJ-oObvS98MRu zu)BYKjggdN_-z9r2+RY_8X<7To0KFt44aI>Rjo1;ToY?Ji>3*^ z{*!RyUv;PHxT3_SE9(tBe>v->9U*UBQI^6!u5aA1B15}}2hvMuXw0Xfo@Wdwt&oa; zostmUs~Ept<@6!GnPEMkFyr$-U9oo$P266>q6sfZ#6PSBqR+wn?e)jKQ7qw2DzLia z;)+c?7ZZrO!7_yF)%}HANOypxV zJAIHdvO!=4b>n?7c=b?X47)pJwisYH$4kG`0|AvvJ`$+%Vxt=#U+(P>-bym%q*R`l znXrV&z*mCKO>QII-9pJeJM!$#BPX2H=JmrJ6L)Uav03af;)5Pe^6cTmVC2_#@MKfP zvJ2w7W7YDb6JW!_GrGsFxjAC;!`3kA4pQ<~7#6u*OdGqRcWfj0_Dew7l1*>M-PiI* zQj9{I4Wmf0A`};{>fzEhA9UlqAn!Rn!cm8nOKgkoJpj!Z>jj?{kk}O|`+I5*m6QUU zBKT72$~yuP^EtuDXMVsBoAhTUvyZ^CNC~~TR{0Zh_&ycigA`(zAGzzn$M=xK;G_H0 zPoxk{0<1awje{1jB_>rrR>%?%hQ;}}$-j2{V2=ORx5ob=tcB{9cLXH>Xn*KFY3YMC zykWr+6}_PycDO@oNi@h5>K$@PS=%AaeST<=s%6ru94<>yR?Ns)4)n%z3T829`iY>T zK=)EylJLfZE85cq+^|Wg$C$57#U_Fpn>X*_uJEm{v*euTOPMMv7RceKbE$=Ln+*9% z&n+EKUQgVRQruyy%mef9UU3*_$vC2^9n)umwt~m5z)@qh>T*@Y^ZD_~Pn&lDtRVQe zK2cTK1;?EwXB!kj_mj^2<<80NG?b{fHPBQO&cWZXwya!98VarL{WWsyzudaizEMenWLup)-ZY^kr3u;{z%Q23jh(tr6XBv0Kw;^j%I5AQ$umh z7aBj6llI0@$Zy|$(lM6DhJ1=!l8r!F=*6w>F?;QkECo&iYB)N~n>SgsZxS+I;!gjK)5IT+jG79_>1UZtm5bbdXfvfU0 za)147Ylb5Cw)Ae7Cf;7Vq@Utkm;(+8t7+5GhChlKSh>973w_qP#wAh3q-6=}qhIf5 z4@LMO)hV~Kv}j_q%i?lU?re!Z%8}8xeNsn|Ht8WLj+x-ums2hh%%KT6qF8<`S`6;Q<&nw?0xnVx8=E4*5;2KsnNn!J9_d_}_?I%(aVfm9S`nqKgBSihO zX8?UB+Tm>~wDnobywKt!NyXvI@b)UBNIU{fE~uCx_hR)%_?r?D#b=)`1KfNfU4Et# zav_9&w&6kGLI=ZnPtE|~iS?B?bV)IwQGNjdMovgY1NdJlT?+C+#mzR8Tn7PYWhhUt zKP&gMsTIF|uRlgOoW;HHes;c50zGos=C=O7FK7w3bv0ua0j>{6}q+(Sk%syIwxnYfxuE;~*-xBDWo z&3S7he+a=ImA)SO?7SmV^w=njoB-Kb-C7c%|HlQrI#yu+;st@y2(&Gqa1=$A^~o!P z6M)hzEuD`^TchEy!p+gA2-cY?@2p7+7yu1@cKZwH#0eP+b6`Q?kvnwc(Ixt2$`Q7E zh`n1)!Uq#v>7Xo`>}3d+OGxnIz{Asf@mNR9TjLsV#_bIsIJ+_jPN})m2aPlddrY~T zwlW_*j1aAMQe{vGDB5u?1<`CkOaoUgyKtGpZnr!iYL_8KL+P3vhN2yWOhM6M(|7S^ zBRr+Sa-}C0{!83r(T1#EAC#Ls7ce0Key=f-eeW#ZyQ1;uoB6M-@xf+yseUczo9Bj8 zzm|*Xt-~*jNuH(N@P~q%w&OvRC(PODu)Obiehg*-BtI26acFB)K7|%D*^)?7!v@=$ zN1by(%9eQUDkD$34>HPPx+*RYW6LO(@~XFfGa(!&yi4w*Bq0kR2X6}9e#9FW9cH+K*p>mdA> zo8y87W^mb9_fw4|z)zWeu5^GdYJrAd+^&xPLFCqWKMfgxrKQY&ERn48@G)y-4bCx3 zKUDbP-jC-$Nc*L!TZ+QFH^W)|@!Zavug)02mFx6_oX8aD`H!q-_FD-|HTL7FV+`>= z*lCZeiJ>$x>v!&U^HM|NUK|(A@F&uM$aD@Jy1Z3M#S3%(Ri%}?f+D+)y*+j$(;GAF z`y8n6Pq6$WX7S>~Q69L)roQ>B2^0`@o6lUDer$^3P8c6%7bbB7bmH?@IiT4S^J&s^ zifHg(B(_Sw8O9#O8DJyl0gw zOeh@jQRnVPfLUhfKYZPTH0MZJ7Y)=bIkOMNWe;4NTL$0coauz?uU33;f^?dJgoP8x zTIP-)3usa%qsxvPjC9;d?y^))Zrgn?ja+d0&RC1T=eEQ04{c3k-|pm#w)R#Q#PUD} zOXD(=lAqp=hRXZcYg_@6DQ?|aMsqO6gF-vXs}&%c3oxfd4CH9wT*F7-Nhg7gs63V$ z{S%`3V0fudnHAt7t>N~5x32i%{FxNPo8v@4&7PH1zG>rwekHt8wSV+ZmYSRtwKLui zEN`*Oo6)7cpnxkp^dnF4FXbuU!iNZ2OY0l+F+w{5I@t^Tb{$p?8k{~zG)tC5EJn?{o*4km^pNPUT@-#FshKA#I zJC>gFf@^PURmQ!A5IW`&KK*7mob-{0Di`*_cdXbR7p}2}VJ$G{qu86-w@(Q=VV&=l zs*cjceHw1NcWmyb1 zz@|`r%WBHfeeqoWW?{cr0tp$q_Vpbrw>7?bPoc8&9xT)E#WQ{}OLiEYXuMQrOjxCc z3sroL|KyHf!unl`31J_%twQT>5`zzl`O)2ISn{#E+5BMQb!!&Q~DFe0n5EgSL$Pj;LG}RTu#8xFx;hih?(GJJE6B6>MZ`>y&rr@{xUj5o@_G z77v2Yj8v=8$8$vijU4KcSF720cQ+Nk50!B(pVeE>Xz`oG|UlVcswf4!%c2Ij>V=pC1Hg>0bgbJ(|k>@sJ$#u=abxNN!w#jgi+t z9}Q5Rdrq`M8&B*(Z38vH8_TzSZ+mc!z=zVs4K029)nlg=4iAfDCLU;M7oa3rs^M8Rgt~e^wpl1u;VhzWE~8d>@cnSkHF7- z;xu>6dL~$?S))_|#!V>~pf!v9>gscSO)~mhtudOR3|*b6Oa@+C0J@^HM_%+uC7Ns2 z6|bDQCuoDMUmgyp_7aGUX5opBB}3H7lKB4DFUXoCQu5a4ARS1OW;`_*NI2jmN*Za3 z=UXGk++^FYx{zh$ZA+;822&ct`2uE(9s!&*SJkWjfnOh|1a#Y75;feLPGRKBre0lf z$IlPk${4PI?PR5E*qj&RjL+FSn~r#sz*r=8ursN=#~8P{93Pe64?v+m$FG4Ic1O&b z_&C1fA>1koGupv|qP{qB!fU51G3w4+t!JB)BxG7X0)F1oq-YW~&nzg@cNzK~&Wx67 z>!=SOp=h;w)wY$x7189UUKU2A2QKkD8c+T*LV+*-o{*8;9qFYS=GQsUOwg^R{_LLK0Qp~L4Nzq#)Es6H7wmll!hYS;t>ODhvDgT_$pDe%YifiuX8fyQ5 zjbS+6)}tBifPSBMWSUPQARX*^=7q_+4-Wk8EUKbOu+qpgN+@eA)AqQfoS%=Aad<0%t*$IcmH~2HWLMbO;(p% z@Ik1x&hn+PalslUP9V0V9X2>@zg34VJ19KDqH9#+^IeeE*|f)sTOt1PJZ1Qr3Bn$( z(K}6!R`9qy2d`8ZI#4jb^YB9IP69?JX`Hi6yX1!jVno*uCc}S;yuE4s{4W(ecleT6 z_(c*7fK|P5XVxRn69+n%vl*}hv_ZYsh0`U#A89@DyhL`XIv|w8=!XiM9Edi}%g^!= zzHW*qdRf=9{L!>X^3`=+=m>5dKKSA1k{jmqeDF_Qk(k#;4`LsP?QujMU;lMQm%@P; z6Z??iue=3o>AQ}7j3>R`N$D{8GO=*c9nsUd4*2YXJZ$=GOG z+PZHW-IvDMtpBWI*AzTDnkJkJ%im%@0UI@j^{=sUNaWZHaq60Y1h<+L?1z;riIYp6yRT^j?U+a$~(G3q`?t zG_!w6l7XN_pN>Zi78ZNrZT9?0m(n5AOkUW~=wd{L@+;=$&v5{s#CYQ)?;hnvV!H3G zW>ldn>dpyT{09D|KTqhJCM*y%fJbD~k%iH% zmPhEk{7R5ALfZdHD1;H5?bP0gBVL;wk#*1D>jOSerGEGSy!(EcFD}$2y}EvcI7F@1 z-OOsyM(F6zZddg#m}O(;c_QK6G-&xAd=dU3%mm{2uf4UNq&8cm^ZMXfqxW{LFIFdyaNJ<&tiV2tZZ|JiNfCVeMUziDnxN)flgHVmyd zwoPmSlOr+hF6RHl-B8EuNh<3#qBfL^^S$%p$X?8cZi~|GiQV!#Y&j=M3)Sz7iD(BI{wN~JFCkt0c(2pwYVQ?VsH0kdNLKHW{+hi&eD|2)D4PmA(>bAC7~W1En1 zgCt_sZZU_%)_c>ADAYkVK3H6&gDeqK6fJne3-iSKKI&EOHK%Y)I2fO40RZ%-HWvIw z$m6tThdpVh{LtwB-`lwn1YEwGp>K(@sUSr1xZMp zDz-IrL?TBYZZq}}ADcm$=q=QM3ZH-BCVt3r^VwOCI8|^(SGv_4#JtfD$9)e-wn~6F z*SjS_^S~q>C!SDI$|CGq7y_9(e{S1|XjbMzPK!j5%69gH`@?BJ^yupwmy$GST))wU z^uiTf(og>U-ToeNat8ilw6DQ zL#-;_zporNf(qz@f^BjuECArm5h_{emp5{|6+DQZrY*PfpYEm{`F- zKH{20O(BvD-N5e{2Tw-R;1E^3zH`7P!VgOo#DBBiN1RQ{O722jq$8qsw`)|clI+C* z%#x~nNTT6Urn|u|4IPU1J0RF@`Vy0(>nP6O_l6BMQI z%TDHannWJkdISC@kH2pwifW0(L<;6x27nQ@KI46a^@k@eTv*-NVo7vjFeF&yR+8Az zVqh1&?=c8^aeMjKxYdU-ix$!wNy_S`pNA~(`QQsTzI;EH3)@?>Uq>~f2K2iN z$8TsJgS4LEO_k`IDR=bJro&xzoHPN-lZulwPjlFygSKqr^~a?D1SurNnYqq$-~&G( zQ5X?STxo@A!}t35JkiDPL)eL?9p!5<1GY=vBCiH!_gz`r!7B}x zx^Zp7ysN^707cBldMZP6EQ6RV%R^}L2`d@dHr`D*mleHnbg=&50t;JHFhwV_kY0~H zjk*&4D7rqfx7zF7dGgYW%|`{#_~NjukAFN-p1(-Vt9Vl%{uE%EI7a!kS0YgdsMxQ& zxPf;Mo^#ByzHGS_$eZMQOV(3c>9{QT!x23t=!$>5d+5zA4A-sd52*W$|#Y2oPt zyZ(ehnVtX%Eb-Wv(&xkaEQz){wR`aHdRrnseXdZ69$_YTSu_7K}is*KXmXc z{0iEyUnk{xi7RQ$TW?-$28R6Ih~nF|LTrh|hv>SnkPEWu9+sbF09q||L-<98okrO3 zp(uIU9-%pUzMEcvR>vbYDC}P-e z@()7orzci|c{y~X`e}zdkgp*W50=`nqd(nI%$WG%fnoxD66W~5_3D@{;^;CTnb0L+ zUP?tV8u1RLV{_w?%l+HnjFJ`1j&&dfbk2+6aqj|gQ|BDW6xx$+54{uIwPtZ7Np4ET zX({r9o7MoLv~XT_2fK-am%9%%k9pyY`iY2fWsuazjm>Q3%KY6F*r3_Zc#5c4rj^xN zMmg-!oR)Xk+FMdQA2rS~YIbG=87Ch~d2{@r1&mJ17E=ki-#|lhcenQLHX%YF64Xt8 z{>~n+{#S0~rUfh@^_KtSda*OOW!86mzb>W#fhRAQ?3B;xidL6wCH@)1K(PL~bt z_~FXcXQ!I@Bw$gT?!3`bowq|2ubX2<*Wu-P?w2h&TI-1CRPs^^97!*BQ|60}ZwlLb z;EQ3UH5FAcANE5t?aF+Z2Lf{U(UTuzr%iC3 z&><%_x)(w@Pv*%;)ni3 zm=b(mtkt=U7m{{ZGsb-XgJsA(-J9C=f8BsK{KCP90{%efFJ_*%2V7 zE{l4baUpphuDsRZ8+eik;xoGu9!+9jf^*xty|_LAx?&a`kG{Ri0*I<}7byh%zo`tYp@IoN zz?Z^s;TCJGI6yaY`N~Wb*i>Z23+;=|L?oI$#v7FW#R+={mu+(sgnV#rSn_;%l?zIL z*rH*w0+L(GkY`-a!y}f+At{;jRW~8~aM`)FqfpZy?XH<$^3NtF0x>-@Jtb;jh{|C^ z$%!_&jHjEWe)51b+(*^8EmsbMMshq_oay2W4_-Z^UESvBd5$c)ThBm#&KD{ACn7u5 zdTz3;(0#Wmn0qYsAnpGDY5l(N`Y^d6M=#yG|{)QMHOX=z(UHDke4v+C4ci%E7kaO= zDzU!=Z_xPXvTkcq(jUx5e)Fe*6ppKGB`(Jc|r$mU! zPObOMa2TCt5)R%kv%#sGOa$J3}!q$E*&+V0)%#!$5WKDYPr^G;u^6?P|UiHGPV zmzxOwxG-dk99l-}dOV5os(-YfyPr$4!;HN=)?eoVW=xC8HxHgM$E@PVQ`njbcDQrG zbF3WHrOIKsJj%p;kDQ?8U4yv}2!GH#p3_LW#z)z=Nmw}1(*bw4U*|gC4#`8vF3&Db z5Uu{42_1aradMiRQ<)RR{MZ-|eeJz6q#PMfd2hje3caJ@<$>p~otR+h*JMdKUq!qy z*>3hvl@zIhpJHjS;|pg3TAC{Da@D1V?Py_XQ&7P8Jn5K5k>pi> z${eT6@-Gn|6q|ElOsyQU|GWX()Uyyruq|XLQ>Y`fySRVwcearn{&k*Dj`=KfL= zdSCk@j%;H6E$Uv8-tMAIdbfIIr1>FlA=43FEePl8!a@O8dh`1inE_Fl8Ys*ySW_|L>1MY@c4fpjoAMq8eJ409JLV+3&1vC zix_y#2v6B5wSj%Frhy7z{jXK#2!RJDIQi3Dpq22%HnVD$rx3sM^*ANt|Iu;lna}se z^@t9duq$x`Nr`4S0CCUP?k9??Ve4;m%~Tsa#@(#$9RTq!P2tDXMiyIiUGda2H(t*o zYMlGx(YL)4nCy1y%j3slFv*p6$ncbAwFv6lT<8(MRTNx2gsMBDUwJ};XIOL$6Rf^lb(?H+lDAjjM_nF|3yIb!jy8(wrQGBw0 zZf~VGDxE)evXBGdhgDX~RgESyROf!GJ_o#^DBL-3%jNiJcxwAgwSOiMBjwBk{#Xf{ z;#8B@tk;y`w&_m3qqor;AXoNr+!aLJu#*j4`7WG(NVujc%60;ln!dm7g{Fuew(97* zHXQ>hIKF|ie*71GK|4AmKWPLQfW&R-*WKYhXok`5o%jSH#3(;*#CEqAoJsF(=u>Sa zCMGORyxprX=!Z*hj|NBGhgG8{)|?IaSKx*Ue+PH9(gJ6H|E+g7LZ#0Flh(Ju z&X(K>nbGCSZ6E%^cmWrNFOK)`8zAZ(TGl2LIRHLf;u*_sX1k*h$!pA)u0hQZtawSO zeGiPflF;5`7rG5XCi(Ny%I0P#qH>52>hzB$Xn^1>53XjJJzR_6+=$f(BOqY7=l z_}O1mha7d7Jo9#my#~R7Z72Q>Ysyyd`W%&tT4?HN>U~p=TC{9=#Ez9mnnrVGVS_PnitUy>PCfw5|!sm&}{)NV@WQeolewh{F z!44?wU4MyjI%qDK0vXOJ&DxitWAW$_U^c@myUd6YwhsR}QDsNG z+wl^v-sx0tWWK;9bX5j0p`r5L_m0832sQq_qj8sXqnRR5w53e;IvI~@hrT?U4pCq# zUZh-k!~-d26*~W`1~*dj0d+bb_+!4U9+%fQ!B67jO4`C0goNS7nJyAX2Q+u_u^^muQddPPn02SL=sonG|p%r%am|->elj!@?W(<#f{t zlDl_APV@jIYJLAAu3LT}!Kh{QBlL^-$_6|!ZY6ZC0QMEWEcyG{>~>tNI=3p(;z9(D-b3}8X!1rHgvDmN6^eCYYjPmrQV#x2+USpaE8rpx5XD3YEeWon_W ztg6upg(-gckz+*!s*Y!8J5JKA@Q#9noCPw5-mNDYVF^fM#QK@VA{RZe zs#pBa$9#~3yL?Z&EV2x;xIYY?Ntn>4UQJn9kxsP3YHAnuK2afn3c1(x;+YE;=ta@% z7su6!0ywGZqY(cYS4{I1lwMB+IVG2EN}zr_4WY^RX6u=Yzr-nVDNa%{h#MP9{7_R@ zgC}bkCW_@^_Pt?=1v$}~ zpQT;OJ@z=A=U`5s%16s}w5W?@JSN@5+Y;A1Y(L!%K+=|2*Hcr6zt z!=~{;rt~~9b7n$KS?MNf!gmb(yV>0RHnx%M?I<=Eem&W7k%o@o1$%cJfEO7u3Y_66;TrS6M&;k9DgE5h57WeHc))sA zthWW+im8L4qR-f2_Bq2EM{Sww5ESR~m&L`*-qNvD#_$6_Q6e3sE??5T`_&uK-+0^C z93X+^6Dl1{VKU%Hdz{$CyZ^yghu=Rd)V0?Tzg}-^#8zzCs>IUs43ZOcU z?B#iK|2^Q$Kbdwr87YNg{j+}dveSJd4Dn_wS9rIj%U^5 zq>GgrE`54?wlfkGZ4S9Q?*<+iW2@OM_uZ8}b*XXP?_689F6%~)be&Z1C)SY3tmvRo ztq-nIap7^bCiQ~R;XJMLDzhD8c7A^%!Gegsm0#K~Rh{xgXkXmnv2c=)F4g>~X{7Qm zKQt{?z0CgB2#S^R&+lv$1L;WT{CBn6fh1iDwQ~B-`J+jD@c3Fu%6?%^&`r;2#e9;_ zH^s|o96s;536BA0mT;$+1-_`w`@e+w&v18X2`DRv~o?+u7)@!v8#};(yQ(E+;%lZ?LRwL3Rqy(R_Ax}>K!t`ZN<(b;*PLt z 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

oFv%fCnc?lzJ&hulxX0?Gi%xp%H2b&sO-k?)7 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 DZ9PVs literal 33115 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 NTSQt~!S12<{|DRqmec?M diff --git a/c/sedona-libgpuspatial/libgpuspatial/test/data/gen_points.py b/c/sedona-libgpuspatial/libgpuspatial/test/data/gen_points.py index a02f4a094..b23a89ebc 100644 --- a/c/sedona-libgpuspatial/libgpuspatial/test/data/gen_points.py +++ b/c/sedona-libgpuspatial/libgpuspatial/test/data/gen_points.py @@ -47,7 +47,7 @@ def calculate_bbox_and_generate_points(geoparquet_path, n_points, output_path): # Generate random coordinates random_x = np.random.uniform(minx, maxx, n_points) - random_y = np.random.uniform(miny, miny, n_points) + random_y = np.random.uniform(miny, maxy, n_points) # 4. Create a GeoDataFrame from the points diff --git a/c/sedona-libgpuspatial/libgpuspatial/test/index_test.cu b/c/sedona-libgpuspatial/libgpuspatial/test/index_test.cu new file mode 100644 index 000000000..42f5769e2 --- /dev/null +++ b/c/sedona-libgpuspatial/libgpuspatial/test/index_test.cu @@ -0,0 +1,300 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +#include "array_stream.hpp" +#include "gpuspatial/index/rt_spatial_index.cuh" +#include "test_common.hpp" + +#include +#include +#include + +#include +#include +#include // For std::iota +#include +#include + +namespace gpuspatial { +template +struct SpatialIndexTest : public ::testing::Test { + using index_t = RTSpatialIndex; + std::shared_ptr rt_engine; + index_t index; + + SpatialIndexTest() { + auto ptx_root = TestUtils::GetTestShaderPath(); + + rt_engine = std::make_shared(); + rt_engine->Init(get_default_rt_config(ptx_root)); + RTSpatialIndexConfig config; + config.rt_engine = rt_engine; + index = std::move(index_t(config)); + } +}; +using PointTypes = ::testing::Types, Point>; +TYPED_TEST_SUITE(SpatialIndexTest, PointTypes); + +template +std::vector> GeneratePoints(size_t n, std::mt19937& rng) { + using scalar_t = typename POINT_T::scalar_t; + std::vector> rects(n); + + for (size_t i = 0; i < n; i++) { + POINT_T p; + for (int dim = 0; dim < POINT_T::n_dim; dim++) { + std::uniform_real_distribution dist(-180.0, 180.0); + p.set_coordinate(dim, dist(rng)); + } + rects[i] = Box(p, p); + } + return rects; +} + +template +std::vector> GenerateRects(size_t n, std::mt19937& rng) { + using scalar_t = typename POINT_T::scalar_t; + std::vector> rects(n); + std::uniform_real_distribution distSize(0.0, 100); + + for (size_t i = 0; i < n; ++i) { + POINT_T min_pt, max_pt, size_pt; + + for (int dim = 0; dim < POINT_T::n_dim; dim++) { + std::uniform_real_distribution dist(-180.0, 180.0); + min_pt.set_coordinate(dim, dist(rng)); + size_pt.set_coordinate(dim, distSize(rng)); + } + max_pt = min_pt + size_pt; + rects[i] = Box(min_pt, max_pt); + } + return rects; +} + +template +void ComputeReference(const std::vector>& build, + const std::vector>& probe, + std::vector& build_indices, + std::vector& probe_indices) { + geos::index::strtree::STRtree tree; + + // FIX: Create a storage container for envelopes that persists + // for the lifetime of the tree usage. + std::vector build_envelopes; + build_envelopes.reserve(build.size()); + + // 2. Build Phase + for (uint32_t j = 0; j < build.size(); j++) { + auto min_corner = build[j].get_min(); + auto max_corner = build[j].get_max(); + + // Emplace the envelope into our persistent vector + build_envelopes.emplace_back(min_corner.x(), max_corner.x(), min_corner.y(), + max_corner.y()); + + // Pass the address of the element inside the vector + // Note: We reserved memory above, so pointers shouldn't be invalidated by resizing + tree.insert(&build_envelopes.back(), + reinterpret_cast(static_cast(j))); + } + + tree.build(); + + // 3. Define Visitor (No changes needed here) + class InteractionVisitor : public geos::index::ItemVisitor { + public: + const std::vector>* build; + const std::vector>* probe; + std::vector* b_indices; + std::vector* p_indices; + uint32_t current_probe_idx; + + void visitItem(void* item) override { + uintptr_t build_idx_ptr = reinterpret_cast(item); + uint32_t build_idx = static_cast(build_idx_ptr); + + // Refinement step + if ((*build)[build_idx].intersects((*probe)[current_probe_idx])) { + b_indices->push_back(build_idx); + p_indices->push_back(current_probe_idx); + } + } + }; + + InteractionVisitor visitor; + visitor.build = &build; + visitor.probe = &probe; + visitor.b_indices = &build_indices; + visitor.p_indices = &probe_indices; + + // 4. Probe Phase + for (uint32_t i = 0; i < probe.size(); i++) { + auto min_corner = probe[i].get_min(); + auto max_corner = probe[i].get_max(); + + // It is safe to create this on the stack here because `query` + // finishes executing before `search_env` goes out of scope. + geos::geom::Envelope search_env(min_corner.x(), max_corner.x(), min_corner.y(), + max_corner.y()); + + visitor.current_probe_idx = i; + tree.query(&search_env, visitor); + } +} + +template +void sort_vectors(std::vector& v1, std::vector& v2) { + if (v1.size() != v2.size()) return; + + // 1. Create indices [0, 1, 2, ..., N-1] + std::vector p(v1.size()); + std::iota(p.begin(), p.end(), 0); + + // 2. Sort indices based on comparing values in v1 and v2 + std::sort(p.begin(), p.end(), [&](size_t i, size_t j) { + if (v1[i] != v1[j]) return v1[i] < v1[j]; // Primary sort by v1 + return v2[i] < v2[j]; // Secondary sort by v2 + }); + + // 3. Apply permutation (Reorder v1 and v2 based on sorted indices) + // Note: Doing this in-place with O(1) space is complex; + // using auxiliary O(N) space is standard. + std::vector sorted_v1, sorted_v2; + sorted_v1.reserve(v1.size()); + sorted_v2.reserve(v2.size()); + + for (size_t i : p) { + sorted_v1.push_back(v1[i]); + sorted_v2.push_back(v2[i]); + } + + v1 = std::move(sorted_v1); + v2 = std::move(sorted_v2); +} + +TYPED_TEST(SpatialIndexTest, PointPoint) { + using point_t = TypeParam; + std::mt19937 gen(0); + + for (int i = 1; i <= 10000; i *= 2) { + auto points1 = GeneratePoints(i, gen); + this->index.Clear(); + this->index.PushBuild(points1.data(), points1.size()); + this->index.FinishBuilding(); + + for (int j = 1; j <= 10000; j *= 2) { + auto points2 = GeneratePoints(j, gen); + + size_t count = static_cast(points1.size() * 0.2); + + // 2. Define the starting point (the last 'count' elements) + auto start_it = points1.end() - count; + + // 3. Append to the second vector + points2.insert(points2.end(), start_it, points1.end()); + + std::vector build_indices, probe_indices; + this->index.Probe(points2.data(), points2.size(), &build_indices, &probe_indices); + sort_vectors(build_indices, probe_indices); + + std::vector ref_build_indices, ref_probe_indices; + ComputeReference(points1, points2, ref_build_indices, ref_probe_indices); + sort_vectors(ref_build_indices, ref_probe_indices); + + ASSERT_EQ(build_indices, ref_build_indices); + ASSERT_EQ(probe_indices, ref_probe_indices); + } + } +} + +TYPED_TEST(SpatialIndexTest, BoxPoint) { + using point_t = TypeParam; + std::mt19937 gen(0); + + for (int i = 1; i <= 10000; i *= 2) { + auto rects1 = GenerateRects(i, gen); + this->index.Clear(); + this->index.PushBuild(rects1.data(), rects1.size()); + this->index.FinishBuilding(); + + for (int j = 1; j <= 10000; j *= 2) { + auto points2 = GeneratePoints(j, gen); + std::vector build_indices, probe_indices; + this->index.Probe(points2.data(), points2.size(), &build_indices, &probe_indices); + sort_vectors(build_indices, probe_indices); + + std::vector ref_build_indices, ref_probe_indices; + ComputeReference(rects1, points2, ref_build_indices, ref_probe_indices); + sort_vectors(ref_build_indices, ref_probe_indices); + + ASSERT_EQ(build_indices, ref_build_indices); + ASSERT_EQ(probe_indices, ref_probe_indices); + } + } +} + +TYPED_TEST(SpatialIndexTest, PointBox) { + using point_t = TypeParam; + std::mt19937 gen(0); + + for (int i = 1; i <= 10000; i *= 2) { + auto points1 = GeneratePoints(i, gen); + this->index.Clear(); + this->index.PushBuild(points1.data(), points1.size()); + this->index.FinishBuilding(); + + for (int j = 1; j <= 10000; j *= 2) { + auto rects2 = GenerateRects(j, gen); + std::vector build_indices, probe_indices; + this->index.Probe(rects2.data(), rects2.size(), &build_indices, &probe_indices); + sort_vectors(build_indices, probe_indices); + + std::vector ref_build_indices, ref_probe_indices; + ComputeReference(points1, rects2, ref_build_indices, ref_probe_indices); + sort_vectors(ref_build_indices, ref_probe_indices); + + ASSERT_EQ(build_indices, ref_build_indices); + ASSERT_EQ(probe_indices, ref_probe_indices); + } + } +} + +TYPED_TEST(SpatialIndexTest, BoxBox) { + using point_t = TypeParam; + std::mt19937 gen(0); + + for (int i = 1; i <= 10000; i *= 2) { + auto rects1 = GenerateRects(i, gen); + this->index.Clear(); + this->index.PushBuild(rects1.data(), rects1.size()); + this->index.FinishBuilding(); + + for (int j = 1; j <= 10000; j *= 2) { + auto rects2 = GenerateRects(j, gen); + std::vector build_indices, probe_indices; + this->index.Probe(rects2.data(), rects2.size(), &build_indices, &probe_indices); + sort_vectors(build_indices, probe_indices); + + std::vector ref_build_indices, ref_probe_indices; + ComputeReference(rects1, rects2, ref_build_indices, ref_probe_indices); + sort_vectors(ref_build_indices, ref_probe_indices); + + ASSERT_EQ(build_indices, ref_build_indices); + ASSERT_EQ(probe_indices, ref_probe_indices); + } + } +} +} // namespace gpuspatial diff --git a/c/sedona-libgpuspatial/libgpuspatial/test/joiner_test.cu b/c/sedona-libgpuspatial/libgpuspatial/test/joiner_test.cu deleted file mode 100644 index bbf415592..000000000 --- a/c/sedona-libgpuspatial/libgpuspatial/test/joiner_test.cu +++ /dev/null @@ -1,438 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. -#include "array_stream.hpp" -#include "gpuspatial/index/spatial_joiner.cuh" -#include "gpuspatial/loader/device_geometries.cuh" -#include "test_common.hpp" - -#include "geoarrow_geos/geoarrow_geos.hpp" -#include "nanoarrow/nanoarrow.hpp" - -#include -#include -#include -#include // For std::iota - -namespace gpuspatial { -// Function to read a single Parquet file and extract a column. -static arrow::Status ReadParquetFromFile( - arrow::fs::FileSystem* fs, // 1. Filesystem pointer (e.g., LocalFileSystem) - const std::string& file_path, // 2. Single file path instead of a folder - int64_t batch_size, const char* column_name, - std::vector>& out_arrays) { - // 1. Get FileInfo for the single path - ARROW_ASSIGN_OR_RAISE(auto file_info, fs->GetFileInfo(file_path)); - - // Check if the path points to a file - if (file_info.type() != arrow::fs::FileType::File) { - return arrow::Status::Invalid("Path is not a file: ", file_path); - } - - std::cout << "--- Processing Parquet file: " << file_path << " ---" << std::endl; - - // 2. Open the input file - ARROW_ASSIGN_OR_RAISE(auto input_file, fs->OpenInputFile(file_info)); - - // 3. Open the Parquet file and create an Arrow reader - ARROW_ASSIGN_OR_RAISE(auto arrow_reader, parquet::arrow::OpenFile( - input_file, arrow::default_memory_pool())); - - // 4. Set the batch size - arrow_reader->set_batch_size(batch_size); - - // 5. Get the RecordBatchReader - auto rb_reader = arrow_reader->GetRecordBatchReader().ValueOrDie(); - // 6. Read all record batches and extract the column - while (true) { - std::shared_ptr batch; - - // Read the next batch - ARROW_THROW_NOT_OK(rb_reader->ReadNext(&batch)); - - // Check for end of stream - if (!batch) { - break; - } - - // Extract the specified column and add to the output vector - std::shared_ptr column_array = batch->GetColumnByName(column_name); - if (!column_array) { - return arrow::Status::Invalid("Column not found: ", column_name); - } - out_arrays.push_back(column_array); - } - - std::cout << "Finished reading. Total arrays extracted: " << out_arrays.size() - << std::endl; - return arrow::Status::OK(); -} - -using GeosBinaryPredicateFn = char (*)(GEOSContextHandle_t, const GEOSGeometry*, - const GEOSGeometry*); -static GeosBinaryPredicateFn GetGeosPredicateFn(Predicate predicate) { - switch (predicate) { - case Predicate::kContains: - return &GEOSContains_r; - case Predicate::kIntersects: - return &GEOSIntersects_r; - case Predicate::kWithin: - return &GEOSWithin_r; - case Predicate::kEquals: - return &GEOSEquals_r; - case Predicate::kTouches: - return &GEOSTouches_r; - default: - throw std::out_of_range("Unsupported GEOS predicate enumeration value."); - } -} - -void TestJoiner(const std::string& build_parquet_path, - const std::string& stream_parquet_path, Predicate predicate, - int batch_size = 10) { - using namespace TestUtils; - auto fs = std::make_shared(); - SpatialJoiner::SpatialJoinerConfig config; - std::string ptx_root = TestUtils::GetTestShaderPath(); - - config.ptx_root = ptx_root.c_str(); - SpatialJoiner spatial_joiner; - - spatial_joiner.Init(&config); - spatial_joiner.Clear(); - - geoarrow::geos::ArrayReader reader; - - class GEOSCppHandle { - public: - GEOSContextHandle_t handle; - - GEOSCppHandle() { handle = GEOS_init_r(); } - - ~GEOSCppHandle() { GEOS_finish_r(handle); } - }; - GEOSCppHandle handle; - - reader.InitFromEncoding(handle.handle, GEOARROW_GEOS_ENCODING_WKB); - - geoarrow::geos::GeometryVector geom_build(handle.handle); - - auto get_total_length = [](const std::vector>& arrays) { - size_t total_length = 0; - for (const auto& array : arrays) { - total_length += array->length(); - } - return total_length; - }; - - std::vector> build_arrays; - ARROW_THROW_NOT_OK(ReadParquetFromFile(fs.get(), build_parquet_path, batch_size, - "geometry", build_arrays)); - - // Using GEOS for reference - geom_build.resize(get_total_length(build_arrays)); - size_t tail_build = 0; - auto* tree = GEOSSTRtree_create_r(handle.handle, 10); - - for (auto& array : build_arrays) { - nanoarrow::UniqueArray unique_array; - nanoarrow::UniqueSchema unique_schema; - - ARROW_THROW_NOT_OK( - arrow::ExportArray(*array, unique_array.get(), unique_schema.get())); - - spatial_joiner.PushBuild(unique_schema.get(), unique_array.get(), 0, - unique_array->length); - - // geos for reference - size_t n_build; - - ASSERT_EQ(reader.Read(unique_array.get(), 0, unique_array->length, - geom_build.mutable_data() + tail_build, &n_build), - GEOARROW_GEOS_OK); - - for (size_t offset = tail_build; offset < tail_build + n_build; offset++) { - auto* geom = geom_build.borrow(offset); - auto* box = GEOSEnvelope_r(handle.handle, geom); - GEOSGeom_setUserData_r(handle.handle, (GEOSGeometry*)geom, (void*)offset); - GEOSSTRtree_insert_r(handle.handle, tree, box, (void*)geom); - GEOSGeom_destroy_r(handle.handle, box); - } - tail_build += n_build; - } - spatial_joiner.FinishBuilding(); - ASSERT_EQ(GEOSSTRtree_build_r(handle.handle, tree), 1); - - std::vector> stream_arrays; - ARROW_THROW_NOT_OK(ReadParquetFromFile( - fs.get(), stream_parquet_path, batch_size, "geometry", stream_arrays)); - int array_index_offset = 0; - auto context = spatial_joiner.CreateContext(); - - for (auto& array : stream_arrays) { - nanoarrow::UniqueArray unique_array; - nanoarrow::UniqueSchema unique_schema; - - ARROW_THROW_NOT_OK( - arrow::ExportArray(*array, unique_array.get(), unique_schema.get())); - std::vector build_indices, stream_indices; - - spatial_joiner.PushStream(context.get(), unique_schema.get(), unique_array.get(), 0, - unique_array->length, predicate, &build_indices, - &stream_indices, array_index_offset); - - geoarrow::geos::GeometryVector geom_stream(handle.handle); - size_t n_stream; - geom_stream.resize(array->length()); - ASSERT_EQ(reader.Read(unique_array.get(), 0, unique_array->length, - geom_stream.mutable_data(), &n_stream), - GEOARROW_GEOS_OK); - struct Payload { - GEOSContextHandle_t handle; - const GEOSGeometry* geom; - int64_t stream_index_offset; - std::vector build_indices; - std::vector stream_indices; - Predicate predicate; - }; - - Payload payload; - payload.predicate = predicate; - payload.handle = handle.handle; - - payload.stream_index_offset = array_index_offset; - - for (size_t offset = 0; offset < n_stream; offset++) { - auto* geom = geom_stream.borrow(offset); - GEOSGeom_setUserData_r(handle.handle, (GEOSGeometry*)geom, (void*)offset); - payload.geom = geom; - - GEOSSTRtree_query_r( - handle.handle, tree, geom, - [](void* item, void* data) { - auto* geom_build = (GEOSGeometry*)item; - auto* payload = (Payload*)data; - auto* geom_stream = payload->geom; - - if (GetGeosPredicateFn(payload->predicate)(payload->handle, geom_build, - geom_stream) == 1) { - auto build_id = (size_t)GEOSGeom_getUserData_r(payload->handle, geom_build); - auto stream_id = - (size_t)GEOSGeom_getUserData_r(payload->handle, geom_stream); - payload->build_indices.push_back(build_id); - payload->stream_indices.push_back(payload->stream_index_offset + stream_id); - } - }, - (void*)&payload); - } - - ASSERT_EQ(payload.build_indices.size(), build_indices.size()); - ASSERT_EQ(payload.stream_indices.size(), stream_indices.size()); - sort_vectors_by_index(payload.build_indices, payload.stream_indices); - sort_vectors_by_index(build_indices, stream_indices); - for (size_t j = 0; j < build_indices.size(); j++) { - ASSERT_EQ(payload.build_indices[j], build_indices[j]); - ASSERT_EQ(payload.stream_indices[j], stream_indices[j]); - } - array_index_offset += array->length(); - } - GEOSSTRtree_destroy_r(handle.handle, tree); -} - -TEST(JoinerTest, PIPContainsParquet) { - using namespace TestUtils; - auto fs = std::make_shared(); - - std::vector polys{ - GetTestDataPath("cities/natural-earth_cities_geo.parquet"), - GetTestDataPath("countries/natural-earth_countries_geo.parquet")}; - std::vector points{GetTestDataPath("cities/generated_points.parquet"), - GetTestDataPath("countries/generated_points.parquet")}; - - for (int i = 0; i < polys.size(); i++) { - auto poly_path = TestUtils::GetTestDataPath(polys[i]); - auto point_path = TestUtils::GetCanonicalPath(points[i]); - TestJoiner(poly_path, point_path, Predicate::kContains, 10); - } -} - -TEST(JoinerTest, PIPWithinParquet) { - using namespace TestUtils; - auto fs = std::make_shared(); - - std::vector polys{ - GetTestDataPath("cities/natural-earth_cities_geo.parquet"), - GetTestDataPath("countries/natural-earth_countries_geo.parquet")}; - std::vector points{GetTestDataPath("cities/generated_points.parquet"), - GetTestDataPath("countries/generated_points.parquet")}; - - for (int i = 0; i < polys.size(); i++) { - auto poly_path = TestUtils::GetTestDataPath(polys[i]); - auto point_path = TestUtils::GetCanonicalPath(points[i]); - TestJoiner(point_path, poly_path, Predicate::kWithin, 10); - } -} - -TEST(JoinerTest, PolyPointIntersectsParquet) { - using namespace TestUtils; - auto fs = std::make_shared(); - - std::vector polys{ - GetTestDataPath("cities/natural-earth_cities_geo.parquet"), - GetTestDataPath("countries/natural-earth_countries_geo.parquet")}; - std::vector points{GetTestDataPath("cities/generated_points.parquet"), - GetTestDataPath("countries/generated_points.parquet")}; - - for (int i = 0; i < polys.size(); i++) { - auto poly_path = TestUtils::GetTestDataPath(polys[i]); - auto point_path = TestUtils::GetCanonicalPath(points[i]); - TestJoiner(point_path, poly_path, Predicate::kIntersects, 10); - } -} - -TEST(JoinerTest, PolygonPolygonContains) { - SpatialJoiner::SpatialJoinerConfig config; - std::string ptx_root = TestUtils::GetTestShaderPath(); - config.ptx_root = ptx_root.c_str(); - SpatialJoiner spatial_joiner; - - nanoarrow::UniqueArrayStream poly1_stream, poly2_stream; - - auto poly1_path = TestUtils::GetTestDataPath("arrowipc/test_polygons1.arrows"); - auto poly2_path = TestUtils::GetTestDataPath("arrowipc/test_polygons2.arrows"); - - ArrayStreamFromIpc(poly1_path, "geometry", poly1_stream.get()); - ArrayStreamFromIpc(poly2_path, "geometry", poly2_stream.get()); - - nanoarrow::UniqueSchema build_schema, stream_schema; - nanoarrow::UniqueArray build_array, stream_array; - ArrowError error; - ArrowErrorSet(&error, ""); - int n_row_groups = 100; - int array_index_offset = 0; - std::vector build_indices, stream_indices; - geoarrow::geos::ArrayReader reader; - - class GEOSCppHandle { - public: - GEOSContextHandle_t handle; - - GEOSCppHandle() { handle = GEOS_init_r(); } - - ~GEOSCppHandle() { GEOS_finish_r(handle); } - }; - GEOSCppHandle handle; - - reader.InitFromEncoding(handle.handle, GEOARROW_GEOS_ENCODING_WKB); - - geoarrow::geos::GeometryVector geom_polygons1(handle.handle); - geoarrow::geos::GeometryVector geom_polygons2(handle.handle); - struct Payload { - GEOSContextHandle_t handle; - const GEOSGeometry* geom; - int64_t build_index_offset; - int64_t stream_index_offset; - std::vector build_indices; - std::vector stream_indices; - }; - - int64_t build_count = 0; - spatial_joiner.Init(&config); - for (int i = 0; i < n_row_groups; i++) { - ASSERT_EQ(ArrowArrayStreamGetNext(poly1_stream.get(), build_array.get(), &error), - NANOARROW_OK); - ASSERT_EQ(ArrowArrayStreamGetSchema(poly1_stream.get(), build_schema.get(), &error), - NANOARROW_OK); - - ASSERT_EQ(ArrowArrayStreamGetNext(poly2_stream.get(), stream_array.get(), &error), - NANOARROW_OK); - ASSERT_EQ(ArrowArrayStreamGetSchema(poly2_stream.get(), stream_schema.get(), &error), - NANOARROW_OK); - - spatial_joiner.Clear(); - spatial_joiner.PushBuild(nullptr, build_array.get(), 0, build_array->length); - auto context = spatial_joiner.CreateContext(); - - build_indices.clear(); - stream_indices.clear(); - spatial_joiner.FinishBuilding(); - spatial_joiner.PushStream(context.get(), nullptr, stream_array.get(), 0, - stream_array->length, Predicate::kContains, &build_indices, - &stream_indices, array_index_offset); - geom_polygons1.resize(build_array->length); - geom_polygons2.resize(stream_array->length); - - size_t n_polygons1 = 0, n_polygons2 = 0; - ASSERT_EQ(reader.Read(build_array.get(), 0, build_array->length, - geom_polygons1.mutable_data(), &n_polygons1), - GEOARROW_GEOS_OK); - ASSERT_EQ(reader.Read(stream_array.get(), 0, stream_array->length, - geom_polygons2.mutable_data(), &n_polygons2), - GEOARROW_GEOS_OK); - - auto* tree = GEOSSTRtree_create_r(handle.handle, 10); - - for (size_t j = 0; j < n_polygons1; j++) { - auto* geom_polygon = geom_polygons1.borrow(j); - auto* box = GEOSEnvelope_r(handle.handle, geom_polygon); - GEOSGeom_setUserData_r(handle.handle, (GEOSGeometry*)geom_polygon, (void*)j); - GEOSSTRtree_insert_r(handle.handle, tree, box, (void*)geom_polygon); - GEOSGeom_destroy_r(handle.handle, box); - } - ASSERT_EQ(GEOSSTRtree_build_r(handle.handle, tree), 1); - - Payload payload; - payload.handle = handle.handle; - - payload.build_index_offset = build_count; - payload.stream_index_offset = array_index_offset; - - for (size_t j = 0; j < n_polygons2; j++) { - auto* geom_poly2 = geom_polygons2.borrow(j); - GEOSGeom_setUserData_r(handle.handle, (GEOSGeometry*)geom_poly2, (void*)j); - - payload.geom = geom_poly2; - - GEOSSTRtree_query_r( - handle.handle, tree, geom_poly2, - [](void* item, void* data) { - auto* polygon1 = (GEOSGeometry*)item; - auto* payload = (Payload*)data; - auto* polygon2 = payload->geom; - - if (GEOSContains_r(payload->handle, polygon1, polygon2) == 1) { - auto polygon1_id = - (size_t)GEOSGeom_getUserData_r(payload->handle, polygon1); - auto polygon2_id = - (size_t)GEOSGeom_getUserData_r(payload->handle, polygon2); - payload->build_indices.push_back(payload->build_index_offset + polygon1_id); - payload->stream_indices.push_back(payload->stream_index_offset + - polygon2_id); - } - }, - (void*)&payload); - } - - GEOSSTRtree_destroy_r(handle.handle, tree); - - ASSERT_EQ(payload.build_indices.size(), build_indices.size()); - - build_count += build_array->length; - array_index_offset += stream_array->length; - } -} - -} // namespace gpuspatial diff --git a/c/sedona-libgpuspatial/libgpuspatial/test/loader_test.cu b/c/sedona-libgpuspatial/libgpuspatial/test/loader_test.cu index f8a762974..bb60bad87 100644 --- a/c/sedona-libgpuspatial/libgpuspatial/test/loader_test.cu +++ b/c/sedona-libgpuspatial/libgpuspatial/test/loader_test.cu @@ -15,13 +15,13 @@ // specific language governing permissions and limitations // under the License. #include "array_stream.hpp" -#include "gpuspatial/geom/geometry_collection.cuh" -#include "gpuspatial/geom/multi_polygon.cuh" -#include "gpuspatial/loader/device_geometries.cuh" -#include "gpuspatial/utils/pinned_vector.h" +#include "gpuspatial/geom/geometry_collection.hpp" +#include "gpuspatial/geom/multi_polygon.hpp" +#include "gpuspatial/loader/device_geometries.hpp" +#include "gpuspatial/utils/pinned_vector.hpp" #include "nanoarrow/nanoarrow.hpp" -#include "gpuspatial/geom/multi_point.cuh" +#include "gpuspatial/geom/multi_point.hpp" #include "test_common.hpp" #include @@ -34,7 +34,7 @@ #include #include #include -#include "gpuspatial/loader/parallel_wkb_loader.h" +#include "gpuspatial/loader/parallel_wkb_loader.hpp" namespace gpuspatial { template @@ -45,6 +45,7 @@ TYPED_TEST(WKBLoaderTest, Point) { using point_t = typename TypeParam::first_type; using index_t = typename TypeParam::second_type; nanoarrow::UniqueArrayStream stream; + nanoarrow::UniqueSchema schema; ArrayStreamFromWKT({{"POINT (0 0)"}, {"POINT (10 20)", "POINT (-5.5 -12.3)"}, {"POINT (100 -50)", "POINT (3.1415926535 2.7182818284)", @@ -62,11 +63,14 @@ TYPED_TEST(WKBLoaderTest, Point) { nanoarrow::UniqueArray array; ArrowError error; ArrowErrorSet(&error, ""); - EXPECT_EQ(ArrowArrayStreamGetNext(stream.get(), array.get(), &error), NANOARROW_OK); + ASSERT_EQ(ArrowArrayStreamGetSchema(stream.get(), schema.get(), &error), NANOARROW_OK) + << error.message; + ASSERT_EQ(ArrowArrayStreamGetNext(stream.get(), array.get(), &error), NANOARROW_OK) + << error.message; if (array->length == 0) { break; } - loader.Parse(cuda_stream, array.get(), 0, array->length); + loader.Parse(cuda_stream, schema.get(), array.get(), 0, array->length); } auto geometries = loader.Finish(cuda_stream); @@ -103,13 +107,17 @@ TYPED_TEST(WKBLoaderTest, MultiPoint) { while (1) { nanoarrow::UniqueArray array; + nanoarrow::UniqueSchema schema; ArrowError error; ArrowErrorSet(&error, ""); - EXPECT_EQ(ArrowArrayStreamGetNext(stream.get(), array.get(), &error), NANOARROW_OK); + ASSERT_EQ(ArrowArrayStreamGetSchema(stream.get(), schema.get(), &error), NANOARROW_OK) + << error.message; + ASSERT_EQ(ArrowArrayStreamGetNext(stream.get(), array.get(), &error), NANOARROW_OK) + << error.message; if (array->length == 0) { break; } - loader.Parse(cuda_stream, array.get(), 0, array->length); + loader.Parse(cuda_stream, schema.get(), array.get(), 0, array->length); } auto geometries = loader.Finish(cuda_stream); @@ -145,6 +153,7 @@ TYPED_TEST(WKBLoaderTest, PointMultiPoint) { using point_t = typename TypeParam::first_type; using index_t = typename TypeParam::second_type; nanoarrow::UniqueArrayStream stream; + nanoarrow::UniqueSchema schema; ArrayStreamFromWKT({{"POINT (1 2)", "MULTIPOINT ((3 4), (5 6))"}, {"POINT (7 8)", "MULTIPOINT ((9 10))"}, {"MULTIPOINT EMPTY", "POINT (11 12)"}}, @@ -158,11 +167,14 @@ TYPED_TEST(WKBLoaderTest, PointMultiPoint) { nanoarrow::UniqueArray array; ArrowError error; ArrowErrorSet(&error, ""); - EXPECT_EQ(ArrowArrayStreamGetNext(stream.get(), array.get(), &error), NANOARROW_OK); + ASSERT_EQ(ArrowArrayStreamGetSchema(stream.get(), schema.get(), &error), NANOARROW_OK) + << error.message; + ASSERT_EQ(ArrowArrayStreamGetNext(stream.get(), array.get(), &error), NANOARROW_OK) + << error.message; if (array->length == 0) { break; } - loader.Parse(cuda_stream, array.get(), 0, array->length); + loader.Parse(cuda_stream, schema.get(), array.get(), 0, array->length); } auto geometries = loader.Finish(cuda_stream); @@ -207,6 +219,7 @@ TYPED_TEST(WKBLoaderTest, PolygonWKBLoaderWithHoles) { GEOARROW_TYPE_WKB, stream.get()); nanoarrow::UniqueArray array; + nanoarrow::UniqueSchema schema; ArrowError error; ArrowErrorSet(&error, ""); @@ -215,9 +228,12 @@ TYPED_TEST(WKBLoaderTest, PolygonWKBLoaderWithHoles) { loader.Init(); - ASSERT_EQ(ArrowArrayStreamGetNext(stream.get(), array.get(), &error), NANOARROW_OK); + ASSERT_EQ(ArrowArrayStreamGetSchema(stream.get(), schema.get(), &error), NANOARROW_OK) + << error.message; + ASSERT_EQ(ArrowArrayStreamGetNext(stream.get(), array.get(), &error), NANOARROW_OK) + << error.message; - loader.Parse(cuda_stream, array.get(), 0, array->length); + loader.Parse(cuda_stream, schema.get(), array.get(), 0, array->length); auto geometries = loader.Finish(cuda_stream); auto points = TestUtils::ToVector(cuda_stream, geometries.get_points()); @@ -327,17 +343,21 @@ TYPED_TEST(WKBLoaderTest, PolygonWKBLoaderMultipolygon) { GEOARROW_TYPE_WKB, stream.get()); nanoarrow::UniqueArray array; + nanoarrow::UniqueSchema schema; ArrowError error; ArrowErrorSet(&error, ""); rmm::cuda_stream cuda_stream; - ASSERT_EQ(ArrowArrayStreamGetNext(stream.get(), array.get(), &error), NANOARROW_OK); + ASSERT_EQ(ArrowArrayStreamGetSchema(stream.get(), schema.get(), &error), NANOARROW_OK) + << error.message; + ASSERT_EQ(ArrowArrayStreamGetNext(stream.get(), array.get(), &error), NANOARROW_OK) + << error.message; ParallelWkbLoader loader; loader.Init(); - loader.Parse(cuda_stream, array.get(), 0, array->length); + loader.Parse(cuda_stream, schema.get(), array.get(), 0, array->length); auto geometries = loader.Finish(cuda_stream); const auto& offsets = geometries.get_offsets(); @@ -431,6 +451,7 @@ TYPED_TEST(WKBLoaderTest, PolygonWKBLoaderMultipolygonLocate) { GEOARROW_TYPE_WKB, stream.get()); nanoarrow::UniqueArray array; + nanoarrow::UniqueSchema schema; ArrowError error; ArrowErrorSet(&error, ""); @@ -438,9 +459,12 @@ TYPED_TEST(WKBLoaderTest, PolygonWKBLoaderMultipolygonLocate) { rmm::cuda_stream cuda_stream; loader.Init(); - ASSERT_EQ(ArrowArrayStreamGetNext(stream.get(), array.get(), &error), NANOARROW_OK); + ASSERT_EQ(ArrowArrayStreamGetSchema(stream.get(), schema.get(), &error), NANOARROW_OK) + << error.message; + ASSERT_EQ(ArrowArrayStreamGetNext(stream.get(), array.get(), &error), NANOARROW_OK) + << error.message; - loader.Parse(cuda_stream, array.get(), 0, array->length); + loader.Parse(cuda_stream, schema.get(), array.get(), 0, array->length); auto geometries = loader.Finish(cuda_stream); const auto& offsets = geometries.get_offsets(); @@ -498,18 +522,21 @@ TYPED_TEST(WKBLoaderTest, MixTypes) { }, GEOARROW_TYPE_WKB, stream.get()); nanoarrow::UniqueArray array; + nanoarrow::UniqueSchema schema; ArrowError error; ArrowErrorSet(&error, ""); rmm::cuda_stream cuda_stream; - - ASSERT_EQ(ArrowArrayStreamGetNext(stream.get(), array.get(), &error), NANOARROW_OK); + ASSERT_EQ(ArrowArrayStreamGetSchema(stream.get(), schema.get(), &error), NANOARROW_OK) + << error.message; + ASSERT_EQ(ArrowArrayStreamGetNext(stream.get(), array.get(), &error), NANOARROW_OK) + << error.message; ParallelWkbLoader loader; loader.Init(); - loader.Parse(cuda_stream, array.get(), 0, array->length); + loader.Parse(cuda_stream, schema.get(), array.get(), 0, array->length); auto geometries = loader.Finish(cuda_stream); const auto& offsets = geometries.get_offsets(); @@ -598,19 +625,22 @@ TYPED_TEST(WKBLoaderTest, GeomCollection) { "MULTIPOLYGON(((30 20, 45 40, 10 40, 30 20)), ((15 5, 40 10, 10 30, 15 5), (20 15, 35 15, 35 25, 20 25, 20 15)))"}}, GEOARROW_TYPE_WKB, stream.get()); nanoarrow::UniqueArray array; + nanoarrow::UniqueSchema schema; ArrowError error; ArrowErrorSet(&error, ""); rmm::cuda_stream cuda_stream; - - ASSERT_EQ(ArrowArrayStreamGetNext(stream.get(), array.get(), &error), NANOARROW_OK); + ASSERT_EQ(ArrowArrayStreamGetSchema(stream.get(), schema.get(), &error), NANOARROW_OK) + << error.message; + ASSERT_EQ(ArrowArrayStreamGetNext(stream.get(), array.get(), &error), NANOARROW_OK) + << error.message; ParallelWkbLoader loader; typename ParallelWkbLoader::Config config; loader.Init(config); - loader.Parse(cuda_stream, array.get(), 0, array->length); + loader.Parse(cuda_stream, schema.get(), array.get(), 0, array->length); auto geometries = loader.Finish(cuda_stream); const auto& offsets = geometries.get_offsets(); diff --git a/c/sedona-libgpuspatial/libgpuspatial/test/main.cc b/c/sedona-libgpuspatial/libgpuspatial/test/main.cc index a8b3c21f3..f89c68fcf 100644 --- a/c/sedona-libgpuspatial/libgpuspatial/test/main.cc +++ b/c/sedona-libgpuspatial/libgpuspatial/test/main.cc @@ -17,6 +17,8 @@ #include // Requires C++17 #include #include + +#include "gpuspatial_testing.hpp" #include "gtest/gtest.h" namespace TestUtils { diff --git a/c/sedona-libgpuspatial/libgpuspatial/test/refiner_test.cu b/c/sedona-libgpuspatial/libgpuspatial/test/refiner_test.cu new file mode 100644 index 000000000..3a48e2e89 --- /dev/null +++ b/c/sedona-libgpuspatial/libgpuspatial/test/refiner_test.cu @@ -0,0 +1,738 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +#include "array_stream.hpp" +#include "gpuspatial/index/rt_spatial_index.hpp" +#include "gpuspatial/loader/device_geometries.hpp" +#include "gpuspatial/refine/rt_spatial_refiner.hpp" +#include "test_common.hpp" + +#include "geoarrow_geos/geoarrow_geos.hpp" +#include "nanoarrow/nanoarrow.hpp" + +#include +#include +#include +#include // For std::iota + +#include "gpuspatial/index/rt_spatial_index.cuh" +#include "gpuspatial/refine/rt_spatial_refiner.cuh" + +namespace gpuspatial { +// Function to read a single Parquet file and extract a column. +static arrow::Status ReadParquetFromFile( + arrow::fs::FileSystem* fs, // 1. Filesystem pointer (e.g., LocalFileSystem) + const std::string& file_path, // 2. Single file path instead of a folder + int64_t batch_size, const char* column_name, + std::vector>& out_arrays) { + // 1. Get FileInfo for the single path + ARROW_ASSIGN_OR_RAISE(auto file_info, fs->GetFileInfo(file_path)); + + // Check if the path points to a file + if (file_info.type() != arrow::fs::FileType::File) { + return arrow::Status::Invalid("Path is not a file: ", file_path); + } + + std::cout << "--- Processing Parquet file: " << file_path << " ---" << std::endl; + + // 2. Open the input file + ARROW_ASSIGN_OR_RAISE(auto input_file, fs->OpenInputFile(file_info)); + + // 3. Open the Parquet file and create an Arrow reader + ARROW_ASSIGN_OR_RAISE(auto arrow_reader, parquet::arrow::OpenFile( + input_file, arrow::default_memory_pool())); + + // 4. Set the batch size + arrow_reader->set_batch_size(batch_size); + + // 5. Get the RecordBatchReader + auto rb_reader = arrow_reader->GetRecordBatchReader().ValueOrDie(); + // 6. Read all record batches and extract the column + while (true) { + std::shared_ptr batch; + + // Read the next batch + ARROW_THROW_NOT_OK(rb_reader->ReadNext(&batch)); + + // Check for end of stream + if (!batch) { + break; + } + + // Extract the specified column and add to the output vector + std::shared_ptr column_array = batch->GetColumnByName(column_name); + if (!column_array) { + return arrow::Status::Invalid("Column not found: ", column_name); + } + out_arrays.push_back(column_array); + } + + std::cout << "Finished reading. Total arrays extracted: " << out_arrays.size() + << std::endl; + return arrow::Status::OK(); +} + +// Helper to concatenate C-style ArrowArrays +arrow::Result> ConcatCArrays( + const std::vector& c_arrays, ArrowSchema* c_schema) { + // 1. Import the schema ONCE into a C++ DataType object. + // This effectively "consumes" c_schema. + ARROW_ASSIGN_OR_RAISE(auto type, arrow::ImportType(c_schema)); + + arrow::ArrayVector arrays_to_concat; + arrays_to_concat.reserve(c_arrays.size()); + + // 2. Loop through arrays using the C++ type object. + for (ArrowArray* c_arr : c_arrays) { + // Use the ImportArray overload that takes std::shared_ptr. + // This validates c_arr against 'type' without consuming 'type'. + ARROW_ASSIGN_OR_RAISE(auto arr, arrow::ImportArray(c_arr, type)); + arrays_to_concat.push_back(arr); + } + + return arrow::Concatenate(arrays_to_concat); +} + +using GeosBinaryPredicateFn = char (*)(GEOSContextHandle_t, const GEOSGeometry*, + const GEOSGeometry*); + +static GeosBinaryPredicateFn GetGeosPredicateFn(Predicate predicate) { + switch (predicate) { + case Predicate::kContains: + return &GEOSContains_r; + case Predicate::kIntersects: + return &GEOSIntersects_r; + case Predicate::kWithin: + return &GEOSWithin_r; + case Predicate::kEquals: + return &GEOSEquals_r; + case Predicate::kTouches: + return &GEOSTouches_r; + default: + throw std::out_of_range("Unsupported GEOS predicate enumeration value."); + } +} + +std::vector> ReadParquet(const std::string& path, + int batch_size = 100) { + using namespace TestUtils; + + auto fs = std::make_shared(); + + std::vector> build_arrays; + ARROW_THROW_NOT_OK( + ReadParquetFromFile(fs.get(), path, batch_size, "geometry", build_arrays)); + return build_arrays; +} + +void ReadArrowIPC(const std::string& path, std::vector& arrays, + std::vector& schemas, + uint32_t limit = std::numeric_limits::max()) { + nanoarrow::UniqueArrayStream stream; + ArrowError error; + + // Assuming this helper exists in your context or you implement it via Arrow C++ + // (It populates the C-stream from the file) + ArrayStreamFromIpc(path, "geometry", stream.get()); + uint32_t count = 0; + while (true) { + // 1. Create fresh objects for this iteration + nanoarrow::UniqueArray array; + nanoarrow::UniqueSchema schema; + + // 2. Get the next batch + // Note: This function expects 'array' to be empty/released. + int code = ArrowArrayStreamGetNext(stream.get(), array.get(), &error); + if (code != NANOARROW_OK) { + // Handle error (log or throw) + break; + } + + // 3. CHECK END OF STREAM + // If release is NULL, the stream is finished. + if (array->release == nullptr) { + break; + } + + // 4. Get the schema for this specific batch + // ArrowArrayStreamGetSchema creates a deep copy of the schema into 'schema'. + code = ArrowArrayStreamGetSchema(stream.get(), schema.get(), &error); + if (code != NANOARROW_OK) { + // Handle error + break; + } + + // 5. Move ownership to the output vectors + arrays.push_back(std::move(array)); + schemas.push_back(std::move(schema)); + count += array->length; + if (count >= limit) break; + } +} + +void TestJoiner(ArrowSchema* build_schema, std::vector& build_arrays, + ArrowSchema* probe_schema, std::vector& probe_arrays, + Predicate predicate) { + using namespace TestUtils; + using coord_t = double; + using fpoint_t = Point; + using box_t = Box; + + auto rt_engine = std::make_shared(); + + { + std::string ptx_root = GetTestShaderPath(); + auto config = get_default_rt_config(ptx_root); + rt_engine->Init(config); + } + + RTSpatialIndexConfig idx_config; + idx_config.rt_engine = rt_engine; + auto rt_index = CreateRTSpatialIndex(idx_config); + RTSpatialRefinerConfig refiner_config; + refiner_config.rt_engine = rt_engine; + auto rt_refiner = CreateRTSpatialRefiner(refiner_config); + + geoarrow::geos::ArrayReader reader; + + class GEOSCppHandle { + public: + GEOSContextHandle_t handle; + + GEOSCppHandle() { handle = GEOS_init_r(); } + + ~GEOSCppHandle() { GEOS_finish_r(handle); } + }; + GEOSCppHandle handle; + + reader.InitFromEncoding(handle.handle, GEOARROW_GEOS_ENCODING_WKB); + + geoarrow::geos::GeometryVector geom_build(handle.handle); + size_t total_build_length = 0; + + for (auto& array : build_arrays) { + total_build_length += array->length; + } + + // Using GEOS for reference + geom_build.resize(total_build_length); + size_t tail_build = 0; + auto* tree = GEOSSTRtree_create_r(handle.handle, 10); + for (auto& array : build_arrays) { + // geos for reference + size_t n_build; + + ASSERT_EQ(reader.Read((ArrowArray*)array, 0, array->length, + geom_build.mutable_data() + tail_build, &n_build), + GEOARROW_GEOS_OK); + ASSERT_EQ(array->length, n_build); + std::vector rects; + + for (size_t offset = tail_build; offset < tail_build + n_build; offset++) { + auto* geom = geom_build.borrow(offset); + auto* box = GEOSEnvelope_r(handle.handle, geom); + + double xmin, ymin, xmax, ymax; + if (GEOSGeom_getExtent_r(handle.handle, box, &xmin, &ymin, &xmax, &ymax) == 0) { + printf("Error getting extent\n"); + xmin = 0; + ymin = 0; + xmax = -1; + ymax = -1; + } + + box_t bbox(fpoint_t((float)xmin, (float)ymin), fpoint_t((float)xmax, (float)ymax)); + + rects.push_back(bbox); + + GEOSGeom_setUserData_r(handle.handle, (GEOSGeometry*)geom, (void*)offset); + GEOSSTRtree_insert_r(handle.handle, tree, box, (void*)geom); + GEOSGeom_destroy_r(handle.handle, box); + } + rt_index->PushBuild(rects.data(), rects.size()); + tail_build += n_build; + } + rt_index->FinishBuilding(); + ASSERT_EQ(GEOSSTRtree_build_r(handle.handle, tree), 1); + + auto build_array_ptr = ConcatCArrays(build_arrays, build_schema).ValueOrDie(); + + nanoarrow::UniqueArray uniq_build_array; + nanoarrow::UniqueSchema uniq_build_schema; + ARROW_THROW_NOT_OK(arrow::ExportArray(*build_array_ptr, uniq_build_array.get(), + uniq_build_schema.get())); + // Start stream processing + + for (auto& array : probe_arrays) { + geoarrow::geos::GeometryVector geom_stream(handle.handle); + size_t n_stream; + geom_stream.resize(array->length); + + ASSERT_EQ(reader.Read(array, 0, array->length, geom_stream.mutable_data(), &n_stream), + GEOARROW_GEOS_OK); + + std::vector queries; + + for (size_t i = 0; i < array->length; i++) { + auto* geom = geom_stream.borrow(i); + double xmin, ymin, xmax, ymax; + int result = GEOSGeom_getExtent_r(handle.handle, geom, &xmin, &ymin, &xmax, &ymax); + ASSERT_EQ(result, 1); + box_t bbox(fpoint_t((float)xmin, (float)ymin), fpoint_t((float)xmax, (float)ymax)); + queries.push_back(bbox); + } + + std::vector build_indices, stream_indices; + + rt_index->Probe(queries.data(), queries.size(), &build_indices, &stream_indices); + auto old_size = build_indices.size(); + + auto new_size = rt_refiner->Refine( + uniq_build_schema.get(), uniq_build_array.get(), probe_schema, array, predicate, + build_indices.data(), stream_indices.data(), build_indices.size()); + + build_indices.resize(new_size); + stream_indices.resize(new_size); + + struct Payload { + GEOSContextHandle_t handle; + const GEOSGeometry* geom; + std::vector build_indices; + std::vector stream_indices; + Predicate predicate; + }; + + Payload payload; + payload.predicate = predicate; + payload.handle = handle.handle; + + for (size_t offset = 0; offset < n_stream; offset++) { + auto* geom = geom_stream.borrow(offset); + GEOSGeom_setUserData_r(handle.handle, (GEOSGeometry*)geom, (void*)offset); + payload.geom = geom; + + GEOSSTRtree_query_r( + handle.handle, tree, geom, + [](void* item, void* data) { + auto* geom_build = (GEOSGeometry*)item; + auto* payload = (Payload*)data; + auto* geom_stream = payload->geom; + + if (GetGeosPredicateFn(payload->predicate)(payload->handle, geom_build, + geom_stream) == 1) { + auto build_id = (size_t)GEOSGeom_getUserData_r(payload->handle, geom_build); + auto stream_id = + (size_t)GEOSGeom_getUserData_r(payload->handle, geom_stream); + payload->build_indices.push_back(build_id); + payload->stream_indices.push_back(stream_id); + } + }, + (void*)&payload); + } + + ASSERT_EQ(payload.build_indices.size(), build_indices.size()); + ASSERT_EQ(payload.stream_indices.size(), stream_indices.size()); + sort_vectors_by_index(payload.build_indices, payload.stream_indices); + sort_vectors_by_index(build_indices, stream_indices); + for (size_t j = 0; j < build_indices.size(); j++) { + ASSERT_EQ(payload.build_indices[j], build_indices[j]); + ASSERT_EQ(payload.stream_indices[j], stream_indices[j]); + } + } + GEOSSTRtree_destroy_r(handle.handle, tree); +} + +void TestJoinerLoaded(ArrowSchema* build_schema, std::vector& build_arrays, + ArrowSchema* probe_schema, std::vector& probe_arrays, + Predicate predicate, bool pipelined = false) { + using namespace TestUtils; + using coord_t = double; + using fpoint_t = Point; + using box_t = Box; + + auto rt_engine = std::make_shared(); + { + std::string ptx_root = TestUtils::GetTestShaderPath(); + auto config = get_default_rt_config(ptx_root); + rt_engine->Init(config); + } + + RTSpatialIndexConfig idx_config; + idx_config.rt_engine = rt_engine; + auto rt_index = CreateRTSpatialIndex(idx_config); + + RTSpatialRefinerConfig refiner_config; + refiner_config.rt_engine = rt_engine; + if (pipelined) { + refiner_config.pipeline_batches = 10; + } + auto rt_refiner = CreateRTSpatialRefiner(refiner_config); + geoarrow::geos::ArrayReader reader; + + class GEOSCppHandle { + public: + GEOSContextHandle_t handle; + + GEOSCppHandle() { handle = GEOS_init_r(); } + + ~GEOSCppHandle() { GEOS_finish_r(handle); } + }; + GEOSCppHandle handle; + + reader.InitFromEncoding(handle.handle, GEOARROW_GEOS_ENCODING_WKB); + + geoarrow::geos::GeometryVector geom_build(handle.handle); + size_t total_build_length = 0; + + for (auto& array : build_arrays) { + total_build_length += array->length; + } + + // Using GEOS for reference + geom_build.resize(total_build_length); + size_t tail_build = 0; + auto* tree = GEOSSTRtree_create_r(handle.handle, 10); + for (auto& array : build_arrays) { + // geos for reference + size_t n_build; + + ASSERT_EQ(reader.Read((ArrowArray*)array, 0, array->length, + geom_build.mutable_data() + tail_build, &n_build), + GEOARROW_GEOS_OK); + ASSERT_EQ(array->length, n_build); + std::vector rects; + + for (size_t offset = tail_build; offset < tail_build + n_build; offset++) { + auto* geom = geom_build.borrow(offset); + auto* box = GEOSEnvelope_r(handle.handle, geom); + + double xmin, ymin, xmax, ymax; + if (GEOSGeom_getExtent_r(handle.handle, box, &xmin, &ymin, &xmax, &ymax) == 0) { + xmin = 0; + ymin = 0; + xmax = -1; + ymax = -1; + } + + box_t bbox(fpoint_t((float)xmin, (float)ymin), fpoint_t((float)xmax, (float)ymax)); + + rects.push_back(bbox); + + GEOSGeom_setUserData_r(handle.handle, (GEOSGeometry*)geom, (void*)offset); + GEOSSTRtree_insert_r(handle.handle, tree, box, (void*)geom); + GEOSGeom_destroy_r(handle.handle, box); + } + rt_index->PushBuild(rects.data(), rects.size()); + tail_build += n_build; + } + rt_index->FinishBuilding(); + ASSERT_EQ(GEOSSTRtree_build_r(handle.handle, tree), 1); + + auto build_array_ptr = ConcatCArrays(build_arrays, build_schema).ValueOrDie(); + + nanoarrow::UniqueArray uniq_build_array; + nanoarrow::UniqueSchema uniq_build_schema; + ARROW_THROW_NOT_OK(arrow::ExportArray(*build_array_ptr, uniq_build_array.get(), + uniq_build_schema.get())); + // Start stream processing + + rt_refiner->PushBuild(uniq_build_schema.get(), uniq_build_array.get()); + rt_refiner->FinishBuilding(); + + for (auto& array : probe_arrays) { + geoarrow::geos::GeometryVector geom_stream(handle.handle); + size_t n_stream; + geom_stream.resize(array->length); + + ASSERT_EQ(reader.Read(array, 0, array->length, geom_stream.mutable_data(), &n_stream), + GEOARROW_GEOS_OK); + + std::vector queries; + + for (size_t i = 0; i < array->length; i++) { + auto* geom = geom_stream.borrow(i); + double xmin, ymin, xmax, ymax; + int result = GEOSGeom_getExtent_r(handle.handle, geom, &xmin, &ymin, &xmax, &ymax); + ASSERT_EQ(result, 1); + box_t bbox(fpoint_t((float)xmin, (float)ymin), fpoint_t((float)xmax, (float)ymax)); + queries.push_back(bbox); + } + + std::vector build_indices, stream_indices; + + rt_index->Probe(queries.data(), queries.size(), &build_indices, &stream_indices); + auto old_size = build_indices.size(); + + auto new_size = + rt_refiner->Refine(probe_schema, array, predicate, build_indices.data(), + stream_indices.data(), build_indices.size()); + + printf("Old size %u, new size %u\n", (unsigned)old_size, (unsigned)new_size); + build_indices.resize(new_size); + stream_indices.resize(new_size); + + struct Payload { + GEOSContextHandle_t handle; + const GEOSGeometry* geom; + std::vector build_indices; + std::vector stream_indices; + Predicate predicate; + }; + + Payload payload; + payload.predicate = predicate; + payload.handle = handle.handle; + + for (size_t offset = 0; offset < n_stream; offset++) { + auto* geom = geom_stream.borrow(offset); + GEOSGeom_setUserData_r(handle.handle, (GEOSGeometry*)geom, (void*)offset); + payload.geom = geom; + + GEOSSTRtree_query_r( + handle.handle, tree, geom, + [](void* item, void* data) { + auto* geom_build = (GEOSGeometry*)item; + auto* payload = (Payload*)data; + auto* geom_stream = payload->geom; + + if (GetGeosPredicateFn(payload->predicate)(payload->handle, geom_build, + geom_stream) == 1) { + auto build_id = (size_t)GEOSGeom_getUserData_r(payload->handle, geom_build); + auto stream_id = + (size_t)GEOSGeom_getUserData_r(payload->handle, geom_stream); + payload->build_indices.push_back(build_id); + payload->stream_indices.push_back(stream_id); + } + }, + (void*)&payload); + } + + ASSERT_EQ(payload.build_indices.size(), build_indices.size()); + ASSERT_EQ(payload.stream_indices.size(), stream_indices.size()); + sort_vectors_by_index(payload.build_indices, payload.stream_indices); + sort_vectors_by_index(build_indices, stream_indices); + for (size_t j = 0; j < build_indices.size(); j++) { + ASSERT_EQ(payload.build_indices[j], build_indices[j]); + ASSERT_EQ(payload.stream_indices[j], stream_indices[j]); + } + } + GEOSSTRtree_destroy_r(handle.handle, tree); +} + +TEST(JoinerTest, PIPContainsParquet) { + using namespace TestUtils; + auto fs = std::make_shared(); + + std::vector polys{ + GetTestDataPath("cities/natural-earth_cities_geo.parquet"), + GetTestDataPath("countries/natural-earth_countries_geo.parquet")}; + std::vector points{GetTestDataPath("cities/generated_points.parquet"), + GetTestDataPath("countries/generated_points.parquet")}; + + for (int i = 0; i < polys.size(); i++) { + auto poly_path = TestUtils::GetTestDataPath(polys[i]); + auto point_path = TestUtils::GetCanonicalPath(points[i]); + auto poly_arrays = ReadParquet(poly_path, 1000); + auto point_arrays = ReadParquet(point_path, 1000); + std::vector poly_uniq_arrays, point_uniq_arrays; + std::vector poly_uniq_schema, point_uniq_schema; + + for (auto& arr : poly_arrays) { + ARROW_THROW_NOT_OK(arrow::ExportArray(*arr, poly_uniq_arrays.emplace_back().get(), + poly_uniq_schema.emplace_back().get())); + } + for (auto& arr : point_arrays) { + ARROW_THROW_NOT_OK(arrow::ExportArray(*arr, point_uniq_arrays.emplace_back().get(), + point_uniq_schema.emplace_back().get())); + } + + std::vector poly_c_arrays, point_c_arrays; + for (auto& arr : poly_uniq_arrays) { + poly_c_arrays.push_back(arr.get()); + } + for (auto& arr : point_uniq_arrays) { + point_c_arrays.push_back(arr.get()); + } + TestJoinerLoaded(poly_uniq_schema[0].get(), poly_c_arrays, point_uniq_schema[0].get(), + point_c_arrays, Predicate::kContains); + } +} + +TEST(JoinerTest, PIPContainsParquetLoaded) { + using namespace TestUtils; + auto fs = std::make_shared(); + + std::vector polys{ + GetTestDataPath("cities/natural-earth_cities_geo.parquet"), + GetTestDataPath("countries/natural-earth_countries_geo.parquet")}; + std::vector points{GetTestDataPath("cities/generated_points.parquet"), + GetTestDataPath("countries/generated_points.parquet")}; + + for (int i = 0; i < polys.size(); i++) { + auto poly_path = TestUtils::GetTestDataPath(polys[i]); + auto point_path = TestUtils::GetCanonicalPath(points[i]); + auto poly_arrays = ReadParquet(poly_path, 1000); + auto point_arrays = ReadParquet(point_path, 1000); + std::vector poly_uniq_arrays, point_uniq_arrays; + std::vector poly_uniq_schema, point_uniq_schema; + + for (auto& arr : poly_arrays) { + ARROW_THROW_NOT_OK(arrow::ExportArray(*arr, poly_uniq_arrays.emplace_back().get(), + poly_uniq_schema.emplace_back().get())); + } + for (auto& arr : point_arrays) { + ARROW_THROW_NOT_OK(arrow::ExportArray(*arr, point_uniq_arrays.emplace_back().get(), + point_uniq_schema.emplace_back().get())); + } + + std::vector poly_c_arrays, point_c_arrays; + for (auto& arr : poly_uniq_arrays) { + poly_c_arrays.push_back(arr.get()); + } + for (auto& arr : point_uniq_arrays) { + point_c_arrays.push_back(arr.get()); + } + TestJoinerLoaded(poly_uniq_schema[0].get(), poly_c_arrays, point_uniq_schema[0].get(), + point_c_arrays, Predicate::kContains); + } +} + +TEST(JoinerTest, PIPContainsParquetPipelined) { + using namespace TestUtils; + auto fs = std::make_shared(); + + std::vector polys{ + GetTestDataPath("cities/natural-earth_cities_geo.parquet"), + GetTestDataPath("countries/natural-earth_countries_geo.parquet")}; + std::vector points{GetTestDataPath("cities/generated_points.parquet"), + GetTestDataPath("countries/generated_points.parquet")}; + + for (int i = 0; i < polys.size(); i++) { + auto poly_path = TestUtils::GetTestDataPath(polys[i]); + auto point_path = TestUtils::GetCanonicalPath(points[i]); + auto poly_arrays = ReadParquet(poly_path, 1000); + auto point_arrays = ReadParquet(point_path, 1000); + std::vector poly_uniq_arrays, point_uniq_arrays; + std::vector poly_uniq_schema, point_uniq_schema; + + for (auto& arr : poly_arrays) { + ARROW_THROW_NOT_OK(arrow::ExportArray(*arr, poly_uniq_arrays.emplace_back().get(), + poly_uniq_schema.emplace_back().get())); + } + for (auto& arr : point_arrays) { + ARROW_THROW_NOT_OK(arrow::ExportArray(*arr, point_uniq_arrays.emplace_back().get(), + point_uniq_schema.emplace_back().get())); + } + + std::vector poly_c_arrays, point_c_arrays; + for (auto& arr : poly_uniq_arrays) { + poly_c_arrays.push_back(arr.get()); + } + for (auto& arr : point_uniq_arrays) { + point_c_arrays.push_back(arr.get()); + } + TestJoinerLoaded(poly_uniq_schema[0].get(), poly_c_arrays, point_uniq_schema[0].get(), + point_c_arrays, Predicate::kContains, true); + } +} + +TEST(JoinerTest, PIPContainsArrowIPC) { + using namespace TestUtils; + auto fs = std::make_shared(); + + std::vector polys{GetTestDataPath("arrowipc/test_polygons.arrows")}; + std::vector points{GetTestDataPath("arrowipc/test_points.arrows")}; + + for (int i = 0; i < polys.size(); i++) { + auto poly_path = TestUtils::GetTestDataPath(polys[i]); + auto point_path = TestUtils::GetCanonicalPath(points[i]); + std::vector poly_uniq_arrays, point_uniq_arrays; + std::vector poly_uniq_schema, point_uniq_schema; + + ReadArrowIPC(poly_path, poly_uniq_arrays, poly_uniq_schema); + ReadArrowIPC(point_path, point_uniq_arrays, point_uniq_schema); + + std::vector poly_c_arrays, point_c_arrays; + for (auto& arr : poly_uniq_arrays) { + poly_c_arrays.push_back(arr.get()); + } + for (auto& arr : point_uniq_arrays) { + point_c_arrays.push_back(arr.get()); + } + + TestJoiner(poly_uniq_schema[0].get(), poly_c_arrays, point_uniq_schema[0].get(), + point_c_arrays, Predicate::kContains); + } +} + +TEST(JoinerTest, PIPWithinArrowIPC) { + using namespace TestUtils; + auto fs = std::make_shared(); + + std::vector polys{GetTestDataPath("arrowipc/test_polygons.arrows")}; + std::vector points{GetTestDataPath("arrowipc/test_points.arrows")}; + + for (int i = 0; i < polys.size(); i++) { + auto poly_path = TestUtils::GetTestDataPath(polys[i]); + auto point_path = TestUtils::GetCanonicalPath(points[i]); + std::vector poly_uniq_arrays, point_uniq_arrays; + std::vector poly_uniq_schema, point_uniq_schema; + + ReadArrowIPC(poly_path, poly_uniq_arrays, poly_uniq_schema); + ReadArrowIPC(point_path, point_uniq_arrays, point_uniq_schema); + + std::vector poly_c_arrays, point_c_arrays; + for (auto& arr : poly_uniq_arrays) { + poly_c_arrays.push_back(arr.get()); + } + for (auto& arr : point_uniq_arrays) { + point_c_arrays.push_back(arr.get()); + } + + TestJoiner(point_uniq_schema[0].get(), point_c_arrays, poly_uniq_schema[0].get(), + poly_c_arrays, Predicate::kWithin); + } +} + +TEST(JoinerTest, PolygonPolygonContains) { + using namespace TestUtils; + auto fs = std::make_shared(); + + std::vector polys1{GetTestDataPath("arrowipc/test_polygons1.arrows")}; + std::vector polys2{GetTestDataPath("arrowipc/test_polygons2.arrows")}; + + for (int i = 0; i < polys1.size(); i++) { + auto poly1_path = TestUtils::GetTestDataPath(polys1[i]); + auto poly2_path = TestUtils::GetCanonicalPath(polys2[i]); + std::vector poly1_uniq_arrays, poly2_uniq_arrays; + std::vector poly1_uniq_schema, poly2_uniq_schema; + + ReadArrowIPC(poly1_path, poly1_uniq_arrays, poly1_uniq_schema, 100); + ReadArrowIPC(poly2_path, poly2_uniq_arrays, poly2_uniq_schema, 100); + + std::vector poly1_c_arrays, poly2_c_arrays; + for (auto& arr : poly1_uniq_arrays) { + poly1_c_arrays.push_back(arr.get()); + } + for (auto& arr : poly2_uniq_arrays) { + poly2_c_arrays.push_back(arr.get()); + } + + TestJoiner(poly1_uniq_schema[0].get(), poly1_c_arrays, poly2_uniq_schema[0].get(), + poly2_c_arrays, Predicate::kIntersects); + } +} +} // namespace gpuspatial diff --git a/c/sedona-libgpuspatial/libgpuspatial/test/related_test.cu b/c/sedona-libgpuspatial/libgpuspatial/test/related_test.cu index fabcd3f5c..2b0ffb9a9 100644 --- a/c/sedona-libgpuspatial/libgpuspatial/test/related_test.cu +++ b/c/sedona-libgpuspatial/libgpuspatial/test/related_test.cu @@ -15,9 +15,9 @@ // specific language governing permissions and limitations // under the License. #include "array_stream.hpp" -#include "gpuspatial/loader/parallel_wkb_loader.h" -#include "gpuspatial/relate/relate.cuh" -#include "gpuspatial/utils/pinned_vector.h" +#include "gpuspatial/loader/parallel_wkb_loader.hpp" +#include "gpuspatial/relate/relate.hpp" +#include "gpuspatial/utils/pinned_vector.hpp" #include "test_common.hpp" @@ -58,15 +58,18 @@ void ParseWKTPoint(const char* wkt, POINT_T& point) { nanoarrow::UniqueArrayStream stream; ArrayStreamFromWKT({{wkt}}, GEOARROW_TYPE_WKB, stream.get()); nanoarrow::UniqueArray array; + nanoarrow::UniqueSchema schema; ArrowError error; ArrowErrorSet(&error, ""); - - ASSERT_EQ(ArrowArrayStreamGetNext(stream.get(), array.get(), &error), NANOARROW_OK); + ASSERT_EQ(ArrowArrayStreamGetSchema(stream.get(), schema.get(), &error), NANOARROW_OK) + << error.message; + ASSERT_EQ(ArrowArrayStreamGetNext(stream.get(), array.get(), &error), NANOARROW_OK) + << error.message; loader_t loader; auto cuda_stream = rmm::cuda_stream_default; loader.Init(); - loader.Parse(cuda_stream, array.get(), 0, array->length); + loader.Parse(cuda_stream, schema.get(), array.get(), 0, array->length); auto device_geometries = loader.Finish(cuda_stream); auto h_vec = TestUtils::ToVector(cuda_stream, device_geometries.get_points()); cuda_stream.synchronize(); @@ -79,15 +82,19 @@ void ParseWKTMultiPoint(Context& ctx, const char* wkt, nanoarrow::UniqueArrayStream stream; ArrayStreamFromWKT({{wkt}}, GEOARROW_TYPE_WKB, stream.get()); nanoarrow::UniqueArray array; + nanoarrow::UniqueSchema schema; ArrowError error; ArrowErrorSet(&error, ""); - ASSERT_EQ(ArrowArrayStreamGetNext(stream.get(), array.get(), &error), NANOARROW_OK); + ASSERT_EQ(ArrowArrayStreamGetSchema(stream.get(), schema.get(), &error), NANOARROW_OK) + << error.message; + ASSERT_EQ(ArrowArrayStreamGetNext(stream.get(), array.get(), &error), NANOARROW_OK) + << error.message; loader_t loader; auto cuda_stream = rmm::cuda_stream_default; loader.Init(); - loader.Parse(cuda_stream, array.get(), 0, array->length); + loader.Parse(cuda_stream, schema.get(), array.get(), 0, array->length); auto device_geometries = loader.Finish(cuda_stream); ctx.prefix_sum1 = TestUtils::ToVector( @@ -108,15 +115,19 @@ void ParseWKTLineString(Context& ctx, const char* wkt, nanoarrow::UniqueArrayStream stream; ArrayStreamFromWKT({{wkt}}, GEOARROW_TYPE_WKB, stream.get()); nanoarrow::UniqueArray array; + nanoarrow::UniqueSchema schema; ArrowError error; ArrowErrorSet(&error, ""); - ASSERT_EQ(ArrowArrayStreamGetNext(stream.get(), array.get(), &error), NANOARROW_OK); + ASSERT_EQ(ArrowArrayStreamGetSchema(stream.get(), schema.get(), &error), NANOARROW_OK) + << error.message; + ASSERT_EQ(ArrowArrayStreamGetNext(stream.get(), array.get(), &error), NANOARROW_OK) + << error.message; loader_t loader; auto cuda_stream = rmm::cuda_stream_default; loader.Init(); - loader.Parse(cuda_stream, array.get(), 0, array->length); + loader.Parse(cuda_stream, schema.get(), array.get(), 0, array->length); auto device_geometries = loader.Finish(cuda_stream); ctx.prefix_sum1 = TestUtils::ToVector( cuda_stream, device_geometries.get_offsets().line_string_offsets.ps_num_points); @@ -136,15 +147,19 @@ void ParseWKTMultiLineString(Context& ctx, const char* wkt, nanoarrow::UniqueArrayStream stream; ArrayStreamFromWKT({{wkt}}, GEOARROW_TYPE_WKB, stream.get()); nanoarrow::UniqueArray array; + nanoarrow::UniqueSchema schema; ArrowError error; ArrowErrorSet(&error, ""); - ASSERT_EQ(ArrowArrayStreamGetNext(stream.get(), array.get(), &error), NANOARROW_OK); + ASSERT_EQ(ArrowArrayStreamGetSchema(stream.get(), schema.get(), &error), NANOARROW_OK) + << error.message; + ASSERT_EQ(ArrowArrayStreamGetNext(stream.get(), array.get(), &error), NANOARROW_OK) + << error.message; loader_t loader; auto cuda_stream = rmm::cuda_stream_default; loader.Init(); - loader.Parse(cuda_stream, array.get(), 0, array->length); + loader.Parse(cuda_stream, schema.get(), array.get(), 0, array->length); auto device_geometries = loader.Finish(cuda_stream); ctx.prefix_sum1 = TestUtils::ToVector( cuda_stream, @@ -169,15 +184,19 @@ void ParseWKTPolygon(Context& ctx, const char* wkt, nanoarrow::UniqueArrayStream stream; ArrayStreamFromWKT({{wkt}}, GEOARROW_TYPE_WKB, stream.get()); nanoarrow::UniqueArray array; + nanoarrow::UniqueSchema schema; ArrowError error; ArrowErrorSet(&error, ""); - ASSERT_EQ(ArrowArrayStreamGetNext(stream.get(), array.get(), &error), NANOARROW_OK); + ASSERT_EQ(ArrowArrayStreamGetSchema(stream.get(), schema.get(), &error), NANOARROW_OK) + << error.message; + ASSERT_EQ(ArrowArrayStreamGetNext(stream.get(), array.get(), &error), NANOARROW_OK) + << error.message; loader_t loader; auto cuda_stream = rmm::cuda_stream_default; loader.Init(); - loader.Parse(cuda_stream, array.get(), 0, array->length); + loader.Parse(cuda_stream, schema.get(), array.get(), 0, array->length); auto device_geometries = loader.Finish(cuda_stream); ctx.prefix_sum1 = TestUtils::ToVector( cuda_stream, device_geometries.get_offsets().polygon_offsets.ps_num_rings); @@ -200,15 +219,19 @@ void ParseWKTMultiPolygon(Context& ctx, const char* wkt, nanoarrow::UniqueArrayStream stream; ArrayStreamFromWKT({{wkt}}, GEOARROW_TYPE_WKB, stream.get()); nanoarrow::UniqueArray array; + nanoarrow::UniqueSchema schema; ArrowError error; ArrowErrorSet(&error, ""); - ASSERT_EQ(ArrowArrayStreamGetNext(stream.get(), array.get(), &error), NANOARROW_OK); + ASSERT_EQ(ArrowArrayStreamGetSchema(stream.get(), schema.get(), &error), NANOARROW_OK) + << error.message; + ASSERT_EQ(ArrowArrayStreamGetNext(stream.get(), array.get(), &error), NANOARROW_OK) + << error.message; loader_t loader; auto cuda_stream = rmm::cuda_stream_default; loader.Init(); - loader.Parse(cuda_stream, array.get(), 0, array->length); + loader.Parse(cuda_stream, schema.get(), array.get(), 0, array->length); auto device_geometries = loader.Finish(cuda_stream); ctx.prefix_sum1 = TestUtils::ToVector( cuda_stream, device_geometries.get_offsets().multi_polygon_offsets.ps_num_parts); diff --git a/c/sedona-libgpuspatial/libgpuspatial/test/test_common.hpp b/c/sedona-libgpuspatial/libgpuspatial/test/test_common.hpp index ecd9fd460..0412cf6f9 100644 --- a/c/sedona-libgpuspatial/libgpuspatial/test/test_common.hpp +++ b/c/sedona-libgpuspatial/libgpuspatial/test/test_common.hpp @@ -16,9 +16,9 @@ // under the License. #pragma once -#include "gpuspatial/geom/point.cuh" -#include "gpuspatial/utils/array_view.h" -#include "gpuspatial/utils/pinned_vector.h" +#include "gpuspatial/geom/point.hpp" +#include "gpuspatial/utils/array_view.hpp" +#include "gpuspatial/utils/pinned_vector.hpp" #include "gtest/gtest.h" #include "rmm/cuda_stream_view.hpp" diff --git a/c/sedona-libgpuspatial/libgpuspatial/vcpkg.json b/c/sedona-libgpuspatial/libgpuspatial/vcpkg.json index b162d78e2..f593623e8 100644 --- a/c/sedona-libgpuspatial/libgpuspatial/vcpkg.json +++ b/c/sedona-libgpuspatial/libgpuspatial/vcpkg.json @@ -7,6 +7,7 @@ "dependencies": [ "gtest", "geos", + "zstd", { "name": "arrow", "features": [ diff --git a/c/sedona-libgpuspatial/src/error.rs b/c/sedona-libgpuspatial/src/error.rs index 3530e40e8..d38897019 100644 --- a/c/sedona-libgpuspatial/src/error.rs +++ b/c/sedona-libgpuspatial/src/error.rs @@ -24,7 +24,8 @@ pub enum GpuSpatialError { Init(String), PushBuild(String), FinishBuild(String), - PushStream(String), + Probe(String), + Refine(String), } impl From for GpuSpatialError { @@ -48,8 +49,11 @@ impl fmt::Display for GpuSpatialError { GpuSpatialError::FinishBuild(errmsg) => { write!(f, "Finish building failed: {}", errmsg) } - GpuSpatialError::PushStream(errmsg) => { - write!(f, "Push stream failed: {}", errmsg) + GpuSpatialError::Probe(errmsg) => { + write!(f, "Probe failed: {}", errmsg) + } + GpuSpatialError::Refine(errmsg) => { + write!(f, "Refine failed: {}", errmsg) } } } diff --git a/c/sedona-libgpuspatial/src/lib.rs b/c/sedona-libgpuspatial/src/lib.rs index 1bcd4ef43..5e646ebcb 100644 --- a/c/sedona-libgpuspatial/src/lib.rs +++ b/c/sedona-libgpuspatial/src/lib.rs @@ -23,30 +23,44 @@ mod libgpuspatial; #[cfg(gpu_available)] mod libgpuspatial_glue_bindgen; -// Import Array trait for len() method (used in gpu_available code) #[cfg(gpu_available)] -use arrow_array::Array; - +use std::sync::{Arc, Mutex}; +// Import Array trait for len() method (used in gpu_available code) +use geo::Rect; // Re-exports for GPU functionality #[cfg(gpu_available)] pub use error::GpuSpatialError; #[cfg(gpu_available)] -pub use libgpuspatial::{GpuSpatialJoinerWrapper, GpuSpatialPredicateWrapper}; +pub use libgpuspatial::{ + GpuSpatialIndexFloat2DWrapper, GpuSpatialRefinerWrapper, GpuSpatialRelationPredicateWrapper, + GpuSpatialRuntimeWrapper, +}; #[cfg(gpu_available)] -pub use libgpuspatial_glue_bindgen::GpuSpatialJoinerContext; +pub use libgpuspatial_glue_bindgen::SedonaSpatialIndexContext; +#[cfg(gpu_available)] +use nvml_wrapper::Nvml; // Mark GPU types as Send for thread safety // SAFETY: The GPU library is designed to be used from multiple threads. // Each thread gets its own context, and the underlying GPU library handles thread safety. // The raw pointers inside are managed by the C++ library which ensures proper synchronization. #[cfg(gpu_available)] -unsafe impl Send for GpuSpatialJoinerContext {} +unsafe impl Send for SedonaSpatialIndexContext {} +#[cfg(gpu_available)] +unsafe impl Send for libgpuspatial_glue_bindgen::GpuSpatialRuntime {} +#[cfg(gpu_available)] +unsafe impl Sync for libgpuspatial_glue_bindgen::GpuSpatialRuntime {} #[cfg(gpu_available)] -unsafe impl Send for libgpuspatial_glue_bindgen::GpuSpatialJoiner {} +unsafe impl Send for libgpuspatial_glue_bindgen::SedonaFloatIndex2D {} +#[cfg(gpu_available)] +unsafe impl Send for libgpuspatial_glue_bindgen::SedonaSpatialRefiner {} + +#[cfg(gpu_available)] +unsafe impl Sync for libgpuspatial_glue_bindgen::SedonaFloatIndex2D {} #[cfg(gpu_available)] -unsafe impl Send for GpuSpatialJoinerWrapper {} +unsafe impl Sync for libgpuspatial_glue_bindgen::SedonaSpatialRefiner {} // Error type for non-GPU builds #[cfg(not(gpu_available))] @@ -58,16 +72,77 @@ pub enum GpuSpatialError { pub type Result = std::result::Result; +/// Spatial predicates for GPU operations +#[repr(u32)] +#[derive(Debug, PartialEq, Copy, Clone)] +pub enum GpuSpatialRelationPredicate { + Equals = 0, + Disjoint = 1, + Touches = 2, + Contains = 3, + Covers = 4, + Intersects = 5, + Within = 6, + CoveredBy = 7, +} + +impl std::fmt::Display for GpuSpatialRelationPredicate { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + GpuSpatialRelationPredicate::Equals => write!(f, "equals"), + GpuSpatialRelationPredicate::Disjoint => write!(f, "disjoint"), + GpuSpatialRelationPredicate::Touches => write!(f, "touches"), + GpuSpatialRelationPredicate::Contains => write!(f, "contains"), + GpuSpatialRelationPredicate::Covers => write!(f, "covers"), + GpuSpatialRelationPredicate::Intersects => write!(f, "intersects"), + GpuSpatialRelationPredicate::Within => write!(f, "within"), + GpuSpatialRelationPredicate::CoveredBy => write!(f, "coveredby"), + } + } +} + +#[cfg(gpu_available)] +impl From for GpuSpatialRelationPredicateWrapper { + fn from(pred: GpuSpatialRelationPredicate) -> Self { + match pred { + GpuSpatialRelationPredicate::Equals => GpuSpatialRelationPredicateWrapper::Equals, + GpuSpatialRelationPredicate::Disjoint => GpuSpatialRelationPredicateWrapper::Disjoint, + GpuSpatialRelationPredicate::Touches => GpuSpatialRelationPredicateWrapper::Touches, + GpuSpatialRelationPredicate::Contains => GpuSpatialRelationPredicateWrapper::Contains, + GpuSpatialRelationPredicate::Covers => GpuSpatialRelationPredicateWrapper::Covers, + GpuSpatialRelationPredicate::Intersects => { + GpuSpatialRelationPredicateWrapper::Intersects + } + GpuSpatialRelationPredicate::Within => GpuSpatialRelationPredicateWrapper::Within, + GpuSpatialRelationPredicate::CoveredBy => GpuSpatialRelationPredicateWrapper::CoveredBy, + } + } +} + +/// Global shared GpuSpatialRuntime. Building an instance is expensive, so we share it across all GpuSpatial instances. +#[cfg(gpu_available)] +static GLOBAL_GPUSPATIAL_RUNTIME: Mutex>>> = + Mutex::new(None); /// High-level wrapper for GPU spatial operations -pub struct GpuSpatialContext { +pub struct GpuSpatial { + #[cfg(gpu_available)] + runtime: Option>>, #[cfg(gpu_available)] - joiner: Option, + index: Option, #[cfg(gpu_available)] - context: Option, - initialized: bool, + refiner: Option, +} + +pub struct GpuSpatialOptions { + pub cuda_use_memory_pool: bool, + pub cuda_memory_pool_init_percent: i32, + pub concurrency: u32, + pub device_id: i32, + pub compress_bvh: bool, + pub pipeline_batches: u32, } -impl GpuSpatialContext { +impl GpuSpatial { pub fn new() -> Result { #[cfg(not(gpu_available))] { @@ -77,197 +152,480 @@ impl GpuSpatialContext { #[cfg(gpu_available)] { Ok(Self { - joiner: None, - context: None, - initialized: false, + runtime: None, + index: None, + refiner: None, }) } } - pub fn init(&mut self) -> Result<()> { + pub fn init(&mut self, options: GpuSpatialOptions) -> Result<()> { #[cfg(not(gpu_available))] { + let _ = options; Err(GpuSpatialError::GpuNotAvailable) } #[cfg(gpu_available)] { - let mut joiner = GpuSpatialJoinerWrapper::new(); - // Get PTX path from OUT_DIR - let out_path = std::path::PathBuf::from(env!("OUT_DIR")); - let ptx_root = out_path.join("share/gpuspatial/shaders"); - let ptx_root_str = ptx_root - .to_str() - .ok_or_else(|| GpuSpatialError::Init("Invalid PTX path".to_string()))?; - - // Initialize with concurrency of 1 for now - joiner.init(1, ptx_root_str)?; - - // Create context - let mut ctx = GpuSpatialJoinerContext { - last_error: std::ptr::null(), - private_data: std::ptr::null_mut(), - build_indices: std::ptr::null_mut(), - stream_indices: std::ptr::null_mut(), - }; - joiner.create_context(&mut ctx); + // Acquire the lock for the global shared runtime + let mut global_runtime_guard = GLOBAL_GPUSPATIAL_RUNTIME.lock().unwrap(); + + // Initialize the global runtime if it hasn't been initialized yet + if global_runtime_guard.is_none() { + // Get PTX path from OUT_DIR + let out_path = std::path::PathBuf::from(env!("OUT_DIR")); + let ptx_root = out_path.join("share/gpuspatial/shaders"); + let ptx_root_str = ptx_root + .to_str() + .ok_or_else(|| GpuSpatialError::Init("Invalid PTX path".to_string()))?; + + let runtime = GpuSpatialRuntimeWrapper::try_new( + options.device_id, + ptx_root_str, + options.cuda_use_memory_pool, + options.cuda_memory_pool_init_percent, + )?; + *global_runtime_guard = Some(Arc::new(Mutex::new(runtime))); + } + + // Get a clone of the Arc to the shared runtime + // safe to unwrap here because we just ensured it is Some + let runtime_ref = global_runtime_guard.as_ref().unwrap().clone(); + // Assign to self + self.runtime = Some(runtime_ref); + + let index = GpuSpatialIndexFloat2DWrapper::try_new( + self.runtime.as_ref().unwrap(), + options.concurrency, + )?; + + self.index = Some(index); + + let refiner = GpuSpatialRefinerWrapper::try_new( + self.runtime.as_ref().unwrap(), + options.concurrency, + options.compress_bvh, + options.pipeline_batches, + )?; + self.refiner = Some(refiner); - self.joiner = Some(joiner); - self.context = Some(ctx); - self.initialized = true; Ok(()) } } - #[cfg(gpu_available)] - pub fn get_joiner_mut(&mut self) -> Option<&mut GpuSpatialJoinerWrapper> { - self.joiner.as_mut() + pub fn is_gpu_available() -> bool { + #[cfg(not(gpu_available))] + { + false + } + #[cfg(gpu_available)] + { + let nvml = match Nvml::init() { + Ok(instance) => instance, + Err(_) => return false, + }; + + // Check if the device count is greater than zero + match nvml.device_count() { + Ok(count) => count > 0, + Err(_) => false, + } + } } - #[cfg(gpu_available)] - pub fn get_context_mut(&mut self) -> Option<&mut GpuSpatialJoinerContext> { - self.context.as_mut() + /// Clear previous build data + pub fn index_clear(&mut self) -> Result<()> { + #[cfg(not(gpu_available))] + { + Err(GpuSpatialError::GpuNotAvailable) + } + #[cfg(gpu_available)] + { + let index = self + .index + .as_mut() + .ok_or_else(|| GpuSpatialError::Init("GPU index is not available".into()))?; + + // Clear previous build data + index.clear(); + Ok(()) + } } - pub fn is_initialized(&self) -> bool { - self.initialized + pub fn index_push_build(&mut self, rects: &[Rect]) -> Result<()> { + #[cfg(not(gpu_available))] + { + let _ = rects; + Err(GpuSpatialError::GpuNotAvailable) + } + #[cfg(gpu_available)] + { + let index = self + .index + .as_mut() + .ok_or_else(|| GpuSpatialError::Init("GPU index not available".into()))?; + + unsafe { index.push_build(rects.as_ptr() as *const f32, rects.len() as u32) } + } + } + + pub fn index_finish_building(&mut self) -> Result<()> { + #[cfg(not(gpu_available))] + return Err(GpuSpatialError::GpuNotAvailable); + + #[cfg(gpu_available)] + self.index + .as_mut() + .ok_or_else(|| GpuSpatialError::Init("GPU index not available".into()))? + .finish_building() } - /// Perform spatial join between two geometry arrays - pub fn spatial_join( - &mut self, - left_geom: arrow_array::ArrayRef, - right_geom: arrow_array::ArrayRef, - predicate: SpatialPredicate, - ) -> Result<(Vec, Vec)> { + pub fn probe(&self, rects: &[Rect]) -> Result<(Vec, Vec)> { #[cfg(not(gpu_available))] { - let _ = (left_geom, right_geom, predicate); + let _ = rects; Err(GpuSpatialError::GpuNotAvailable) } #[cfg(gpu_available)] { - if !self.initialized { - return Err(GpuSpatialError::Init("Context not initialized".into())); - } + let index = self + .index + .as_ref() + .ok_or_else(|| GpuSpatialError::Init("GPU index not available".into()))?; - let joiner = self - .joiner - .as_mut() - .ok_or_else(|| GpuSpatialError::Init("GPU joiner not available".into()))?; + let mut ctx = SedonaSpatialIndexContext { + private_data: std::ptr::null_mut(), + }; + index.create_context(&mut ctx); - // Clear previous build data - joiner.clear(); - - // Push build data (left side) - log::info!( - "DEBUG: Pushing {} geometries to GPU (build side)", - left_geom.len() - ); - log::info!("DEBUG: Left array data type: {:?}", left_geom.data_type()); - if let Some(binary_arr) = left_geom - .as_any() - .downcast_ref::() - { - log::info!("DEBUG: Left binary array has {} values", binary_arr.len()); - if binary_arr.len() > 0 { - let first_wkb = binary_arr.value(0); - log::info!( - "DEBUG: First left WKB length: {}, first bytes: {:?}", - first_wkb.len(), - &first_wkb[..8.min(first_wkb.len())] - ); + let result = (|| -> Result<(Vec, Vec)> { + unsafe { + // If this fails, it returns Err from the *closure*, not the function + index.probe(&mut ctx, rects.as_ptr() as *const f32, rects.len() as u32)?; } - } - joiner.push_build(&left_geom, 0, left_geom.len() as i64)?; - joiner.finish_building()?; + // Copy results + let build_indices = index.get_build_indices_buffer(&mut ctx).to_vec(); + let probe_indices = index.get_probe_indices_buffer(&mut ctx).to_vec(); - // Recreate context after building (required by libgpuspatial) - let mut new_context = libgpuspatial_glue_bindgen::GpuSpatialJoinerContext { - last_error: std::ptr::null(), - private_data: std::ptr::null_mut(), - build_indices: std::ptr::null_mut(), - stream_indices: std::ptr::null_mut(), - }; - joiner.create_context(&mut new_context); - self.context = Some(new_context); - let context = self.context.as_mut().unwrap(); - // Push stream data (right side) and perform join - let gpu_predicate = predicate.into(); - joiner.push_stream( - context, - &right_geom, - 0, - right_geom.len() as i64, - gpu_predicate, - 0, // array_index_offset - )?; + Ok((build_indices, probe_indices)) + })(); - // Get results - let build_indices = joiner.get_build_indices_buffer(context).to_vec(); - let stream_indices = joiner.get_stream_indices_buffer(context).to_vec(); + index.destroy_context(&mut ctx); - Ok((build_indices, stream_indices)) + result } } -} -/// Spatial predicates for GPU operations -#[repr(u32)] -#[derive(Debug, PartialEq, Copy, Clone)] -pub enum SpatialPredicate { - Equals = 0, - Disjoint = 1, - Touches = 2, - Contains = 3, - Covers = 4, - Intersects = 5, - Within = 6, - CoveredBy = 7, -} + pub fn refiner_clear(&mut self) -> Result<()> { + #[cfg(not(gpu_available))] + { + Err(GpuSpatialError::GpuNotAvailable) + } + #[cfg(gpu_available)] + { + let refiner = self + .refiner + .as_mut() + .ok_or_else(|| GpuSpatialError::Init("GPU refiner is not available".into()))?; -#[cfg(gpu_available)] -impl From for GpuSpatialPredicateWrapper { - fn from(pred: SpatialPredicate) -> Self { - match pred { - SpatialPredicate::Equals => GpuSpatialPredicateWrapper::Equals, - SpatialPredicate::Disjoint => GpuSpatialPredicateWrapper::Disjoint, - SpatialPredicate::Touches => GpuSpatialPredicateWrapper::Touches, - SpatialPredicate::Contains => GpuSpatialPredicateWrapper::Contains, - SpatialPredicate::Covers => GpuSpatialPredicateWrapper::Covers, - SpatialPredicate::Intersects => GpuSpatialPredicateWrapper::Intersects, - SpatialPredicate::Within => GpuSpatialPredicateWrapper::Within, - SpatialPredicate::CoveredBy => GpuSpatialPredicateWrapper::CoveredBy, + // Clear previous build data + refiner.clear(); + Ok(()) } } -} -// Cleanup implementation -impl Drop for GpuSpatialContext { - fn drop(&mut self) { + pub fn refiner_push_build(&mut self, array: &arrow_array::ArrayRef) -> Result<()> { + #[cfg(not(gpu_available))] + { + let _ = array; + Err(GpuSpatialError::GpuNotAvailable) + } #[cfg(gpu_available)] { - if let (Some(mut joiner), Some(mut ctx)) = (self.joiner.take(), self.context.take()) { - joiner.destroy_context(&mut ctx); - joiner.release(); - } + let refiner = self + .refiner + .as_ref() + .ok_or_else(|| GpuSpatialError::Init("GPU refiner not available".into()))?; + + refiner.push_build(array) + } + } + + pub fn refiner_finish_building(&mut self) -> Result<()> { + #[cfg(not(gpu_available))] + { + Err(GpuSpatialError::GpuNotAvailable) + } + #[cfg(gpu_available)] + { + let refiner = self + .refiner + .as_mut() + .ok_or_else(|| GpuSpatialError::Init("GPU refiner not available".into()))?; + + refiner.finish_building() + } + } + + pub fn refine_loaded( + &self, + probe_array: &arrow_array::ArrayRef, + predicate: GpuSpatialRelationPredicate, + build_indices: &mut Vec, + probe_indices: &mut Vec, + ) -> Result<()> { + #[cfg(not(gpu_available))] + { + let _ = (probe_array, predicate, build_indices, probe_indices); + Err(GpuSpatialError::GpuNotAvailable) + } + #[cfg(gpu_available)] + { + let refiner = self + .refiner + .as_ref() + .ok_or_else(|| GpuSpatialError::Init("GPU refiner not available".into()))?; + + refiner.refine_loaded( + probe_array, + GpuSpatialRelationPredicateWrapper::from(predicate), + build_indices, + probe_indices, + ) + } + } + + pub fn refine( + &self, + array1: &arrow_array::ArrayRef, + array2: &arrow_array::ArrayRef, + predicate: GpuSpatialRelationPredicate, + indices1: &mut Vec, + indices2: &mut Vec, + ) -> Result<()> { + #[cfg(not(gpu_available))] + { + let _ = (array1, array2, predicate, indices1, indices2); + Err(GpuSpatialError::GpuNotAvailable) + } + #[cfg(gpu_available)] + { + let refiner = self + .refiner + .as_ref() + .ok_or_else(|| GpuSpatialError::Init("GPU refiner not available".into()))?; + + refiner.refine( + array1, + array2, + GpuSpatialRelationPredicateWrapper::from(predicate), + indices1, + indices2, + ) } } } +#[cfg(gpu_available)] #[cfg(test)] mod tests { use super::*; + use geo::{BoundingRect, Intersects, Point, Polygon}; + use sedona_expr::scalar_udf::SedonaScalarUDF; + use sedona_geos::register::scalar_kernels; + use sedona_schema::crs::lnglat; + use sedona_schema::datatypes::{Edges, SedonaType, WKB_GEOMETRY}; + use sedona_testing::create::create_array_storage; + use sedona_testing::testers::ScalarUdfTester; + use wkt::TryFromWkt; + + pub fn find_intersection_pairs( + vec_a: &[Rect], + vec_b: &[Rect], + ) -> (Vec, Vec) { + let mut ids_a = Vec::new(); + let mut ids_b = Vec::new(); + + // Iterate through A with index 'i' + for (i, rect_a) in vec_a.iter().enumerate() { + // Only proceed if 'a' exists + // Iterate through B with index 'j' + for (j, rect_b) in vec_b.iter().enumerate() { + // Check if 'b' exists and intersects 'a' + if rect_a.intersects(rect_b) { + ids_a.push(i as u32); + ids_b.push(j as u32); + } + } + } + (ids_a, ids_b) + } #[test] - fn test_context_creation() { - let ctx = GpuSpatialContext::new(); - #[cfg(gpu_available)] - assert!(ctx.is_ok()); - #[cfg(not(gpu_available))] - assert!(ctx.is_err()); + fn test_spatial_index() { + let mut gs = GpuSpatial::new().unwrap(); + let options = GpuSpatialOptions { + concurrency: 1, + device_id: 0, + compress_bvh: false, + pipeline_batches: 1, + cuda_use_memory_pool: true, + cuda_memory_pool_init_percent: 10, + }; + gs.init(options).expect("Failed to initialize GpuSpatial"); + + let polygon_values = &[ + Some("POLYGON ((30 10, 40 40, 20 40, 10 20, 30 10))"), + Some("POLYGON ((35 10, 45 45, 15 40, 10 20, 35 10), (20 30, 35 35, 30 20, 20 30))"), + Some("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0), (2 2, 3 2, 3 3, 2 3, 2 2), (6 6, 8 6, 8 8, 6 8, 6 6))"), + Some("POLYGON ((30 0, 60 20, 50 50, 10 50, 0 20, 30 0), (20 30, 25 40, 15 40, 20 30), (30 30, 35 40, 25 40, 30 30), (40 30, 45 40, 35 40, 40 30))"), + Some("POLYGON ((40 0, 50 30, 80 20, 90 70, 60 90, 30 80, 20 40, 40 0), (50 20, 65 30, 60 50, 45 40, 50 20), (30 60, 50 70, 45 80, 30 60))"), + ]; + let rects: Vec> = polygon_values + .iter() + .filter_map(|opt_wkt| { + let wkt_str = opt_wkt.as_ref()?; + let polygon: Polygon = Polygon::try_from_wkt_str(wkt_str).ok()?; + + polygon.bounding_rect() + }) + .collect(); + gs.index_push_build(&rects) + .expect("Failed to push build data"); + gs.index_finish_building() + .expect("Failed to finish building"); + let point_values = &[ + Some("POINT (30 20)"), + Some("POINT (20 20)"), + Some("POINT (1 1)"), + Some("POINT (70 70)"), + Some("POINT (55 35)"), + ]; + let points: Vec> = point_values + .iter() + .map(|opt_wkt| -> Rect { + let wkt_str = opt_wkt.unwrap(); + let point: Point = Point::try_from_wkt_str(wkt_str).ok().unwrap(); + point.bounding_rect() + }) + .collect(); + let (mut build_indices, mut probe_indices) = gs.probe(&points).unwrap(); + build_indices.sort(); + probe_indices.sort(); + + let (mut ans_build_indices, mut ans_probe_indices) = + find_intersection_pairs(&rects, &points); + + ans_build_indices.sort(); + ans_probe_indices.sort(); + + assert_eq!(build_indices, ans_build_indices); + assert_eq!(probe_indices, ans_probe_indices); + } + + #[test] + fn test_spatial_refiner() { + let mut gs = GpuSpatial::new().unwrap(); + let options = GpuSpatialOptions { + concurrency: 1, + device_id: 0, + compress_bvh: false, + pipeline_batches: 1, + cuda_use_memory_pool: true, + cuda_memory_pool_init_percent: 10, + }; + gs.init(options).expect("Failed to initialize GpuSpatial"); + + let polygon_values = &[ + Some("POLYGON ((30 10, 40 40, 20 40, 10 20, 30 10))"), + Some("POLYGON ((35 10, 45 45, 15 40, 10 20, 35 10), (20 30, 35 35, 30 20, 20 30))"), + Some("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0), (2 2, 3 2, 3 3, 2 3, 2 2), (6 6, 8 6, 8 8, 6 8, 6 6))"), + Some("POLYGON ((30 0, 60 20, 50 50, 10 50, 0 20, 30 0), (20 30, 25 40, 15 40, 20 30), (30 30, 35 40, 25 40, 30 30), (40 30, 45 40, 35 40, 40 30))"), + Some("POLYGON ((40 0, 50 30, 80 20, 90 70, 60 90, 30 80, 20 40, 40 0), (50 20, 65 30, 60 50, 45 40, 50 20), (30 60, 50 70, 45 80, 30 60))"), + ]; + let polygons = create_array_storage(polygon_values, &WKB_GEOMETRY); + + let rects: Vec> = polygon_values + .iter() + .map(|opt_wkt| -> Rect { + let wkt_str = opt_wkt.unwrap(); + let polygon: Polygon = Polygon::try_from_wkt_str(wkt_str).ok().unwrap(); + polygon.bounding_rect().unwrap() + }) + .collect(); + gs.index_push_build(&rects) + .expect("Failed to push build data"); + gs.index_finish_building() + .expect("Failed to finish building"); + let point_values = &[ + Some("POINT (30 20)"), + Some("POINT (20 20)"), + Some("POINT (1 1)"), + Some("POINT (70 70)"), + Some("POINT (55 35)"), + ]; + let points = create_array_storage(point_values, &WKB_GEOMETRY); + let point_rects: Vec> = point_values + .iter() + .map(|wkt| -> Rect { + let wkt_str = wkt.unwrap(); + + let point: Point = Point::try_from_wkt_str(wkt_str).unwrap(); + + point.bounding_rect() + }) + .collect(); + let (mut build_indices, mut probe_indices) = gs.probe(&point_rects).unwrap(); + + gs.refine( + &polygons, + &points, + GpuSpatialRelationPredicate::Intersects, + &mut build_indices, + &mut probe_indices, + ) + .expect("Failed to refine results"); + + build_indices.sort(); + probe_indices.sort(); + + let kernels = scalar_kernels(); + + // Iterate through the vector and find the one named "st_intersects" + let st_intersects = kernels + .into_iter() + .find(|(name, _)| *name == "st_intersects") + .map(|(_, kernel_ref)| kernel_ref) + .unwrap(); + + let sedona_type = SedonaType::Wkb(Edges::Planar, lnglat()); + let udf = SedonaScalarUDF::from_impl("st_intersects", st_intersects); + let tester = + ScalarUdfTester::new(udf.into(), vec![sedona_type.clone(), sedona_type.clone()]); + + let mut ans_build_indices: Vec = Vec::new(); + let mut ans_probe_indices: Vec = Vec::new(); + + for (poly_index, poly) in polygon_values.iter().enumerate() { + for (point_index, point) in point_values.iter().enumerate() { + let result = tester + .invoke_scalar_scalar(poly.unwrap(), point.unwrap()) + .unwrap(); + if result == true.into() { + ans_build_indices.push(poly_index as u32); + ans_probe_indices.push(point_index as u32); + } + } + } + + ans_build_indices.sort(); + ans_probe_indices.sort(); + + assert_eq!(build_indices, ans_build_indices); + assert_eq!(probe_indices, ans_probe_indices); } } diff --git a/c/sedona-libgpuspatial/src/libgpuspatial.rs b/c/sedona-libgpuspatial/src/libgpuspatial.rs index 414b92e09..5723ac35c 100644 --- a/c/sedona-libgpuspatial/src/libgpuspatial.rs +++ b/c/sedona-libgpuspatial/src/libgpuspatial.rs @@ -17,106 +17,143 @@ use crate::error::GpuSpatialError; use crate::libgpuspatial_glue_bindgen::*; -use arrow_array::{ffi::FFI_ArrowArray, ArrayRef}; +use arrow_array::{ffi::FFI_ArrowArray, Array, ArrayRef}; +use arrow_schema::ffi::FFI_ArrowSchema; use std::convert::TryFrom; use std::ffi::CString; use std::mem::transmute; -use std::os::raw::{c_uint, c_void}; +use std::os::raw::c_uint; +use std::sync::{Arc, Mutex}; -pub struct GpuSpatialJoinerWrapper { - joiner: GpuSpatialJoiner, +pub struct GpuSpatialRuntimeWrapper { + runtime: GpuSpatialRuntime, } -#[repr(u32)] -#[derive(Debug, PartialEq, Copy, Clone)] -pub enum GpuSpatialPredicateWrapper { - Equals = 0, - Disjoint = 1, - Touches = 2, - Contains = 3, - Covers = 4, - Intersects = 5, - Within = 6, - CoveredBy = 7, -} +impl GpuSpatialRuntimeWrapper { + /// # Initializes the GpuSpatialRuntime + /// This function should only be called once per engine instance. + /// # Arguments + /// * `device_id` - The GPU device ID to use. + /// * `ptx_root` - The root directory for PTX files. + pub fn try_new( + device_id: i32, + ptx_root: &str, + use_cuda_memory_pool: bool, + cuda_memory_pool_init_precent: i32, + ) -> Result { + let mut runtime = GpuSpatialRuntime { + init: None, + release: None, + get_last_error: None, + private_data: std::ptr::null_mut(), + }; -impl TryFrom for GpuSpatialPredicateWrapper { - type Error = &'static str; + unsafe { + // Set function pointers to the C functions + GpuSpatialRuntimeCreate(&mut runtime); + } - fn try_from(v: c_uint) -> Result { - match v { - 0 => Ok(GpuSpatialPredicateWrapper::Equals), - 1 => Ok(GpuSpatialPredicateWrapper::Disjoint), - 2 => Ok(GpuSpatialPredicateWrapper::Touches), - 3 => Ok(GpuSpatialPredicateWrapper::Contains), - 4 => Ok(GpuSpatialPredicateWrapper::Covers), - 5 => Ok(GpuSpatialPredicateWrapper::Intersects), - 6 => Ok(GpuSpatialPredicateWrapper::Within), - 7 => Ok(GpuSpatialPredicateWrapper::CoveredBy), - _ => Err("Invalid GpuSpatialPredicate value"), + if let Some(init_fn) = runtime.init { + let c_ptx_root = CString::new(ptx_root).expect("CString::new failed"); + + let mut config = GpuSpatialRuntimeConfig { + device_id, + ptx_root: c_ptx_root.as_ptr(), + use_cuda_memory_pool, + cuda_memory_pool_init_precent, + }; + + // This is an unsafe call because it's calling a C function from the bindings. + unsafe { + if init_fn(&runtime as *const _ as *mut _, &mut config) != 0 { + let error_message = + runtime.get_last_error.unwrap()(&runtime as *const _ as *mut _); + let c_str = std::ffi::CStr::from_ptr(error_message); + let error_string = c_str.to_string_lossy().into_owned(); + return Err(GpuSpatialError::Init(error_string)); + } + } } - } -} -impl Default for GpuSpatialJoinerWrapper { - fn default() -> Self { - Self::new() + Ok(GpuSpatialRuntimeWrapper { runtime }) } } -impl GpuSpatialJoinerWrapper { - pub fn new() -> Self { - GpuSpatialJoinerWrapper { - joiner: GpuSpatialJoiner { +impl Default for GpuSpatialRuntimeWrapper { + fn default() -> Self { + GpuSpatialRuntimeWrapper { + runtime: GpuSpatialRuntime { init: None, - clear: None, - create_context: None, - destroy_context: None, - push_build: None, - finish_building: None, - push_stream: None, - get_build_indices_buffer: None, - get_stream_indices_buffer: None, release: None, + get_last_error: None, private_data: std::ptr::null_mut(), - last_error: std::ptr::null(), }, } } +} +impl Drop for GpuSpatialRuntimeWrapper { + fn drop(&mut self) { + // Call the release function if it exists + if let Some(release_fn) = self.runtime.release { + unsafe { + release_fn(&mut self.runtime as *mut _); + } + } + } +} + +pub struct GpuSpatialIndexFloat2DWrapper { + index: SedonaFloatIndex2D, + _runtime: Arc>, // Keep a reference to the RT engine to ensure it lives as long as the index +} + +impl GpuSpatialIndexFloat2DWrapper { /// # Initializes the GpuSpatialJoiner /// This function should only be called once per joiner instance. /// /// # Arguments + /// * `runtime` - The GPUSpatial runtime to use for GPU operations. /// * `concurrency` - How many threads will call the joiner concurrently. - /// * `ptx_root` - The root directory for PTX files. - pub fn init(&mut self, concurrency: u32, ptx_root: &str) -> Result<(), GpuSpatialError> { - let joiner_ptr: *mut GpuSpatialJoiner = &mut self.joiner; + pub fn try_new( + runtime: &Arc>, + concurrency: u32, + ) -> Result { + let mut index = SedonaFloatIndex2D { + clear: None, + create_context: None, + destroy_context: None, + push_build: None, + finish_building: None, + probe: None, + get_build_indices_buffer: None, + get_probe_indices_buffer: None, + get_last_error: None, + context_get_last_error: None, + release: None, + private_data: std::ptr::null_mut(), + }; + let mut engine_guard = runtime + .lock() + .map_err(|_| GpuSpatialError::Init("Failed to acquire mutex lock".to_string()))?; + let config = GpuSpatialIndexConfig { + runtime: &mut engine_guard.runtime, + concurrency, + }; unsafe { // Set function pointers to the C functions - GpuSpatialJoinerCreate(joiner_ptr); - } - - if let Some(init_fn) = self.joiner.init { - let c_ptx_root = CString::new(ptx_root).expect("CString::new failed"); - - let mut config = GpuSpatialJoinerConfig { - concurrency, - ptx_root: c_ptx_root.as_ptr(), - }; - - // This is an unsafe call because it's calling a C function from the bindings. - unsafe { - if init_fn(&self.joiner as *const _ as *mut _, &mut config) != 0 { - let error_message = self.joiner.last_error; - let c_str = std::ffi::CStr::from_ptr(error_message); - let error_string = c_str.to_string_lossy().into_owned(); - return Err(GpuSpatialError::Init(error_string)); - } + if GpuSpatialIndexFloat2DCreate(&mut index, &config) != 0 { + let error_message = index.get_last_error.unwrap()(&runtime as *const _ as *mut _); + let c_str = std::ffi::CStr::from_ptr(error_message); + let error_string = c_str.to_string_lossy().into_owned(); + return Err(GpuSpatialError::Init(error_string)); } } - Ok(()) + Ok(GpuSpatialIndexFloat2DWrapper { + index, + _runtime: runtime.clone(), + }) } /// # Clears the GpuSpatialJoiner @@ -126,69 +163,43 @@ impl GpuSpatialJoinerWrapper { /// instead of building a new one because creating a new joiner is expensive. /// **This method is not thread-safe and should be called from a single thread.** pub fn clear(&mut self) { - if let Some(clear_fn) = self.joiner.clear { + if let Some(clear_fn) = self.index.clear { unsafe { - clear_fn(&mut self.joiner as *mut _); + clear_fn(&mut self.index as *mut _); } } } - /// # Pushes an array of WKBs to the build side of the joiner + /// # Pushes an array of rectangles to the build side of the joiner /// This function can be called multiple times to push multiple arrays. - /// The joiner will internally parse the WKBs and build a spatial index. + /// The joiner will internally parse the rectangles and build a spatial index. /// After pushing all build data, you must call `finish_building()` to build the /// spatial index. /// **This method is not thread-safe and should be called from a single thread.** /// # Arguments - /// * `array` - The array of WKBs to push. - /// * `offset` - The offset of the array to push. - /// * `length` - The length of the array to push. - pub fn push_build( + /// * `buf` - The array pointer to the rectangles to push. + /// * `n_rects` - The number of rectangles in the array. + /// # Safety + /// This function is unsafe because it takes a raw pointer to the rectangles. + /// + pub unsafe fn push_build( &mut self, - array: &ArrayRef, - offset: i64, - length: i64, + buf: *const f32, + n_rects: u32, ) -> Result<(), GpuSpatialError> { - log::info!( - "DEBUG FFI: push_build called with offset={}, length={}", - offset, - length - ); - log::info!( - "DEBUG FFI: Array length={}, null_count={}", - array.len(), - array.null_count() - ); - - // 1. Convert the single ArrayRef to its FFI representation - let (ffi_array, _) = arrow_array::ffi::to_ffi(&array.to_data())?; - - log::info!("DEBUG FFI: FFI conversion successful"); - log::info!("DEBUG FFI: FFI array null_count={}", ffi_array.null_count()); - - // 2. Get the raw pointer to the FFI_ArrowArray struct - // let arrow_ptr = &mut ffi_array as *mut FFI_ArrowArray as *mut ArrowArray; + log::debug!("DEBUG FFI: push_build called with length={}", n_rects); - if let Some(push_build_fn) = self.joiner.push_build { + if let Some(push_build_fn) = self.index.push_build { unsafe { - let ffi_array_ptr: *const ArrowArray = - transmute(&ffi_array as *const FFI_ArrowArray); - log::info!("DEBUG FFI: Calling C++ push_build function"); - if push_build_fn( - &mut self.joiner as *mut _, - std::ptr::null_mut(), // schema is unused currently - ffi_array_ptr as *mut _, - offset, - length, - ) != 0 - { - let error_message = self.joiner.last_error; + if push_build_fn(&mut self.index as *mut _, buf, n_rects) != 0 { + let error_message = + self.index.get_last_error.unwrap()(&mut self.index as *mut _); let c_str = std::ffi::CStr::from_ptr(error_message); let error_string = c_str.to_string_lossy().into_owned(); log::error!("DEBUG FFI: push_build failed: {}", error_string); return Err(GpuSpatialError::PushBuild(error_string)); } - log::info!("DEBUG FFI: push_build C++ call succeeded"); + log::debug!("DEBUG FFI: push_build C++ call succeeded"); } } Ok(()) @@ -201,10 +212,11 @@ impl GpuSpatialJoinerWrapper { /// for spatial join operations. /// **This method is not thread-safe and should be called from a single thread.** pub fn finish_building(&mut self) -> Result<(), GpuSpatialError> { - if let Some(finish_building_fn) = self.joiner.finish_building { + if let Some(finish_building_fn) = self.index.finish_building { unsafe { - if finish_building_fn(&mut self.joiner as *mut _) != 0 { - let error_message = self.joiner.last_error; + if finish_building_fn(&mut self.index as *mut _) != 0 { + let error_message = + self.index.get_last_error.unwrap()(&mut self.index as *mut _); let c_str = std::ffi::CStr::from_ptr(error_message); let error_string = c_str.to_string_lossy().into_owned(); return Err(GpuSpatialError::FinishBuild(error_string)); @@ -224,89 +236,73 @@ impl GpuSpatialJoinerWrapper { /// The context can be destroyed by calling the `destroy_context` function pointer in the `GpuSpatialJoiner` struct. /// The context should be destroyed before destroying the joiner. /// **This method is thread-safe.** - pub fn create_context(&mut self, ctx: &mut GpuSpatialJoinerContext) { - if let Some(create_context_fn) = self.joiner.create_context { + pub fn create_context(&self, ctx: &mut SedonaSpatialIndexContext) { + if let Some(create_context_fn) = self.index.create_context { unsafe { - create_context_fn(&mut self.joiner as *mut _, ctx as *mut _); + // Cast the shared reference to a raw pointer, then to a mutable raw pointer + create_context_fn(ctx as *mut _); } } } - pub fn destroy_context(&mut self, ctx: &mut GpuSpatialJoinerContext) { - if let Some(destroy_context_fn) = self.joiner.destroy_context { + pub fn destroy_context(&self, ctx: &mut SedonaSpatialIndexContext) { + if let Some(destroy_context_fn) = self.index.destroy_context { unsafe { destroy_context_fn(ctx as *mut _); } } } - pub fn push_stream( - &mut self, - ctx: &mut GpuSpatialJoinerContext, - array: &ArrayRef, - offset: i64, - length: i64, - predicate: GpuSpatialPredicateWrapper, - array_index_offset: i32, + /// # Probes an array of rectangles against the built spatial index + /// This function probes an array of rectangles against the spatial index built + /// using `push_build()` and `finish_building()`. It finds all pairs of rectangles + /// that satisfy the spatial relation defined by the index. + /// The results are stored in the context passed to the function. + /// **This method is thread-safe if each thread uses its own context.** + /// # Arguments + /// * `ctx` - The context for the thread performing the spatial join. + /// * `buf` - A pointer to the array of rectangles to probe. + /// * `n_rects` - The number of rectangles in the array. + /// # Safety + /// This function is unsafe because it takes a raw pointer to the rectangles. + pub unsafe fn probe( + &self, + ctx: &mut SedonaSpatialIndexContext, + buf: *const f32, + n_rects: u32, ) -> Result<(), GpuSpatialError> { - log::info!( - "DEBUG FFI: push_stream called with offset={}, length={}, predicate={:?}", - offset, - length, - predicate - ); - log::info!( - "DEBUG FFI: Array length={}, null_count={}", - array.len(), - array.null_count() - ); - - // 1. Convert the single ArrayRef to its FFI representation - let (ffi_array, _) = arrow_array::ffi::to_ffi(&array.to_data())?; - - log::info!("DEBUG FFI: FFI conversion successful"); - log::info!("DEBUG FFI: FFI array null_count={}", ffi_array.null_count()); - - // 2. Get the raw pointer to the FFI_ArrowArray struct - // let arrow_ptr = &mut ffi_array as *mut FFI_ArrowArray as *mut ArrowArray; + log::debug!("DEBUG FFI: probe called with length={}", n_rects); - if let Some(push_stream_fn) = self.joiner.push_stream { + if let Some(probe_fn) = self.index.probe { unsafe { - let ffi_array_ptr: *const ArrowArray = - transmute(&ffi_array as *const FFI_ArrowArray); - log::info!("DEBUG FFI: Calling C++ push_stream function"); - if push_stream_fn( - &mut self.joiner as *mut _, + if probe_fn( + &self.index as *const _ as *mut _, ctx as *mut _, - std::ptr::null_mut(), // schema is unused currently - ffi_array_ptr as *mut _, - offset, - length, - predicate as c_uint, - array_index_offset, + buf, + n_rects, ) != 0 { - let error_message = ctx.last_error; + let error_message = self.index.context_get_last_error.unwrap()(ctx); let c_str = std::ffi::CStr::from_ptr(error_message); let error_string = c_str.to_string_lossy().into_owned(); - log::error!("DEBUG FFI: push_stream failed: {}", error_string); - return Err(GpuSpatialError::PushStream(error_string)); + log::error!("DEBUG FFI: probe failed: {}", error_string); + return Err(GpuSpatialError::Probe(error_string)); } - log::info!("DEBUG FFI: push_stream C++ call succeeded"); + log::debug!("DEBUG FFI: probe C++ call succeeded"); } } Ok(()) } - pub fn get_build_indices_buffer(&self, ctx: &mut GpuSpatialJoinerContext) -> &[u32] { - if let Some(get_build_indices_buffer_fn) = self.joiner.get_build_indices_buffer { - let mut build_indices_ptr: *mut c_void = std::ptr::null_mut(); + pub fn get_build_indices_buffer(&self, ctx: &mut SedonaSpatialIndexContext) -> &[u32] { + if let Some(get_build_indices_buffer_fn) = self.index.get_build_indices_buffer { + let mut build_indices_ptr: *mut u32 = std::ptr::null_mut(); let mut build_indices_len: u32 = 0; unsafe { get_build_indices_buffer_fn( ctx as *mut _, - &mut build_indices_ptr as *mut *mut c_void, + &mut build_indices_ptr as *mut *mut u32, &mut build_indices_len as *mut u32, ); @@ -331,179 +327,387 @@ impl GpuSpatialJoinerWrapper { &[] } - pub fn get_stream_indices_buffer(&self, ctx: &mut GpuSpatialJoinerContext) -> &[u32] { - if let Some(get_stream_indices_buffer_fn) = self.joiner.get_stream_indices_buffer { - let mut stream_indices_ptr: *mut c_void = std::ptr::null_mut(); - let mut stream_indices_len: u32 = 0; + pub fn get_probe_indices_buffer(&self, ctx: &mut SedonaSpatialIndexContext) -> &[u32] { + if let Some(get_probe_indices_buffer_fn) = self.index.get_probe_indices_buffer { + let mut probe_indices_ptr: *mut u32 = std::ptr::null_mut(); + let mut probe_indices_len: u32 = 0; unsafe { - get_stream_indices_buffer_fn( + get_probe_indices_buffer_fn( ctx as *mut _, - &mut stream_indices_ptr as *mut *mut c_void, - &mut stream_indices_len as *mut u32, + &mut probe_indices_ptr as *mut *mut u32, + &mut probe_indices_len as *mut u32, ); // Check length first - empty vectors return empty slice - if stream_indices_len == 0 { + if probe_indices_len == 0 { return &[]; } // Validate pointer (should not be null if length > 0) - if stream_indices_ptr.is_null() { + if probe_indices_ptr.is_null() { return &[]; } // Convert the raw pointer to a slice. This is safe to do because // we've validated the pointer is non-null and length is valid. - let typed_ptr = stream_indices_ptr as *const u32; + let typed_ptr = probe_indices_ptr as *const u32; // Safety: We've checked ptr is non-null and len > 0 - return std::slice::from_raw_parts(typed_ptr, stream_indices_len as usize); + return std::slice::from_raw_parts(typed_ptr, probe_indices_len as usize); } } &[] } +} - pub fn release(&mut self) { - // Call the release function if it exists - if let Some(release_fn) = self.joiner.release { - unsafe { - release_fn(&mut self.joiner as *mut _); - } +impl Default for GpuSpatialIndexFloat2DWrapper { + fn default() -> Self { + GpuSpatialIndexFloat2DWrapper { + index: SedonaFloatIndex2D { + clear: None, + create_context: None, + destroy_context: None, + push_build: None, + finish_building: None, + probe: None, + get_build_indices_buffer: None, + get_probe_indices_buffer: None, + get_last_error: None, + context_get_last_error: None, + release: None, + private_data: std::ptr::null_mut(), + }, + _runtime: Arc::new(Mutex::new(GpuSpatialRuntimeWrapper::default())), } } } -impl Drop for GpuSpatialJoinerWrapper { +impl Drop for GpuSpatialIndexFloat2DWrapper { fn drop(&mut self) { // Call the release function if it exists - if let Some(release_fn) = self.joiner.release { + if let Some(release_fn) = self.index.release { unsafe { - release_fn(&mut self.joiner as *mut _); + release_fn(&mut self.index as *mut _); } } } } -#[cfg(test)] -mod test { - use super::*; - use sedona_expr::scalar_udf::SedonaScalarUDF; - use sedona_geos::register::scalar_kernels; - use sedona_schema::crs::lnglat; - use sedona_schema::datatypes::{Edges, SedonaType, WKB_GEOMETRY}; - use sedona_testing::create::create_array_storage; - use sedona_testing::testers::ScalarUdfTester; - use std::env; - use std::path::PathBuf; - - #[test] - fn test_gpu_joiner_end2end() { - let mut joiner = GpuSpatialJoinerWrapper::new(); - - let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); - let ptx_root = out_path.join("share/gpuspatial/shaders"); - - joiner - .init( - 1, - ptx_root.to_str().expect("Failed to convert path to string"), - ) - .expect("Failed to init GpuSpatialJoiner"); - - let polygon_values = &[ - Some("POLYGON ((30 10, 40 40, 20 40, 10 20, 30 10))"), - Some("POLYGON ((35 10, 45 45, 15 40, 10 20, 35 10), (20 30, 35 35, 30 20, 20 30))"), - Some("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0), (2 2, 3 2, 3 3, 2 3, 2 2), (6 6, 8 6, 8 8, 6 8, 6 6))"), - Some("POLYGON ((30 0, 60 20, 50 50, 10 50, 0 20, 30 0), (20 30, 25 40, 15 40, 20 30), (30 30, 35 40, 25 40, 30 30), (40 30, 45 40, 35 40, 40 30))"), - Some("POLYGON ((40 0, 50 30, 80 20, 90 70, 60 90, 30 80, 20 40, 40 0), (50 20, 65 30, 60 50, 45 40, 50 20), (30 60, 50 70, 45 80, 30 60))"), - ]; - let polygons = create_array_storage(polygon_values, &WKB_GEOMETRY); - - // Let the gpusaptial joiner to parse WKBs and get building boxes - joiner - .push_build(&polygons, 0, polygons.len().try_into().unwrap()) - .expect("Failed to push building"); - // Build a spatial index for Build internally on GPU - joiner.finish_building().expect("Failed to finish building"); - - // Each thread that performs spatial joins should have its own context. - // The context is passed to PushStream calls to perform spatial joins. - let mut ctx = GpuSpatialJoinerContext { - last_error: std::ptr::null(), +#[repr(u32)] +#[derive(Debug, PartialEq, Copy, Clone)] +pub enum GpuSpatialRelationPredicateWrapper { + Equals = 0, + Disjoint = 1, + Touches = 2, + Contains = 3, + Covers = 4, + Intersects = 5, + Within = 6, + CoveredBy = 7, +} + +impl TryFrom for GpuSpatialRelationPredicateWrapper { + type Error = &'static str; + + fn try_from(v: c_uint) -> Result { + match v { + 0 => Ok(GpuSpatialRelationPredicateWrapper::Equals), + 1 => Ok(GpuSpatialRelationPredicateWrapper::Disjoint), + 2 => Ok(GpuSpatialRelationPredicateWrapper::Touches), + 3 => Ok(GpuSpatialRelationPredicateWrapper::Contains), + 4 => Ok(GpuSpatialRelationPredicateWrapper::Covers), + 5 => Ok(GpuSpatialRelationPredicateWrapper::Intersects), + 6 => Ok(GpuSpatialRelationPredicateWrapper::Within), + 7 => Ok(GpuSpatialRelationPredicateWrapper::CoveredBy), + _ => Err("Invalid GpuSpatialPredicate value"), + } + } +} + +pub struct GpuSpatialRefinerWrapper { + refiner: SedonaSpatialRefiner, + _runtime: Arc>, // Keep a reference to the RT engine to ensure it lives as long as the refiner +} + +impl GpuSpatialRefinerWrapper { + /// # Initializes the GpuSpatialJoiner + /// This function should only be called once per joiner instance. + /// + /// # Arguments + /// * `concurrency` - How many threads will call the joiner concurrently. + /// * `ptx_root` - The root directory for PTX files. + pub fn try_new( + runtime: &Arc>, + concurrency: u32, + compress_bvh: bool, + pipeline_batches: u32, + ) -> Result { + let mut refiner = SedonaSpatialRefiner { + clear: None, + push_build: None, + finish_building: None, + refine_loaded: None, + refine: None, + get_last_error: None, + release: None, private_data: std::ptr::null_mut(), - build_indices: std::ptr::null_mut(), - stream_indices: std::ptr::null_mut(), }; + let mut engine_guard = runtime + .lock() + .map_err(|_| GpuSpatialError::Init("Failed to acquire mutex lock".to_string()))?; + let config = GpuSpatialRefinerConfig { + runtime: &mut engine_guard.runtime, + concurrency, + compress_bvh, + pipeline_batches, + }; + unsafe { + // Set function pointers to the C functions + if GpuSpatialRefinerCreate(&mut refiner, &config) != 0 { + let error_message = refiner.get_last_error.unwrap()(&refiner as *const _ as *mut _); + let c_str = std::ffi::CStr::from_ptr(error_message); + let error_string = c_str.to_string_lossy().into_owned(); + return Err(GpuSpatialError::Init(error_string)); + } + } + Ok(GpuSpatialRefinerWrapper { + refiner, + _runtime: runtime.clone(), + }) + } + + pub fn clear(&self) { + log::debug!("DEBUG FFI: clear called"); + if let Some(clear_fn) = self.refiner.clear { + unsafe { + clear_fn(&self.refiner as *const _ as *mut _); + } + log::debug!("DEBUG FFI: clear completed"); + } + } - joiner.create_context(&mut ctx); - - let point_values = &[ - Some("POINT (30 20)"), // poly0 - Some("POINT (20 20)"), // poly1 - Some("POINT (1 1)"), // poly2 - Some("POINT (70 70)"), - Some("POINT (55 35)"), // poly4 - ]; - let points = create_array_storage(point_values, &WKB_GEOMETRY); - - // array_index_offset offsets the result of stream indices - let array_index_offset = 0; - joiner - .push_stream( - &mut ctx, - &points, - 0, - points.len().try_into().unwrap(), - GpuSpatialPredicateWrapper::Intersects, - array_index_offset, - ) - .expect("Failed to push building"); - - let build_indices = joiner.get_build_indices_buffer(&mut ctx); - let stream_indices = joiner.get_stream_indices_buffer(&mut ctx); - - let mut result_pairs: Vec<(u32, u32)> = Vec::new(); - - for (build_index, stream_index) in build_indices.iter().zip(stream_indices.iter()) { - result_pairs.push((*build_index, *stream_index)); + /// # Loads a build array into the GPU spatial refiner + /// This function loads an array of geometries into the GPU spatial refiner + /// for parsing and loading on the GPU side. + /// # Arguments + /// * `array` - The array of geometries to load. + /// # Returns + /// * `Result<(), GpuSpatialError>` - Ok if successful, Err if an error occurred. + pub fn push_build(&self, array: &ArrayRef) -> Result<(), GpuSpatialError> { + log::debug!("DEBUG FFI: push_build called with array={}", array.len(),); + + let (ffi_array, ffi_schema) = arrow_array::ffi::to_ffi(&array.to_data())?; + log::debug!("DEBUG FFI: FFI conversion successful"); + if let Some(load_fn) = self.refiner.push_build { + unsafe { + let ffi_array_ptr: *const ArrowArray = + transmute(&ffi_array as *const FFI_ArrowArray); + let ffi_schema_ptr: *const ArrowSchema = + transmute(&ffi_schema as *const FFI_ArrowSchema); + log::debug!("DEBUG FFI: Calling C++ refine function"); + let _new_len: u32 = 0; + if load_fn( + &self.refiner as *const _ as *mut _, + ffi_schema_ptr as *mut _, + ffi_array_ptr as *mut _, + ) != 0 + { + let error_message = + self.refiner.get_last_error.unwrap()(&self.refiner as *const _ as *mut _); + let c_str = std::ffi::CStr::from_ptr(error_message); + let error_string = c_str.to_string_lossy().into_owned(); + log::error!("DEBUG FFI: push_build failed: {}", error_string); + return Err(GpuSpatialError::PushBuild(error_string)); + } + log::debug!("DEBUG FFI: push_build C++ call succeeded"); + } + } + Ok(()) + } + + pub fn finish_building(&self) -> Result<(), GpuSpatialError> { + log::debug!("DEBUG FFI: finish_building called"); + + if let Some(finish_building_fn) = self.refiner.finish_building { + unsafe { + if finish_building_fn(&self.refiner as *const _ as *mut _) != 0 { + let error_message = + self.refiner.get_last_error.unwrap()(&self.refiner as *const _ as *mut _); + let c_str = std::ffi::CStr::from_ptr(error_message); + let error_string = c_str.to_string_lossy().into_owned(); + log::error!("DEBUG FFI: finish_building failed: {}", error_string); + return Err(GpuSpatialError::FinishBuild(error_string)); + } + log::debug!("DEBUG FFI: finish_building C++ call succeeded"); + } } + Ok(()) + } - let kernels = scalar_kernels(); - - // Iterate through the vector and find the one named "st_intersects" - let st_intersects = kernels - .into_iter() - .find(|(name, _)| *name == "st_intersects") - .map(|(_, kernel_ref)| kernel_ref) - .unwrap(); - - let sedona_type = SedonaType::Wkb(Edges::Planar, lnglat()); - let udf = SedonaScalarUDF::from_kernel("st_intersects", st_intersects); - let tester = - ScalarUdfTester::new(udf.into(), vec![sedona_type.clone(), sedona_type.clone()]); - - let mut answer_pairs: Vec<(u32, u32)> = Vec::new(); - - for (poly_index, poly) in polygon_values.iter().enumerate() { - for (point_index, point) in point_values.iter().enumerate() { - let result = tester - .invoke_scalar_scalar(poly.unwrap(), point.unwrap()) - .unwrap(); - if result == true.into() { - answer_pairs.push((poly_index as u32, point_index as u32)); + /// # Refines candidate pairs using the GPU spatial refiner + /// This function refines candidate pairs of geometries using the GPU spatial refiner. + /// It takes the probe side array of geometries and a predicate, and outputs the refined pairs of + /// indices that satisfy the predicate. + /// # Arguments + /// * `array` - The array of geometries on the probe side. + /// * `predicate` - The spatial relation predicate to use for refinement. + /// * `build_indices` - The input/output vector of indices for the first array. + /// * `probe_indices` - The input/output vector of indices for the second array. + /// # Returns + /// * `Result<(), GpuSpatialError>` - Ok if successful, Err if an error occurred. + pub fn refine_loaded( + &self, + array: &ArrayRef, + predicate: GpuSpatialRelationPredicateWrapper, + build_indices: &mut Vec, + probe_indices: &mut Vec, + ) -> Result<(), GpuSpatialError> { + log::debug!( + "DEBUG FFI: refine called with array={}, indices={}, predicate={:?}", + array.len(), + build_indices.len(), + predicate + ); + + let (ffi_array, ffi_schema) = arrow_array::ffi::to_ffi(&array.to_data())?; + + log::debug!("DEBUG FFI: FFI conversion successful"); + + if let Some(refine_fn) = self.refiner.refine_loaded { + unsafe { + let ffi_array_ptr: *const ArrowArray = + transmute(&ffi_array as *const FFI_ArrowArray); + let ffi_schema_ptr: *const ArrowSchema = + transmute(&ffi_schema as *const FFI_ArrowSchema); + log::debug!("DEBUG FFI: Calling C++ refine function"); + let mut new_len: u32 = 0; + if refine_fn( + &self.refiner as *const _ as *mut _, + ffi_schema_ptr as *mut _, + ffi_array_ptr as *mut _, + predicate as c_uint, + build_indices.as_mut_ptr(), + probe_indices.as_mut_ptr(), + build_indices.len() as u32, + &mut new_len as *mut u32, + ) != 0 + { + let error_message = + self.refiner.get_last_error.unwrap()(&self.refiner as *const _ as *mut _); + let c_str = std::ffi::CStr::from_ptr(error_message); + let error_string = c_str.to_string_lossy().into_owned(); + log::error!("DEBUG FFI: refine failed: {}", error_string); + return Err(GpuSpatialError::Refine(error_string)); } + log::debug!("DEBUG FFI: refine C++ call succeeded"); + // Update the lengths of the output index vectors + build_indices.truncate(new_len as usize); + probe_indices.truncate(new_len as usize); } } + Ok(()) + } + /// # Refines candidate pairs using the GPU spatial refiner + /// This function refines candidate pairs of geometries using the GPU spatial refiner. + /// It takes two arrays of geometries and a predicate, and outputs the refined pairs of + /// indices that satisfy the predicate. + /// # Arguments + /// * `array1` - The first array of geometries. + /// * `array2` - The second array of geometries. + /// * `predicate` - The spatial relation predicate to use for refinement. + /// * `indices1` - The input/output vector of indices for the first array. + /// * `indices2` - The input/output vector of indices for the second array. + /// # Returns + /// * `Result<(), GpuSpatialError>` - Ok if successful, Err if an error occurred. + pub fn refine( + &self, + array1: &ArrayRef, + array2: &ArrayRef, + predicate: GpuSpatialRelationPredicateWrapper, + indices1: &mut Vec, + indices2: &mut Vec, + ) -> Result<(), GpuSpatialError> { + log::debug!( + "DEBUG FFI: refine called with array1={}, array2={}, indices={}, predicate={:?}", + array1.len(), + array2.len(), + indices1.len(), + predicate + ); - // Sort both vectors. The default sort on tuples compares element by element. - result_pairs.sort(); - answer_pairs.sort(); + let (ffi_array1, ffi_schema1) = arrow_array::ffi::to_ffi(&array1.to_data())?; + let (ffi_array2, ffi_schema2) = arrow_array::ffi::to_ffi(&array2.to_data())?; - // Assert that the two sorted vectors are equal. - assert_eq!(result_pairs, answer_pairs); + log::debug!("DEBUG FFI: FFI conversion successful"); - joiner.destroy_context(&mut ctx); - joiner.release(); + if let Some(refine_fn) = self.refiner.refine { + unsafe { + let ffi_array1_ptr: *const ArrowArray = + transmute(&ffi_array1 as *const FFI_ArrowArray); + let ffi_schema1_ptr: *const ArrowSchema = + transmute(&ffi_schema1 as *const FFI_ArrowSchema); + let ffi_array2_ptr: *const ArrowArray = + transmute(&ffi_array2 as *const FFI_ArrowArray); + let ffi_schema2_ptr: *const ArrowSchema = + transmute(&ffi_schema2 as *const FFI_ArrowSchema); + log::debug!("DEBUG FFI: Calling C++ refine function"); + let mut new_len: u32 = 0; + if refine_fn( + &self.refiner as *const _ as *mut _, + ffi_schema1_ptr as *mut _, + ffi_array1_ptr as *mut _, + ffi_schema2_ptr as *mut _, + ffi_array2_ptr as *mut _, + predicate as c_uint, + indices1.as_mut_ptr(), + indices2.as_mut_ptr(), + indices1.len() as u32, + &mut new_len as *mut u32, + ) != 0 + { + let error_message = + self.refiner.get_last_error.unwrap()(&self.refiner as *const _ as *mut _); + let c_str = std::ffi::CStr::from_ptr(error_message); + let error_string = c_str.to_string_lossy().into_owned(); + log::error!("DEBUG FFI: refine failed: {}", error_string); + return Err(GpuSpatialError::Refine(error_string)); + } + log::debug!("DEBUG FFI: refine C++ call succeeded"); + // Update the lengths of the output index vectors + indices1.truncate(new_len as usize); + indices2.truncate(new_len as usize); + } + } + Ok(()) + } +} + +impl Default for GpuSpatialRefinerWrapper { + fn default() -> Self { + GpuSpatialRefinerWrapper { + refiner: SedonaSpatialRefiner { + clear: None, + push_build: None, + finish_building: None, + refine_loaded: None, + refine: None, + get_last_error: None, + release: None, + private_data: std::ptr::null_mut(), + }, + _runtime: Arc::new(Mutex::new(GpuSpatialRuntimeWrapper::default())), + } + } +} + +impl Drop for GpuSpatialRefinerWrapper { + fn drop(&mut self) { + // Call the release function if it exists + if let Some(release_fn) = self.refiner.release { + unsafe { + release_fn(&mut self.refiner as *mut _); + } + } } }