From 04c8f683cf9e3f838ad34f71c788275c118b6d33 Mon Sep 17 00:00:00 2001 From: yiaany Date: Tue, 29 Sep 2026 22:19:11 +0500 Subject: [PATCH 1/2] docs: publish DataFusion 55 documentation snapshots --- .../rust_lint.sh | 126 + .../capitalized_example.csv | 5 + .../rust_fmt.sh | 68 + .../rust_toml_fmt.sh | 69 + .../rust_clippy.sh | 76 + .../parquet_query_sql.sql | 32 + versions/55.0.0/_images/flamegraph.svg | 491 + versions/55.0.0/_images/original.svg | 31 + versions/55.0.0/_images/original_dark.svg | 31 + versions/55.0.0/_images/samply_profiler.png | Bin 0 -> 605887 bytes .../contributor-guide/api-health.md.txt | 126 + .../contributor-guide/architecture.md.txt | 91 + .../architecture/dependency-graph.md.txt | 180 + .../contributor-guide/communication.md.txt | 106 + .../development_environment.md.txt | 132 + .../contributor-guide/governance.md.txt | 179 + .../gsoc_application_guidelines_2025.md.txt | 105 + .../gsoc/gsoc_project_ideas_2025.md.txt | 112 + .../contributor-guide/gsoc/index.rst.txt | 36 + .../_sources/contributor-guide/howtos.md.txt | 190 + .../_sources/contributor-guide/index.md.txt | 244 + .../contributor-guide/inviting.md.txt | 418 + .../contributor-guide/pr_review.md.txt | 240 + .../release_management.md.txt | 202 + .../_sources/contributor-guide/roadmap.md.txt | 156 + .../specification/index.rst.txt | 35 + .../specification/invariants.md.txt | 327 + .../output-field-name-semantic.md.txt | 212 + .../_sources/contributor-guide/testing.md.txt | 270 + versions/55.0.0/_sources/download.md.txt | 77 + versions/55.0.0/_sources/index.rst.txt | 186 + .../building-logical-plans.md.txt | 226 + .../library-user-guide/catalogs.md.txt | 315 + .../custom-table-providers.md.txt | 973 ++ .../extending-operators.md.txt | 62 + .../library-user-guide/extending-sql.md.txt | 389 + .../library-user-guide/extensions.md.txt | 64 + .../functions/adding-udfs.md.txt | 1603 ++++ .../functions/index.rst.txt | 25 + .../library-user-guide/functions/spark.md.txt | 29 + .../_sources/library-user-guide/index.md.txt | 43 + .../library-user-guide/profiling.md.txt | 124 + .../library-user-guide/query-optimizer.md.txt | 522 ++ .../table-constraints.md.txt | 42 + .../upgrading/46.0.0.md.txt | 310 + .../upgrading/47.0.0.md.txt | 135 + .../upgrading/48.0.0.md.txt | 244 + .../upgrading/48.0.1.md.txt | 39 + .../upgrading/49.0.0.md.txt | 222 + .../upgrading/50.0.0.md.txt | 330 + .../upgrading/51.0.0.md.txt | 272 + .../upgrading/52.0.0.md.txt | 669 ++ .../upgrading/53.0.0.md.txt | 551 ++ .../upgrading/54.0.0.md.txt | 935 ++ .../upgrading/55.0.0.md.txt | 1293 +++ .../upgrading/index.rst.txt | 34 + .../using-the-dataframe-api.md.txt | 286 + .../using-the-sql-api.md.txt | 225 + .../working-with-exprs.md.txt | 369 + .../user-guide/arrow-introduction.md.txt | 256 + .../user-guide/cli/datasources.md.txt | 492 + .../_sources/user-guide/cli/functions.md.txt | 227 + .../_sources/user-guide/cli/index.rst.txt | 28 + .../user-guide/cli/installation.md.txt | 64 + .../_sources/user-guide/cli/overview.md.txt | 45 + .../_sources/user-guide/cli/usage.md.txt | 257 + .../concepts-readings-events.md.txt | 233 + .../55.0.0/_sources/user-guide/configs.md.txt | 366 + .../user-guide/crate-configuration.md.txt | 251 + .../_sources/user-guide/dataframe.md.txt | 125 + .../_sources/user-guide/example-usage.md.txt | 182 + .../_sources/user-guide/explain-usage.md.txt | 432 + .../_sources/user-guide/expressions.md.txt | 337 + .../55.0.0/_sources/user-guide/faq.md.txt | 65 + .../_sources/user-guide/features.md.txt | 114 + .../_sources/user-guide/introduction.md.txt | 221 + .../55.0.0/_sources/user-guide/metrics.md.txt | 47 + .../parquet-content-defined-chunking.md.txt | 163 + .../user-guide/sql/aggregate_functions.md.txt | 1245 +++ .../_sources/user-guide/sql/data_types.md.txt | 138 + .../55.0.0/_sources/user-guide/sql/ddl.md.txt | 437 + .../55.0.0/_sources/user-guide/sql/dml.md.txt | 138 + .../_sources/user-guide/sql/explain.md.txt | 297 + .../user-guide/sql/format_options.md.txt | 185 + .../_sources/user-guide/sql/index.rst.txt | 38 + .../user-guide/sql/information_schema.md.txt | 121 + .../_sources/user-guide/sql/operators.md.txt | 615 ++ .../user-guide/sql/prepared_statements.md.txt | 139 + .../user-guide/sql/scalar_functions.md.txt | 6109 +++++++++++++ .../_sources/user-guide/sql/select.md.txt | 1087 +++ .../user-guide/sql/special_functions.md.txt | 101 + .../user-guide/sql/struct_coercion.md.txt | 374 + .../_sources/user-guide/sql/subqueries.md.txt | 423 + .../user-guide/sql/window_functions.md.txt | 529 ++ .../55.0.0/_static/2x_bgwhite_original.png | Bin 0 -> 60451 bytes versions/55.0.0/_static/base-stemmer.js | 476 + versions/55.0.0/_static/basic.css | 906 ++ versions/55.0.0/_static/data/deps.svg | 668 ++ versions/55.0.0/_static/doctools.js | 150 + .../55.0.0/_static/documentation_options.js | 13 + versions/55.0.0/_static/english-stemmer.js | 1066 +++ versions/55.0.0/_static/favicon.svg | 10 + versions/55.0.0/_static/file.png | Bin 0 -> 286 bytes .../_static/images/2x_bgwhite_original.png | Bin 0 -> 60451 bytes versions/55.0.0/_static/images/flamegraph.svg | 491 + .../DataFusion-Logo-Background-White.png | Bin 0 -> 12401 bytes .../DataFusion-Logo-Background-White.svg | 1 + .../images/old_logo/DataFusion-Logo-Dark.png | Bin 0 -> 20134 bytes .../images/old_logo/DataFusion-Logo-Dark.svg | 1 + .../images/old_logo/DataFusion-Logo-Light.png | Bin 0 -> 19102 bytes .../images/old_logo/DataFusion-Logo-Light.svg | 1 + versions/55.0.0/_static/images/original.png | Bin 0 -> 26337 bytes versions/55.0.0/_static/images/original.svg | 31 + versions/55.0.0/_static/images/original2x.png | Bin 0 -> 66517 bytes .../55.0.0/_static/images/original_dark.svg | 31 + versions/55.0.0/_static/language_data.js | 13 + versions/55.0.0/_static/minus.png | Bin 0 -> 90 bytes versions/55.0.0/_static/original.svg | 31 + versions/55.0.0/_static/original_dark.svg | 31 + versions/55.0.0/_static/plus.png | Bin 0 -> 90 bytes versions/55.0.0/_static/pygments.css | 152 + versions/55.0.0/_static/scripts/bootstrap.js | 3 + .../_static/scripts/bootstrap.js.LICENSE.txt | 5 + .../55.0.0/_static/scripts/bootstrap.js.map | 1 + .../55.0.0/_static/scripts/fontawesome.js | 3 + .../scripts/fontawesome.js.LICENSE.txt | 5 + .../55.0.0/_static/scripts/fontawesome.js.map | 1 + .../_static/scripts/pydata-sphinx-theme.js | 2 + .../scripts/pydata-sphinx-theme.js.map | 1 + versions/55.0.0/_static/searchtools.js | 693 ++ versions/55.0.0/_static/sphinx_highlight.js | 159 + .../_static/styles/pydata-sphinx-theme.css | 27 + .../styles/pydata-sphinx-theme.css.map | 1 + versions/55.0.0/_static/styles/theme.css | 2 + versions/55.0.0/_static/theme_overrides.css | 158 + .../fontawesome/webfonts/fa-brands-400.woff2 | Bin 0 -> 110088 bytes .../fontawesome/webfonts/fa-regular-400.woff2 | Bin 0 -> 18924 bytes .../fontawesome/webfonts/fa-solid-900.woff2 | Bin 0 -> 114740 bytes versions/55.0.0/_static/webpack-macros.html | 24 + .../55.0.0/contributor-guide/api-health.html | 704 ++ .../contributor-guide/architecture.html | 662 ++ .../architecture/dependency-graph.html | 1420 +++ .../contributor-guide/communication.html | 695 ++ .../development_environment.html | 715 ++ .../55.0.0/contributor-guide/governance.html | 1053 +++ .../gsoc_application_guidelines_2025.html | 710 ++ .../gsoc/gsoc_project_ideas_2025.html | 727 ++ .../55.0.0/contributor-guide/gsoc/index.html | 587 ++ versions/55.0.0/contributor-guide/howtos.html | 757 ++ versions/55.0.0/contributor-guide/index.html | 827 ++ .../55.0.0/contributor-guide/inviting.html | 989 ++ .../55.0.0/contributor-guide/pr_review.html | 814 ++ .../contributor-guide/release_management.html | 788 ++ .../55.0.0/contributor-guide/roadmap.html | 730 ++ .../specification/index.html | 587 ++ .../specification/invariants.html | 954 ++ .../output-field-name-semantic.html | 1022 +++ .../55.0.0/contributor-guide/testing.html | 809 ++ versions/55.0.0/download.html | 657 ++ versions/55.0.0/genindex.html | 499 + versions/55.0.0/index.html | 686 ++ .../library-user-guide/adding-udfs.html | 1 + .../building-logical-plans.html | 782 ++ .../55.0.0/library-user-guide/catalogs.html | 815 ++ .../custom-table-providers.html | 1494 +++ .../extending-operators.html | 642 ++ .../library-user-guide/extending-sql.html | 1002 ++ .../55.0.0/library-user-guide/extensions.html | 655 ++ .../functions/adding-udfs.html | 1594 ++++ .../library-user-guide/functions/index.html | 589 ++ .../library-user-guide/functions/spark.html | 598 ++ versions/55.0.0/library-user-guide/index.html | 608 ++ .../55.0.0/library-user-guide/profiling.html | 706 ++ .../library-user-guide/query-optimizer.html | 1031 +++ .../library-user-guide/table-constraints.html | 609 ++ .../55.0.0/library-user-guide/upgrading.html | 1 + .../library-user-guide/upgrading/46.0.0.html | 840 ++ .../library-user-guide/upgrading/47.0.0.html | 693 ++ .../library-user-guide/upgrading/48.0.0.html | 803 ++ .../library-user-guide/upgrading/48.0.1.html | 626 ++ .../library-user-guide/upgrading/49.0.0.html | 773 ++ .../library-user-guide/upgrading/50.0.0.html | 861 ++ .../library-user-guide/upgrading/51.0.0.html | 836 ++ .../library-user-guide/upgrading/52.0.0.html | 1175 +++ .../library-user-guide/upgrading/53.0.0.html | 1082 +++ .../library-user-guide/upgrading/54.0.0.html | 1486 +++ .../library-user-guide/upgrading/55.0.0.html | 1759 ++++ .../library-user-guide/upgrading/index.html | 589 ++ .../using-the-dataframe-api.html | 853 ++ .../library-user-guide/using-the-sql-api.html | 799 ++ .../working-with-exprs.html | 854 ++ versions/55.0.0/llms.txt | 26 + versions/55.0.0/objects.inv | Bin 0 -> 2435 bytes versions/55.0.0/search.html | 519 ++ versions/55.0.0/searchindex.js | 1 + versions/55.0.0/sitemap.xml | 2 + .../55.0.0/user-guide/arrow-introduction.html | 782 ++ .../55.0.0/user-guide/cli/datasources.html | 1105 +++ versions/55.0.0/user-guide/cli/functions.html | 963 ++ versions/55.0.0/user-guide/cli/index.html | 626 ++ .../55.0.0/user-guide/cli/installation.html | 647 ++ versions/55.0.0/user-guide/cli/overview.html | 615 ++ versions/55.0.0/user-guide/cli/usage.html | 838 ++ .../user-guide/concepts-readings-events.html | 765 ++ versions/55.0.0/user-guide/configs.html | 1429 +++ .../user-guide/crate-configuration.html | 804 ++ versions/55.0.0/user-guide/dataframe.html | 687 ++ versions/55.0.0/user-guide/example-usage.html | 759 ++ versions/55.0.0/user-guide/explain-usage.html | 1071 +++ versions/55.0.0/user-guide/expressions.html | 1421 +++ versions/55.0.0/user-guide/faq.html | 650 ++ versions/55.0.0/user-guide/features.html | 730 ++ versions/55.0.0/user-guide/introduction.html | 786 ++ versions/55.0.0/user-guide/metrics.html | 665 ++ .../parquet-content-defined-chunking.html | 749 ++ .../55.0.0/user-guide/runtime_configs.html | 1 + .../user-guide/sql/aggregate_functions.html | 2085 +++++ .../55.0.0/user-guide/sql/data_types.html | 835 ++ versions/55.0.0/user-guide/sql/ddl.html | 1004 ++ versions/55.0.0/user-guide/sql/dml.html | 717 ++ versions/55.0.0/user-guide/sql/explain.html | 872 ++ .../55.0.0/user-guide/sql/format_options.html | 1021 +++ versions/55.0.0/user-guide/sql/index.html | 711 ++ .../user-guide/sql/information_schema.html | 703 ++ versions/55.0.0/user-guide/sql/operators.html | 1170 +++ .../user-guide/sql/prepared_statements.html | 688 ++ .../user-guide/sql/scalar_functions.html | 8092 +++++++++++++++++ versions/55.0.0/user-guide/sql/select.html | 1598 ++++ .../user-guide/sql/special_functions.html | 705 ++ .../user-guide/sql/struct_coercion.html | 983 ++ .../55.0.0/user-guide/sql/subqueries.html | 1031 +++ .../user-guide/sql/window_functions.html | 1159 +++ .../rust_lint.sh | 126 + .../capitalized_example.csv | 5 + .../rust_fmt.sh | 68 + .../rust_toml_fmt.sh | 69 + .../rust_clippy.sh | 76 + .../parquet_query_sql.sql | 32 + versions/55.1.0/_images/flamegraph.svg | 491 + versions/55.1.0/_images/original.svg | 31 + versions/55.1.0/_images/original_dark.svg | 31 + versions/55.1.0/_images/samply_profiler.png | Bin 0 -> 605887 bytes .../contributor-guide/api-health.md.txt | 126 + .../contributor-guide/architecture.md.txt | 91 + .../architecture/dependency-graph.md.txt | 180 + .../contributor-guide/communication.md.txt | 106 + .../development_environment.md.txt | 132 + .../contributor-guide/governance.md.txt | 179 + .../gsoc_application_guidelines_2025.md.txt | 105 + .../gsoc/gsoc_project_ideas_2025.md.txt | 112 + .../contributor-guide/gsoc/index.rst.txt | 36 + .../_sources/contributor-guide/howtos.md.txt | 190 + .../_sources/contributor-guide/index.md.txt | 244 + .../contributor-guide/inviting.md.txt | 418 + .../contributor-guide/pr_review.md.txt | 240 + .../release_management.md.txt | 202 + .../_sources/contributor-guide/roadmap.md.txt | 156 + .../specification/index.rst.txt | 35 + .../specification/invariants.md.txt | 327 + .../output-field-name-semantic.md.txt | 212 + .../_sources/contributor-guide/testing.md.txt | 270 + versions/55.1.0/_sources/download.md.txt | 77 + versions/55.1.0/_sources/index.rst.txt | 186 + .../building-logical-plans.md.txt | 226 + .../library-user-guide/catalogs.md.txt | 315 + .../custom-table-providers.md.txt | 973 ++ .../extending-operators.md.txt | 62 + .../library-user-guide/extending-sql.md.txt | 389 + .../library-user-guide/extensions.md.txt | 64 + .../functions/adding-udfs.md.txt | 1603 ++++ .../functions/index.rst.txt | 25 + .../library-user-guide/functions/spark.md.txt | 29 + .../_sources/library-user-guide/index.md.txt | 43 + .../library-user-guide/profiling.md.txt | 124 + .../library-user-guide/query-optimizer.md.txt | 522 ++ .../table-constraints.md.txt | 42 + .../upgrading/46.0.0.md.txt | 310 + .../upgrading/47.0.0.md.txt | 135 + .../upgrading/48.0.0.md.txt | 244 + .../upgrading/48.0.1.md.txt | 39 + .../upgrading/49.0.0.md.txt | 222 + .../upgrading/50.0.0.md.txt | 330 + .../upgrading/51.0.0.md.txt | 272 + .../upgrading/52.0.0.md.txt | 669 ++ .../upgrading/53.0.0.md.txt | 551 ++ .../upgrading/54.0.0.md.txt | 935 ++ .../upgrading/55.0.0.md.txt | 1293 +++ .../upgrading/index.rst.txt | 34 + .../using-the-dataframe-api.md.txt | 286 + .../using-the-sql-api.md.txt | 225 + .../working-with-exprs.md.txt | 369 + .../user-guide/arrow-introduction.md.txt | 256 + .../user-guide/cli/datasources.md.txt | 492 + .../_sources/user-guide/cli/functions.md.txt | 227 + .../_sources/user-guide/cli/index.rst.txt | 28 + .../user-guide/cli/installation.md.txt | 64 + .../_sources/user-guide/cli/overview.md.txt | 45 + .../_sources/user-guide/cli/usage.md.txt | 257 + .../concepts-readings-events.md.txt | 233 + .../55.1.0/_sources/user-guide/configs.md.txt | 366 + .../user-guide/crate-configuration.md.txt | 251 + .../_sources/user-guide/dataframe.md.txt | 125 + .../_sources/user-guide/example-usage.md.txt | 182 + .../_sources/user-guide/explain-usage.md.txt | 432 + .../_sources/user-guide/expressions.md.txt | 337 + .../55.1.0/_sources/user-guide/faq.md.txt | 65 + .../_sources/user-guide/features.md.txt | 114 + .../_sources/user-guide/introduction.md.txt | 221 + .../55.1.0/_sources/user-guide/metrics.md.txt | 47 + .../parquet-content-defined-chunking.md.txt | 163 + .../user-guide/sql/aggregate_functions.md.txt | 1245 +++ .../_sources/user-guide/sql/data_types.md.txt | 138 + .../55.1.0/_sources/user-guide/sql/ddl.md.txt | 437 + .../55.1.0/_sources/user-guide/sql/dml.md.txt | 138 + .../_sources/user-guide/sql/explain.md.txt | 297 + .../user-guide/sql/format_options.md.txt | 185 + .../_sources/user-guide/sql/index.rst.txt | 38 + .../user-guide/sql/information_schema.md.txt | 121 + .../_sources/user-guide/sql/operators.md.txt | 615 ++ .../user-guide/sql/prepared_statements.md.txt | 139 + .../user-guide/sql/scalar_functions.md.txt | 6109 +++++++++++++ .../_sources/user-guide/sql/select.md.txt | 1087 +++ .../user-guide/sql/special_functions.md.txt | 101 + .../user-guide/sql/struct_coercion.md.txt | 374 + .../_sources/user-guide/sql/subqueries.md.txt | 423 + .../user-guide/sql/window_functions.md.txt | 529 ++ .../55.1.0/_static/2x_bgwhite_original.png | Bin 0 -> 60451 bytes versions/55.1.0/_static/base-stemmer.js | 476 + versions/55.1.0/_static/basic.css | 906 ++ versions/55.1.0/_static/data/deps.svg | 668 ++ versions/55.1.0/_static/doctools.js | 150 + .../55.1.0/_static/documentation_options.js | 13 + versions/55.1.0/_static/english-stemmer.js | 1066 +++ versions/55.1.0/_static/favicon.svg | 10 + versions/55.1.0/_static/file.png | Bin 0 -> 286 bytes .../_static/images/2x_bgwhite_original.png | Bin 0 -> 60451 bytes versions/55.1.0/_static/images/flamegraph.svg | 491 + .../DataFusion-Logo-Background-White.png | Bin 0 -> 12401 bytes .../DataFusion-Logo-Background-White.svg | 1 + .../images/old_logo/DataFusion-Logo-Dark.png | Bin 0 -> 20134 bytes .../images/old_logo/DataFusion-Logo-Dark.svg | 1 + .../images/old_logo/DataFusion-Logo-Light.png | Bin 0 -> 19102 bytes .../images/old_logo/DataFusion-Logo-Light.svg | 1 + versions/55.1.0/_static/images/original.png | Bin 0 -> 26337 bytes versions/55.1.0/_static/images/original.svg | 31 + versions/55.1.0/_static/images/original2x.png | Bin 0 -> 66517 bytes .../55.1.0/_static/images/original_dark.svg | 31 + versions/55.1.0/_static/language_data.js | 13 + versions/55.1.0/_static/minus.png | Bin 0 -> 90 bytes versions/55.1.0/_static/original.svg | 31 + versions/55.1.0/_static/original_dark.svg | 31 + versions/55.1.0/_static/plus.png | Bin 0 -> 90 bytes versions/55.1.0/_static/pygments.css | 152 + versions/55.1.0/_static/scripts/bootstrap.js | 3 + .../_static/scripts/bootstrap.js.LICENSE.txt | 5 + .../55.1.0/_static/scripts/bootstrap.js.map | 1 + .../55.1.0/_static/scripts/fontawesome.js | 3 + .../scripts/fontawesome.js.LICENSE.txt | 5 + .../55.1.0/_static/scripts/fontawesome.js.map | 1 + .../_static/scripts/pydata-sphinx-theme.js | 2 + .../scripts/pydata-sphinx-theme.js.map | 1 + versions/55.1.0/_static/searchtools.js | 693 ++ versions/55.1.0/_static/sphinx_highlight.js | 159 + .../_static/styles/pydata-sphinx-theme.css | 27 + .../styles/pydata-sphinx-theme.css.map | 1 + versions/55.1.0/_static/styles/theme.css | 2 + versions/55.1.0/_static/theme_overrides.css | 158 + .../fontawesome/webfonts/fa-brands-400.woff2 | Bin 0 -> 110088 bytes .../fontawesome/webfonts/fa-regular-400.woff2 | Bin 0 -> 18924 bytes .../fontawesome/webfonts/fa-solid-900.woff2 | Bin 0 -> 114740 bytes versions/55.1.0/_static/webpack-macros.html | 24 + .../55.1.0/contributor-guide/api-health.html | 704 ++ .../contributor-guide/architecture.html | 662 ++ .../architecture/dependency-graph.html | 1420 +++ .../contributor-guide/communication.html | 695 ++ .../development_environment.html | 715 ++ .../55.1.0/contributor-guide/governance.html | 1053 +++ .../gsoc_application_guidelines_2025.html | 710 ++ .../gsoc/gsoc_project_ideas_2025.html | 727 ++ .../55.1.0/contributor-guide/gsoc/index.html | 587 ++ versions/55.1.0/contributor-guide/howtos.html | 757 ++ versions/55.1.0/contributor-guide/index.html | 827 ++ .../55.1.0/contributor-guide/inviting.html | 989 ++ .../55.1.0/contributor-guide/pr_review.html | 814 ++ .../contributor-guide/release_management.html | 788 ++ .../55.1.0/contributor-guide/roadmap.html | 730 ++ .../specification/index.html | 587 ++ .../specification/invariants.html | 954 ++ .../output-field-name-semantic.html | 1022 +++ .../55.1.0/contributor-guide/testing.html | 809 ++ versions/55.1.0/download.html | 657 ++ versions/55.1.0/genindex.html | 499 + versions/55.1.0/index.html | 686 ++ .../library-user-guide/adding-udfs.html | 1 + .../building-logical-plans.html | 782 ++ .../55.1.0/library-user-guide/catalogs.html | 815 ++ .../custom-table-providers.html | 1494 +++ .../extending-operators.html | 642 ++ .../library-user-guide/extending-sql.html | 1002 ++ .../55.1.0/library-user-guide/extensions.html | 655 ++ .../functions/adding-udfs.html | 1594 ++++ .../library-user-guide/functions/index.html | 589 ++ .../library-user-guide/functions/spark.html | 598 ++ versions/55.1.0/library-user-guide/index.html | 608 ++ .../55.1.0/library-user-guide/profiling.html | 706 ++ .../library-user-guide/query-optimizer.html | 1031 +++ .../library-user-guide/table-constraints.html | 609 ++ .../55.1.0/library-user-guide/upgrading.html | 1 + .../library-user-guide/upgrading/46.0.0.html | 840 ++ .../library-user-guide/upgrading/47.0.0.html | 693 ++ .../library-user-guide/upgrading/48.0.0.html | 803 ++ .../library-user-guide/upgrading/48.0.1.html | 626 ++ .../library-user-guide/upgrading/49.0.0.html | 773 ++ .../library-user-guide/upgrading/50.0.0.html | 861 ++ .../library-user-guide/upgrading/51.0.0.html | 836 ++ .../library-user-guide/upgrading/52.0.0.html | 1175 +++ .../library-user-guide/upgrading/53.0.0.html | 1082 +++ .../library-user-guide/upgrading/54.0.0.html | 1486 +++ .../library-user-guide/upgrading/55.0.0.html | 1759 ++++ .../library-user-guide/upgrading/index.html | 589 ++ .../using-the-dataframe-api.html | 853 ++ .../library-user-guide/using-the-sql-api.html | 799 ++ .../working-with-exprs.html | 854 ++ versions/55.1.0/llms.txt | 26 + versions/55.1.0/objects.inv | Bin 0 -> 2435 bytes versions/55.1.0/search.html | 519 ++ versions/55.1.0/searchindex.js | 1 + versions/55.1.0/sitemap.xml | 2 + .../55.1.0/user-guide/arrow-introduction.html | 782 ++ .../55.1.0/user-guide/cli/datasources.html | 1105 +++ versions/55.1.0/user-guide/cli/functions.html | 963 ++ versions/55.1.0/user-guide/cli/index.html | 626 ++ .../55.1.0/user-guide/cli/installation.html | 647 ++ versions/55.1.0/user-guide/cli/overview.html | 615 ++ versions/55.1.0/user-guide/cli/usage.html | 838 ++ .../user-guide/concepts-readings-events.html | 765 ++ versions/55.1.0/user-guide/configs.html | 1429 +++ .../user-guide/crate-configuration.html | 804 ++ versions/55.1.0/user-guide/dataframe.html | 687 ++ versions/55.1.0/user-guide/example-usage.html | 759 ++ versions/55.1.0/user-guide/explain-usage.html | 1071 +++ versions/55.1.0/user-guide/expressions.html | 1421 +++ versions/55.1.0/user-guide/faq.html | 650 ++ versions/55.1.0/user-guide/features.html | 730 ++ versions/55.1.0/user-guide/introduction.html | 786 ++ versions/55.1.0/user-guide/metrics.html | 665 ++ .../parquet-content-defined-chunking.html | 749 ++ .../55.1.0/user-guide/runtime_configs.html | 1 + .../user-guide/sql/aggregate_functions.html | 2085 +++++ .../55.1.0/user-guide/sql/data_types.html | 835 ++ versions/55.1.0/user-guide/sql/ddl.html | 1004 ++ versions/55.1.0/user-guide/sql/dml.html | 717 ++ versions/55.1.0/user-guide/sql/explain.html | 872 ++ .../55.1.0/user-guide/sql/format_options.html | 1021 +++ versions/55.1.0/user-guide/sql/index.html | 711 ++ .../user-guide/sql/information_schema.html | 703 ++ versions/55.1.0/user-guide/sql/operators.html | 1170 +++ .../user-guide/sql/prepared_statements.html | 688 ++ .../user-guide/sql/scalar_functions.html | 8092 +++++++++++++++++ versions/55.1.0/user-guide/sql/select.html | 1598 ++++ .../user-guide/sql/special_functions.html | 705 ++ .../user-guide/sql/struct_coercion.html | 983 ++ .../55.1.0/user-guide/sql/subqueries.html | 1031 +++ .../user-guide/sql/window_functions.html | 1159 +++ 464 files changed, 236970 insertions(+) create mode 100644 versions/55.0.0/_downloads/1e336e540124589d1e27210fa4f805f6/rust_lint.sh create mode 100644 versions/55.0.0/_downloads/3cce4d737d8c5814f5b50d859d21ba53/capitalized_example.csv create mode 100644 versions/55.0.0/_downloads/4a91b8629bd17ce8ae200ee0b5345f19/rust_fmt.sh create mode 100644 versions/55.0.0/_downloads/5b1657e27416274aa54d7579d8b125c3/rust_toml_fmt.sh create mode 100644 versions/55.0.0/_downloads/ca7ec2ded721157172203812bd6e22b2/rust_clippy.sh create mode 100644 versions/55.0.0/_downloads/ef2c125b8dcc9e4b052e1280935df561/parquet_query_sql.sql create mode 100644 versions/55.0.0/_images/flamegraph.svg create mode 100644 versions/55.0.0/_images/original.svg create mode 100644 versions/55.0.0/_images/original_dark.svg create mode 100644 versions/55.0.0/_images/samply_profiler.png create mode 100644 versions/55.0.0/_sources/contributor-guide/api-health.md.txt create mode 100644 versions/55.0.0/_sources/contributor-guide/architecture.md.txt create mode 100644 versions/55.0.0/_sources/contributor-guide/architecture/dependency-graph.md.txt create mode 100644 versions/55.0.0/_sources/contributor-guide/communication.md.txt create mode 100644 versions/55.0.0/_sources/contributor-guide/development_environment.md.txt create mode 100644 versions/55.0.0/_sources/contributor-guide/governance.md.txt create mode 100644 versions/55.0.0/_sources/contributor-guide/gsoc/gsoc_application_guidelines_2025.md.txt create mode 100644 versions/55.0.0/_sources/contributor-guide/gsoc/gsoc_project_ideas_2025.md.txt create mode 100644 versions/55.0.0/_sources/contributor-guide/gsoc/index.rst.txt create mode 100644 versions/55.0.0/_sources/contributor-guide/howtos.md.txt create mode 100644 versions/55.0.0/_sources/contributor-guide/index.md.txt create mode 100644 versions/55.0.0/_sources/contributor-guide/inviting.md.txt create mode 100644 versions/55.0.0/_sources/contributor-guide/pr_review.md.txt create mode 100644 versions/55.0.0/_sources/contributor-guide/release_management.md.txt create mode 100644 versions/55.0.0/_sources/contributor-guide/roadmap.md.txt create mode 100644 versions/55.0.0/_sources/contributor-guide/specification/index.rst.txt create mode 100644 versions/55.0.0/_sources/contributor-guide/specification/invariants.md.txt create mode 100644 versions/55.0.0/_sources/contributor-guide/specification/output-field-name-semantic.md.txt create mode 100644 versions/55.0.0/_sources/contributor-guide/testing.md.txt create mode 100644 versions/55.0.0/_sources/download.md.txt create mode 100644 versions/55.0.0/_sources/index.rst.txt create mode 100644 versions/55.0.0/_sources/library-user-guide/building-logical-plans.md.txt create mode 100644 versions/55.0.0/_sources/library-user-guide/catalogs.md.txt create mode 100644 versions/55.0.0/_sources/library-user-guide/custom-table-providers.md.txt create mode 100644 versions/55.0.0/_sources/library-user-guide/extending-operators.md.txt create mode 100644 versions/55.0.0/_sources/library-user-guide/extending-sql.md.txt create mode 100644 versions/55.0.0/_sources/library-user-guide/extensions.md.txt create mode 100644 versions/55.0.0/_sources/library-user-guide/functions/adding-udfs.md.txt create mode 100644 versions/55.0.0/_sources/library-user-guide/functions/index.rst.txt create mode 100644 versions/55.0.0/_sources/library-user-guide/functions/spark.md.txt create mode 100644 versions/55.0.0/_sources/library-user-guide/index.md.txt create mode 100644 versions/55.0.0/_sources/library-user-guide/profiling.md.txt create mode 100644 versions/55.0.0/_sources/library-user-guide/query-optimizer.md.txt create mode 100644 versions/55.0.0/_sources/library-user-guide/table-constraints.md.txt create mode 100644 versions/55.0.0/_sources/library-user-guide/upgrading/46.0.0.md.txt create mode 100644 versions/55.0.0/_sources/library-user-guide/upgrading/47.0.0.md.txt create mode 100644 versions/55.0.0/_sources/library-user-guide/upgrading/48.0.0.md.txt create mode 100644 versions/55.0.0/_sources/library-user-guide/upgrading/48.0.1.md.txt create mode 100644 versions/55.0.0/_sources/library-user-guide/upgrading/49.0.0.md.txt create mode 100644 versions/55.0.0/_sources/library-user-guide/upgrading/50.0.0.md.txt create mode 100644 versions/55.0.0/_sources/library-user-guide/upgrading/51.0.0.md.txt create mode 100644 versions/55.0.0/_sources/library-user-guide/upgrading/52.0.0.md.txt create mode 100644 versions/55.0.0/_sources/library-user-guide/upgrading/53.0.0.md.txt create mode 100644 versions/55.0.0/_sources/library-user-guide/upgrading/54.0.0.md.txt create mode 100644 versions/55.0.0/_sources/library-user-guide/upgrading/55.0.0.md.txt create mode 100644 versions/55.0.0/_sources/library-user-guide/upgrading/index.rst.txt create mode 100644 versions/55.0.0/_sources/library-user-guide/using-the-dataframe-api.md.txt create mode 100644 versions/55.0.0/_sources/library-user-guide/using-the-sql-api.md.txt create mode 100644 versions/55.0.0/_sources/library-user-guide/working-with-exprs.md.txt create mode 100644 versions/55.0.0/_sources/user-guide/arrow-introduction.md.txt create mode 100644 versions/55.0.0/_sources/user-guide/cli/datasources.md.txt create mode 100644 versions/55.0.0/_sources/user-guide/cli/functions.md.txt create mode 100644 versions/55.0.0/_sources/user-guide/cli/index.rst.txt create mode 100644 versions/55.0.0/_sources/user-guide/cli/installation.md.txt create mode 100644 versions/55.0.0/_sources/user-guide/cli/overview.md.txt create mode 100644 versions/55.0.0/_sources/user-guide/cli/usage.md.txt create mode 100644 versions/55.0.0/_sources/user-guide/concepts-readings-events.md.txt create mode 100644 versions/55.0.0/_sources/user-guide/configs.md.txt create mode 100644 versions/55.0.0/_sources/user-guide/crate-configuration.md.txt create mode 100644 versions/55.0.0/_sources/user-guide/dataframe.md.txt create mode 100644 versions/55.0.0/_sources/user-guide/example-usage.md.txt create mode 100644 versions/55.0.0/_sources/user-guide/explain-usage.md.txt create mode 100644 versions/55.0.0/_sources/user-guide/expressions.md.txt create mode 100644 versions/55.0.0/_sources/user-guide/faq.md.txt create mode 100644 versions/55.0.0/_sources/user-guide/features.md.txt create mode 100644 versions/55.0.0/_sources/user-guide/introduction.md.txt create mode 100644 versions/55.0.0/_sources/user-guide/metrics.md.txt create mode 100644 versions/55.0.0/_sources/user-guide/parquet-content-defined-chunking.md.txt create mode 100644 versions/55.0.0/_sources/user-guide/sql/aggregate_functions.md.txt create mode 100644 versions/55.0.0/_sources/user-guide/sql/data_types.md.txt create mode 100644 versions/55.0.0/_sources/user-guide/sql/ddl.md.txt create mode 100644 versions/55.0.0/_sources/user-guide/sql/dml.md.txt create mode 100644 versions/55.0.0/_sources/user-guide/sql/explain.md.txt create mode 100644 versions/55.0.0/_sources/user-guide/sql/format_options.md.txt create mode 100644 versions/55.0.0/_sources/user-guide/sql/index.rst.txt create mode 100644 versions/55.0.0/_sources/user-guide/sql/information_schema.md.txt create mode 100644 versions/55.0.0/_sources/user-guide/sql/operators.md.txt create mode 100644 versions/55.0.0/_sources/user-guide/sql/prepared_statements.md.txt create mode 100644 versions/55.0.0/_sources/user-guide/sql/scalar_functions.md.txt create mode 100644 versions/55.0.0/_sources/user-guide/sql/select.md.txt create mode 100644 versions/55.0.0/_sources/user-guide/sql/special_functions.md.txt create mode 100644 versions/55.0.0/_sources/user-guide/sql/struct_coercion.md.txt create mode 100644 versions/55.0.0/_sources/user-guide/sql/subqueries.md.txt create mode 100644 versions/55.0.0/_sources/user-guide/sql/window_functions.md.txt create mode 100644 versions/55.0.0/_static/2x_bgwhite_original.png create mode 100644 versions/55.0.0/_static/base-stemmer.js create mode 100644 versions/55.0.0/_static/basic.css create mode 100644 versions/55.0.0/_static/data/deps.svg create mode 100644 versions/55.0.0/_static/doctools.js create mode 100644 versions/55.0.0/_static/documentation_options.js create mode 100644 versions/55.0.0/_static/english-stemmer.js create mode 100644 versions/55.0.0/_static/favicon.svg create mode 100644 versions/55.0.0/_static/file.png create mode 100644 versions/55.0.0/_static/images/2x_bgwhite_original.png create mode 100644 versions/55.0.0/_static/images/flamegraph.svg create mode 100644 versions/55.0.0/_static/images/old_logo/DataFusion-Logo-Background-White.png create mode 100644 versions/55.0.0/_static/images/old_logo/DataFusion-Logo-Background-White.svg create mode 100644 versions/55.0.0/_static/images/old_logo/DataFusion-Logo-Dark.png create mode 100644 versions/55.0.0/_static/images/old_logo/DataFusion-Logo-Dark.svg create mode 100644 versions/55.0.0/_static/images/old_logo/DataFusion-Logo-Light.png create mode 100644 versions/55.0.0/_static/images/old_logo/DataFusion-Logo-Light.svg create mode 100644 versions/55.0.0/_static/images/original.png create mode 100644 versions/55.0.0/_static/images/original.svg create mode 100644 versions/55.0.0/_static/images/original2x.png create mode 100644 versions/55.0.0/_static/images/original_dark.svg create mode 100644 versions/55.0.0/_static/language_data.js create mode 100644 versions/55.0.0/_static/minus.png create mode 100644 versions/55.0.0/_static/original.svg create mode 100644 versions/55.0.0/_static/original_dark.svg create mode 100644 versions/55.0.0/_static/plus.png create mode 100644 versions/55.0.0/_static/pygments.css create mode 100644 versions/55.0.0/_static/scripts/bootstrap.js create mode 100644 versions/55.0.0/_static/scripts/bootstrap.js.LICENSE.txt create mode 100644 versions/55.0.0/_static/scripts/bootstrap.js.map create mode 100644 versions/55.0.0/_static/scripts/fontawesome.js create mode 100644 versions/55.0.0/_static/scripts/fontawesome.js.LICENSE.txt create mode 100644 versions/55.0.0/_static/scripts/fontawesome.js.map create mode 100644 versions/55.0.0/_static/scripts/pydata-sphinx-theme.js create mode 100644 versions/55.0.0/_static/scripts/pydata-sphinx-theme.js.map create mode 100644 versions/55.0.0/_static/searchtools.js create mode 100644 versions/55.0.0/_static/sphinx_highlight.js create mode 100644 versions/55.0.0/_static/styles/pydata-sphinx-theme.css create mode 100644 versions/55.0.0/_static/styles/pydata-sphinx-theme.css.map create mode 100644 versions/55.0.0/_static/styles/theme.css create mode 100644 versions/55.0.0/_static/theme_overrides.css create mode 100644 versions/55.0.0/_static/vendor/fontawesome/webfonts/fa-brands-400.woff2 create mode 100644 versions/55.0.0/_static/vendor/fontawesome/webfonts/fa-regular-400.woff2 create mode 100644 versions/55.0.0/_static/vendor/fontawesome/webfonts/fa-solid-900.woff2 create mode 100644 versions/55.0.0/_static/webpack-macros.html create mode 100644 versions/55.0.0/contributor-guide/api-health.html create mode 100644 versions/55.0.0/contributor-guide/architecture.html create mode 100644 versions/55.0.0/contributor-guide/architecture/dependency-graph.html create mode 100644 versions/55.0.0/contributor-guide/communication.html create mode 100644 versions/55.0.0/contributor-guide/development_environment.html create mode 100644 versions/55.0.0/contributor-guide/governance.html create mode 100644 versions/55.0.0/contributor-guide/gsoc/gsoc_application_guidelines_2025.html create mode 100644 versions/55.0.0/contributor-guide/gsoc/gsoc_project_ideas_2025.html create mode 100644 versions/55.0.0/contributor-guide/gsoc/index.html create mode 100644 versions/55.0.0/contributor-guide/howtos.html create mode 100644 versions/55.0.0/contributor-guide/index.html create mode 100644 versions/55.0.0/contributor-guide/inviting.html create mode 100644 versions/55.0.0/contributor-guide/pr_review.html create mode 100644 versions/55.0.0/contributor-guide/release_management.html create mode 100644 versions/55.0.0/contributor-guide/roadmap.html create mode 100644 versions/55.0.0/contributor-guide/specification/index.html create mode 100644 versions/55.0.0/contributor-guide/specification/invariants.html create mode 100644 versions/55.0.0/contributor-guide/specification/output-field-name-semantic.html create mode 100644 versions/55.0.0/contributor-guide/testing.html create mode 100644 versions/55.0.0/download.html create mode 100644 versions/55.0.0/genindex.html create mode 100644 versions/55.0.0/index.html create mode 100644 versions/55.0.0/library-user-guide/adding-udfs.html create mode 100644 versions/55.0.0/library-user-guide/building-logical-plans.html create mode 100644 versions/55.0.0/library-user-guide/catalogs.html create mode 100644 versions/55.0.0/library-user-guide/custom-table-providers.html create mode 100644 versions/55.0.0/library-user-guide/extending-operators.html create mode 100644 versions/55.0.0/library-user-guide/extending-sql.html create mode 100644 versions/55.0.0/library-user-guide/extensions.html create mode 100644 versions/55.0.0/library-user-guide/functions/adding-udfs.html create mode 100644 versions/55.0.0/library-user-guide/functions/index.html create mode 100644 versions/55.0.0/library-user-guide/functions/spark.html create mode 100644 versions/55.0.0/library-user-guide/index.html create mode 100644 versions/55.0.0/library-user-guide/profiling.html create mode 100644 versions/55.0.0/library-user-guide/query-optimizer.html create mode 100644 versions/55.0.0/library-user-guide/table-constraints.html create mode 100644 versions/55.0.0/library-user-guide/upgrading.html create mode 100644 versions/55.0.0/library-user-guide/upgrading/46.0.0.html create mode 100644 versions/55.0.0/library-user-guide/upgrading/47.0.0.html create mode 100644 versions/55.0.0/library-user-guide/upgrading/48.0.0.html create mode 100644 versions/55.0.0/library-user-guide/upgrading/48.0.1.html create mode 100644 versions/55.0.0/library-user-guide/upgrading/49.0.0.html create mode 100644 versions/55.0.0/library-user-guide/upgrading/50.0.0.html create mode 100644 versions/55.0.0/library-user-guide/upgrading/51.0.0.html create mode 100644 versions/55.0.0/library-user-guide/upgrading/52.0.0.html create mode 100644 versions/55.0.0/library-user-guide/upgrading/53.0.0.html create mode 100644 versions/55.0.0/library-user-guide/upgrading/54.0.0.html create mode 100644 versions/55.0.0/library-user-guide/upgrading/55.0.0.html create mode 100644 versions/55.0.0/library-user-guide/upgrading/index.html create mode 100644 versions/55.0.0/library-user-guide/using-the-dataframe-api.html create mode 100644 versions/55.0.0/library-user-guide/using-the-sql-api.html create mode 100644 versions/55.0.0/library-user-guide/working-with-exprs.html create mode 100644 versions/55.0.0/llms.txt create mode 100644 versions/55.0.0/objects.inv create mode 100644 versions/55.0.0/search.html create mode 100644 versions/55.0.0/searchindex.js create mode 100644 versions/55.0.0/sitemap.xml create mode 100644 versions/55.0.0/user-guide/arrow-introduction.html create mode 100644 versions/55.0.0/user-guide/cli/datasources.html create mode 100644 versions/55.0.0/user-guide/cli/functions.html create mode 100644 versions/55.0.0/user-guide/cli/index.html create mode 100644 versions/55.0.0/user-guide/cli/installation.html create mode 100644 versions/55.0.0/user-guide/cli/overview.html create mode 100644 versions/55.0.0/user-guide/cli/usage.html create mode 100644 versions/55.0.0/user-guide/concepts-readings-events.html create mode 100644 versions/55.0.0/user-guide/configs.html create mode 100644 versions/55.0.0/user-guide/crate-configuration.html create mode 100644 versions/55.0.0/user-guide/dataframe.html create mode 100644 versions/55.0.0/user-guide/example-usage.html create mode 100644 versions/55.0.0/user-guide/explain-usage.html create mode 100644 versions/55.0.0/user-guide/expressions.html create mode 100644 versions/55.0.0/user-guide/faq.html create mode 100644 versions/55.0.0/user-guide/features.html create mode 100644 versions/55.0.0/user-guide/introduction.html create mode 100644 versions/55.0.0/user-guide/metrics.html create mode 100644 versions/55.0.0/user-guide/parquet-content-defined-chunking.html create mode 100644 versions/55.0.0/user-guide/runtime_configs.html create mode 100644 versions/55.0.0/user-guide/sql/aggregate_functions.html create mode 100644 versions/55.0.0/user-guide/sql/data_types.html create mode 100644 versions/55.0.0/user-guide/sql/ddl.html create mode 100644 versions/55.0.0/user-guide/sql/dml.html create mode 100644 versions/55.0.0/user-guide/sql/explain.html create mode 100644 versions/55.0.0/user-guide/sql/format_options.html create mode 100644 versions/55.0.0/user-guide/sql/index.html create mode 100644 versions/55.0.0/user-guide/sql/information_schema.html create mode 100644 versions/55.0.0/user-guide/sql/operators.html create mode 100644 versions/55.0.0/user-guide/sql/prepared_statements.html create mode 100644 versions/55.0.0/user-guide/sql/scalar_functions.html create mode 100644 versions/55.0.0/user-guide/sql/select.html create mode 100644 versions/55.0.0/user-guide/sql/special_functions.html create mode 100644 versions/55.0.0/user-guide/sql/struct_coercion.html create mode 100644 versions/55.0.0/user-guide/sql/subqueries.html create mode 100644 versions/55.0.0/user-guide/sql/window_functions.html create mode 100644 versions/55.1.0/_downloads/1e336e540124589d1e27210fa4f805f6/rust_lint.sh create mode 100644 versions/55.1.0/_downloads/3cce4d737d8c5814f5b50d859d21ba53/capitalized_example.csv create mode 100644 versions/55.1.0/_downloads/4a91b8629bd17ce8ae200ee0b5345f19/rust_fmt.sh create mode 100644 versions/55.1.0/_downloads/5b1657e27416274aa54d7579d8b125c3/rust_toml_fmt.sh create mode 100644 versions/55.1.0/_downloads/ca7ec2ded721157172203812bd6e22b2/rust_clippy.sh create mode 100644 versions/55.1.0/_downloads/ef2c125b8dcc9e4b052e1280935df561/parquet_query_sql.sql create mode 100644 versions/55.1.0/_images/flamegraph.svg create mode 100644 versions/55.1.0/_images/original.svg create mode 100644 versions/55.1.0/_images/original_dark.svg create mode 100644 versions/55.1.0/_images/samply_profiler.png create mode 100644 versions/55.1.0/_sources/contributor-guide/api-health.md.txt create mode 100644 versions/55.1.0/_sources/contributor-guide/architecture.md.txt create mode 100644 versions/55.1.0/_sources/contributor-guide/architecture/dependency-graph.md.txt create mode 100644 versions/55.1.0/_sources/contributor-guide/communication.md.txt create mode 100644 versions/55.1.0/_sources/contributor-guide/development_environment.md.txt create mode 100644 versions/55.1.0/_sources/contributor-guide/governance.md.txt create mode 100644 versions/55.1.0/_sources/contributor-guide/gsoc/gsoc_application_guidelines_2025.md.txt create mode 100644 versions/55.1.0/_sources/contributor-guide/gsoc/gsoc_project_ideas_2025.md.txt create mode 100644 versions/55.1.0/_sources/contributor-guide/gsoc/index.rst.txt create mode 100644 versions/55.1.0/_sources/contributor-guide/howtos.md.txt create mode 100644 versions/55.1.0/_sources/contributor-guide/index.md.txt create mode 100644 versions/55.1.0/_sources/contributor-guide/inviting.md.txt create mode 100644 versions/55.1.0/_sources/contributor-guide/pr_review.md.txt create mode 100644 versions/55.1.0/_sources/contributor-guide/release_management.md.txt create mode 100644 versions/55.1.0/_sources/contributor-guide/roadmap.md.txt create mode 100644 versions/55.1.0/_sources/contributor-guide/specification/index.rst.txt create mode 100644 versions/55.1.0/_sources/contributor-guide/specification/invariants.md.txt create mode 100644 versions/55.1.0/_sources/contributor-guide/specification/output-field-name-semantic.md.txt create mode 100644 versions/55.1.0/_sources/contributor-guide/testing.md.txt create mode 100644 versions/55.1.0/_sources/download.md.txt create mode 100644 versions/55.1.0/_sources/index.rst.txt create mode 100644 versions/55.1.0/_sources/library-user-guide/building-logical-plans.md.txt create mode 100644 versions/55.1.0/_sources/library-user-guide/catalogs.md.txt create mode 100644 versions/55.1.0/_sources/library-user-guide/custom-table-providers.md.txt create mode 100644 versions/55.1.0/_sources/library-user-guide/extending-operators.md.txt create mode 100644 versions/55.1.0/_sources/library-user-guide/extending-sql.md.txt create mode 100644 versions/55.1.0/_sources/library-user-guide/extensions.md.txt create mode 100644 versions/55.1.0/_sources/library-user-guide/functions/adding-udfs.md.txt create mode 100644 versions/55.1.0/_sources/library-user-guide/functions/index.rst.txt create mode 100644 versions/55.1.0/_sources/library-user-guide/functions/spark.md.txt create mode 100644 versions/55.1.0/_sources/library-user-guide/index.md.txt create mode 100644 versions/55.1.0/_sources/library-user-guide/profiling.md.txt create mode 100644 versions/55.1.0/_sources/library-user-guide/query-optimizer.md.txt create mode 100644 versions/55.1.0/_sources/library-user-guide/table-constraints.md.txt create mode 100644 versions/55.1.0/_sources/library-user-guide/upgrading/46.0.0.md.txt create mode 100644 versions/55.1.0/_sources/library-user-guide/upgrading/47.0.0.md.txt create mode 100644 versions/55.1.0/_sources/library-user-guide/upgrading/48.0.0.md.txt create mode 100644 versions/55.1.0/_sources/library-user-guide/upgrading/48.0.1.md.txt create mode 100644 versions/55.1.0/_sources/library-user-guide/upgrading/49.0.0.md.txt create mode 100644 versions/55.1.0/_sources/library-user-guide/upgrading/50.0.0.md.txt create mode 100644 versions/55.1.0/_sources/library-user-guide/upgrading/51.0.0.md.txt create mode 100644 versions/55.1.0/_sources/library-user-guide/upgrading/52.0.0.md.txt create mode 100644 versions/55.1.0/_sources/library-user-guide/upgrading/53.0.0.md.txt create mode 100644 versions/55.1.0/_sources/library-user-guide/upgrading/54.0.0.md.txt create mode 100644 versions/55.1.0/_sources/library-user-guide/upgrading/55.0.0.md.txt create mode 100644 versions/55.1.0/_sources/library-user-guide/upgrading/index.rst.txt create mode 100644 versions/55.1.0/_sources/library-user-guide/using-the-dataframe-api.md.txt create mode 100644 versions/55.1.0/_sources/library-user-guide/using-the-sql-api.md.txt create mode 100644 versions/55.1.0/_sources/library-user-guide/working-with-exprs.md.txt create mode 100644 versions/55.1.0/_sources/user-guide/arrow-introduction.md.txt create mode 100644 versions/55.1.0/_sources/user-guide/cli/datasources.md.txt create mode 100644 versions/55.1.0/_sources/user-guide/cli/functions.md.txt create mode 100644 versions/55.1.0/_sources/user-guide/cli/index.rst.txt create mode 100644 versions/55.1.0/_sources/user-guide/cli/installation.md.txt create mode 100644 versions/55.1.0/_sources/user-guide/cli/overview.md.txt create mode 100644 versions/55.1.0/_sources/user-guide/cli/usage.md.txt create mode 100644 versions/55.1.0/_sources/user-guide/concepts-readings-events.md.txt create mode 100644 versions/55.1.0/_sources/user-guide/configs.md.txt create mode 100644 versions/55.1.0/_sources/user-guide/crate-configuration.md.txt create mode 100644 versions/55.1.0/_sources/user-guide/dataframe.md.txt create mode 100644 versions/55.1.0/_sources/user-guide/example-usage.md.txt create mode 100644 versions/55.1.0/_sources/user-guide/explain-usage.md.txt create mode 100644 versions/55.1.0/_sources/user-guide/expressions.md.txt create mode 100644 versions/55.1.0/_sources/user-guide/faq.md.txt create mode 100644 versions/55.1.0/_sources/user-guide/features.md.txt create mode 100644 versions/55.1.0/_sources/user-guide/introduction.md.txt create mode 100644 versions/55.1.0/_sources/user-guide/metrics.md.txt create mode 100644 versions/55.1.0/_sources/user-guide/parquet-content-defined-chunking.md.txt create mode 100644 versions/55.1.0/_sources/user-guide/sql/aggregate_functions.md.txt create mode 100644 versions/55.1.0/_sources/user-guide/sql/data_types.md.txt create mode 100644 versions/55.1.0/_sources/user-guide/sql/ddl.md.txt create mode 100644 versions/55.1.0/_sources/user-guide/sql/dml.md.txt create mode 100644 versions/55.1.0/_sources/user-guide/sql/explain.md.txt create mode 100644 versions/55.1.0/_sources/user-guide/sql/format_options.md.txt create mode 100644 versions/55.1.0/_sources/user-guide/sql/index.rst.txt create mode 100644 versions/55.1.0/_sources/user-guide/sql/information_schema.md.txt create mode 100644 versions/55.1.0/_sources/user-guide/sql/operators.md.txt create mode 100644 versions/55.1.0/_sources/user-guide/sql/prepared_statements.md.txt create mode 100644 versions/55.1.0/_sources/user-guide/sql/scalar_functions.md.txt create mode 100644 versions/55.1.0/_sources/user-guide/sql/select.md.txt create mode 100644 versions/55.1.0/_sources/user-guide/sql/special_functions.md.txt create mode 100644 versions/55.1.0/_sources/user-guide/sql/struct_coercion.md.txt create mode 100644 versions/55.1.0/_sources/user-guide/sql/subqueries.md.txt create mode 100644 versions/55.1.0/_sources/user-guide/sql/window_functions.md.txt create mode 100644 versions/55.1.0/_static/2x_bgwhite_original.png create mode 100644 versions/55.1.0/_static/base-stemmer.js create mode 100644 versions/55.1.0/_static/basic.css create mode 100644 versions/55.1.0/_static/data/deps.svg create mode 100644 versions/55.1.0/_static/doctools.js create mode 100644 versions/55.1.0/_static/documentation_options.js create mode 100644 versions/55.1.0/_static/english-stemmer.js create mode 100644 versions/55.1.0/_static/favicon.svg create mode 100644 versions/55.1.0/_static/file.png create mode 100644 versions/55.1.0/_static/images/2x_bgwhite_original.png create mode 100644 versions/55.1.0/_static/images/flamegraph.svg create mode 100644 versions/55.1.0/_static/images/old_logo/DataFusion-Logo-Background-White.png create mode 100644 versions/55.1.0/_static/images/old_logo/DataFusion-Logo-Background-White.svg create mode 100644 versions/55.1.0/_static/images/old_logo/DataFusion-Logo-Dark.png create mode 100644 versions/55.1.0/_static/images/old_logo/DataFusion-Logo-Dark.svg create mode 100644 versions/55.1.0/_static/images/old_logo/DataFusion-Logo-Light.png create mode 100644 versions/55.1.0/_static/images/old_logo/DataFusion-Logo-Light.svg create mode 100644 versions/55.1.0/_static/images/original.png create mode 100644 versions/55.1.0/_static/images/original.svg create mode 100644 versions/55.1.0/_static/images/original2x.png create mode 100644 versions/55.1.0/_static/images/original_dark.svg create mode 100644 versions/55.1.0/_static/language_data.js create mode 100644 versions/55.1.0/_static/minus.png create mode 100644 versions/55.1.0/_static/original.svg create mode 100644 versions/55.1.0/_static/original_dark.svg create mode 100644 versions/55.1.0/_static/plus.png create mode 100644 versions/55.1.0/_static/pygments.css create mode 100644 versions/55.1.0/_static/scripts/bootstrap.js create mode 100644 versions/55.1.0/_static/scripts/bootstrap.js.LICENSE.txt create mode 100644 versions/55.1.0/_static/scripts/bootstrap.js.map create mode 100644 versions/55.1.0/_static/scripts/fontawesome.js create mode 100644 versions/55.1.0/_static/scripts/fontawesome.js.LICENSE.txt create mode 100644 versions/55.1.0/_static/scripts/fontawesome.js.map create mode 100644 versions/55.1.0/_static/scripts/pydata-sphinx-theme.js create mode 100644 versions/55.1.0/_static/scripts/pydata-sphinx-theme.js.map create mode 100644 versions/55.1.0/_static/searchtools.js create mode 100644 versions/55.1.0/_static/sphinx_highlight.js create mode 100644 versions/55.1.0/_static/styles/pydata-sphinx-theme.css create mode 100644 versions/55.1.0/_static/styles/pydata-sphinx-theme.css.map create mode 100644 versions/55.1.0/_static/styles/theme.css create mode 100644 versions/55.1.0/_static/theme_overrides.css create mode 100644 versions/55.1.0/_static/vendor/fontawesome/webfonts/fa-brands-400.woff2 create mode 100644 versions/55.1.0/_static/vendor/fontawesome/webfonts/fa-regular-400.woff2 create mode 100644 versions/55.1.0/_static/vendor/fontawesome/webfonts/fa-solid-900.woff2 create mode 100644 versions/55.1.0/_static/webpack-macros.html create mode 100644 versions/55.1.0/contributor-guide/api-health.html create mode 100644 versions/55.1.0/contributor-guide/architecture.html create mode 100644 versions/55.1.0/contributor-guide/architecture/dependency-graph.html create mode 100644 versions/55.1.0/contributor-guide/communication.html create mode 100644 versions/55.1.0/contributor-guide/development_environment.html create mode 100644 versions/55.1.0/contributor-guide/governance.html create mode 100644 versions/55.1.0/contributor-guide/gsoc/gsoc_application_guidelines_2025.html create mode 100644 versions/55.1.0/contributor-guide/gsoc/gsoc_project_ideas_2025.html create mode 100644 versions/55.1.0/contributor-guide/gsoc/index.html create mode 100644 versions/55.1.0/contributor-guide/howtos.html create mode 100644 versions/55.1.0/contributor-guide/index.html create mode 100644 versions/55.1.0/contributor-guide/inviting.html create mode 100644 versions/55.1.0/contributor-guide/pr_review.html create mode 100644 versions/55.1.0/contributor-guide/release_management.html create mode 100644 versions/55.1.0/contributor-guide/roadmap.html create mode 100644 versions/55.1.0/contributor-guide/specification/index.html create mode 100644 versions/55.1.0/contributor-guide/specification/invariants.html create mode 100644 versions/55.1.0/contributor-guide/specification/output-field-name-semantic.html create mode 100644 versions/55.1.0/contributor-guide/testing.html create mode 100644 versions/55.1.0/download.html create mode 100644 versions/55.1.0/genindex.html create mode 100644 versions/55.1.0/index.html create mode 100644 versions/55.1.0/library-user-guide/adding-udfs.html create mode 100644 versions/55.1.0/library-user-guide/building-logical-plans.html create mode 100644 versions/55.1.0/library-user-guide/catalogs.html create mode 100644 versions/55.1.0/library-user-guide/custom-table-providers.html create mode 100644 versions/55.1.0/library-user-guide/extending-operators.html create mode 100644 versions/55.1.0/library-user-guide/extending-sql.html create mode 100644 versions/55.1.0/library-user-guide/extensions.html create mode 100644 versions/55.1.0/library-user-guide/functions/adding-udfs.html create mode 100644 versions/55.1.0/library-user-guide/functions/index.html create mode 100644 versions/55.1.0/library-user-guide/functions/spark.html create mode 100644 versions/55.1.0/library-user-guide/index.html create mode 100644 versions/55.1.0/library-user-guide/profiling.html create mode 100644 versions/55.1.0/library-user-guide/query-optimizer.html create mode 100644 versions/55.1.0/library-user-guide/table-constraints.html create mode 100644 versions/55.1.0/library-user-guide/upgrading.html create mode 100644 versions/55.1.0/library-user-guide/upgrading/46.0.0.html create mode 100644 versions/55.1.0/library-user-guide/upgrading/47.0.0.html create mode 100644 versions/55.1.0/library-user-guide/upgrading/48.0.0.html create mode 100644 versions/55.1.0/library-user-guide/upgrading/48.0.1.html create mode 100644 versions/55.1.0/library-user-guide/upgrading/49.0.0.html create mode 100644 versions/55.1.0/library-user-guide/upgrading/50.0.0.html create mode 100644 versions/55.1.0/library-user-guide/upgrading/51.0.0.html create mode 100644 versions/55.1.0/library-user-guide/upgrading/52.0.0.html create mode 100644 versions/55.1.0/library-user-guide/upgrading/53.0.0.html create mode 100644 versions/55.1.0/library-user-guide/upgrading/54.0.0.html create mode 100644 versions/55.1.0/library-user-guide/upgrading/55.0.0.html create mode 100644 versions/55.1.0/library-user-guide/upgrading/index.html create mode 100644 versions/55.1.0/library-user-guide/using-the-dataframe-api.html create mode 100644 versions/55.1.0/library-user-guide/using-the-sql-api.html create mode 100644 versions/55.1.0/library-user-guide/working-with-exprs.html create mode 100644 versions/55.1.0/llms.txt create mode 100644 versions/55.1.0/objects.inv create mode 100644 versions/55.1.0/search.html create mode 100644 versions/55.1.0/searchindex.js create mode 100644 versions/55.1.0/sitemap.xml create mode 100644 versions/55.1.0/user-guide/arrow-introduction.html create mode 100644 versions/55.1.0/user-guide/cli/datasources.html create mode 100644 versions/55.1.0/user-guide/cli/functions.html create mode 100644 versions/55.1.0/user-guide/cli/index.html create mode 100644 versions/55.1.0/user-guide/cli/installation.html create mode 100644 versions/55.1.0/user-guide/cli/overview.html create mode 100644 versions/55.1.0/user-guide/cli/usage.html create mode 100644 versions/55.1.0/user-guide/concepts-readings-events.html create mode 100644 versions/55.1.0/user-guide/configs.html create mode 100644 versions/55.1.0/user-guide/crate-configuration.html create mode 100644 versions/55.1.0/user-guide/dataframe.html create mode 100644 versions/55.1.0/user-guide/example-usage.html create mode 100644 versions/55.1.0/user-guide/explain-usage.html create mode 100644 versions/55.1.0/user-guide/expressions.html create mode 100644 versions/55.1.0/user-guide/faq.html create mode 100644 versions/55.1.0/user-guide/features.html create mode 100644 versions/55.1.0/user-guide/introduction.html create mode 100644 versions/55.1.0/user-guide/metrics.html create mode 100644 versions/55.1.0/user-guide/parquet-content-defined-chunking.html create mode 100644 versions/55.1.0/user-guide/runtime_configs.html create mode 100644 versions/55.1.0/user-guide/sql/aggregate_functions.html create mode 100644 versions/55.1.0/user-guide/sql/data_types.html create mode 100644 versions/55.1.0/user-guide/sql/ddl.html create mode 100644 versions/55.1.0/user-guide/sql/dml.html create mode 100644 versions/55.1.0/user-guide/sql/explain.html create mode 100644 versions/55.1.0/user-guide/sql/format_options.html create mode 100644 versions/55.1.0/user-guide/sql/index.html create mode 100644 versions/55.1.0/user-guide/sql/information_schema.html create mode 100644 versions/55.1.0/user-guide/sql/operators.html create mode 100644 versions/55.1.0/user-guide/sql/prepared_statements.html create mode 100644 versions/55.1.0/user-guide/sql/scalar_functions.html create mode 100644 versions/55.1.0/user-guide/sql/select.html create mode 100644 versions/55.1.0/user-guide/sql/special_functions.html create mode 100644 versions/55.1.0/user-guide/sql/struct_coercion.html create mode 100644 versions/55.1.0/user-guide/sql/subqueries.html create mode 100644 versions/55.1.0/user-guide/sql/window_functions.html diff --git a/versions/55.0.0/_downloads/1e336e540124589d1e27210fa4f805f6/rust_lint.sh b/versions/55.0.0/_downloads/1e336e540124589d1e27210fa4f805f6/rust_lint.sh new file mode 100644 index 0000000000000..73cab9c7f70bd --- /dev/null +++ b/versions/55.0.0/_downloads/1e336e540124589d1e27210fa4f805f6/rust_lint.sh @@ -0,0 +1,126 @@ +#!/usr/bin/env bash + +# 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. + +# This script runs all the Rust lints locally the same way the +# DataFusion CI does +# +# Note: The installed checking tools (e.g., taplo) are not guaranteed to match +# the CI versions for simplicity, there might be some minor differences. Check +# `.github/workflows` for the CI versions. +# +# +# +# For each lint scripts: +# +# By default, they run in check mode: +# ./ci/scripts/rust_fmt.sh +# +# With `--write`, scripts perform best-effort auto fixes: +# ./ci/scripts/rust_fmt.sh --write +# +# The `--write` flag assumes a clean git repository (no uncommitted changes); to force +# auto fixes even if there are unstaged changes, use `--allow-dirty`: +# ./ci/scripts/rust_fmt.sh --write --allow-dirty +# +# New scripts can use `rust_fmt.sh` as a reference. + +set -euo pipefail + +usage() { + cat >&2 < /dev/null; then + echo "Installing $cmd using: $install_cmd" + eval "$install_cmd" + fi +} + +MODE="check" +ALLOW_DIRTY=0 + +while [[ $# -gt 0 ]]; do + case "$1" in + --write) + MODE="write" + ;; + --allow-dirty) + ALLOW_DIRTY=1 + ;; + -h|--help) + usage + ;; + *) + usage + ;; + esac + shift +done + +SCRIPT_NAME="$(basename "${BASH_SOURCE[0]}")" + +ensure_tool "taplo" "cargo install taplo-cli --locked" +ensure_tool "hawkeye" "cargo install hawkeye --locked" +ensure_tool "typos" "cargo install typos-cli --locked" + +run_step() { + local name="$1" + shift + echo "[${SCRIPT_NAME}] Running ${name}" + "$@" +} + +declare -a WRITE_STEPS=( + "ci/scripts/rust_fmt.sh|true" + "ci/scripts/rust_clippy.sh|true" + "ci/scripts/rust_toml_fmt.sh|true" + "ci/scripts/license_header.sh|true" + "ci/scripts/typos_check.sh|true" + "ci/scripts/doc_prettier_check.sh|true" +) + +declare -a READONLY_STEPS=( + "ci/scripts/check_no_cargo_install_in_workflows.sh|false" + "ci/scripts/rust_docs.sh|false" +) + +for entry in "${WRITE_STEPS[@]}" "${READONLY_STEPS[@]}"; do + IFS='|' read -r script_path supports_write <<<"$entry" + script_name="$(basename "$script_path")" + args=() + if [[ "$supports_write" == "true" && "$MODE" == "write" ]]; then + args+=(--write) + [[ $ALLOW_DIRTY -eq 1 ]] && args+=(--allow-dirty) + fi + if [[ ${#args[@]} -gt 0 ]]; then + run_step "$script_name" "$script_path" "${args[@]}" + else + run_step "$script_name" "$script_path" + fi +done diff --git a/versions/55.0.0/_downloads/3cce4d737d8c5814f5b50d859d21ba53/capitalized_example.csv b/versions/55.0.0/_downloads/3cce4d737d8c5814f5b50d859d21ba53/capitalized_example.csv new file mode 100644 index 0000000000000..dbc8f5c5a0a60 --- /dev/null +++ b/versions/55.0.0/_downloads/3cce4d737d8c5814f5b50d859d21ba53/capitalized_example.csv @@ -0,0 +1,5 @@ +A,b,c +1,2,3 +1,10,5 +2,5,6 +2,1,4 \ No newline at end of file diff --git a/versions/55.0.0/_downloads/4a91b8629bd17ce8ae200ee0b5345f19/rust_fmt.sh b/versions/55.0.0/_downloads/4a91b8629bd17ce8ae200ee0b5345f19/rust_fmt.sh new file mode 100644 index 0000000000000..16c87cea5e0fa --- /dev/null +++ b/versions/55.0.0/_downloads/4a91b8629bd17ce8ae200ee0b5345f19/rust_fmt.sh @@ -0,0 +1,68 @@ +#!/usr/bin/env bash +# +# 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. + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +SCRIPT_NAME="$(basename "${BASH_SOURCE[0]}")" +source "${SCRIPT_DIR}/utils/git.sh" + +MODE="check" +ALLOW_DIRTY=0 + +usage() { + cat >&2 <&2 <&2 < 0; +select distinct dict_10_required from t where dict_1000_optional is not NULL and i64_optional > 0; +select distinct dict_10_required from t where dict_1000_optional is not NULL and i64_required > 0; +select distinct dict_10_required from t where dict_1000_optional is not NULL and i64_required > 0; + +-- Test basic aggregations +select dict_10_optional, count(*) from t group by dict_10_optional; +select dict_10_optional, dict_100_optional, count(*) from t group by dict_10_optional, dict_100_optional; + +-- Test float aggregations +select dict_10_optional, dict_100_optional, MIN(f64_required), MAX(f64_required), AVG(f64_required) from t group by dict_10_optional, dict_100_optional; +select dict_10_optional, dict_100_optional, MIN(f64_optional), MAX(f64_optional), AVG(f64_optional) from t group by dict_10_optional, dict_100_optional; +select dict_10_required, dict_100_required, MIN(f64_optional), MAX(f64_optional), AVG(f64_optional) from t group by dict_10_required, dict_100_required; diff --git a/versions/55.0.0/_images/flamegraph.svg b/versions/55.0.0/_images/flamegraph.svg new file mode 100644 index 0000000000000..951cbb1ff3664 --- /dev/null +++ b/versions/55.0.0/_images/flamegraph.svg @@ -0,0 +1,491 @@ +Flame Graph Reset ZoomSearch datafusion-cli`<tokio::runtime::coop::with_budget::ResetGuard as core::ops::drop::Drop>::drop (16 samples, 0.02%)datafusion-cli`datafusion_cli::main_inner::_{{closure}} (91 samples, 0.11%)datafusion-cli`<tokio::runtime::coop::with_budget::ResetGuard as core::ops::drop::Drop>::drop (69 samples, 0.08%)datafusion-cli`datafusion_cli::exec::exec_from_files::_{{closure}} (19 samples, 0.02%)datafusion-cli`datafusion_cli::exec::exec_and_print::_{{closure}} (101 samples, 0.12%)datafusion-cli`<futures_util::stream::try_stream::try_collect::TryCollect<St,C> as core::future::future::Future>::poll (66 samples, 0.08%)datafusion-cli`<parquet::format::FileMetaData as parquet::thrift::TSerializable>::read_from_in_protocol (13 samples, 0.02%)datafusion-cli`<parquet::format::ColumnChunk as parquet::thrift::TSerializable>::read_from_in_protocol (13 samples, 0.02%)datafusion-cli`datafusion::datasource::file_format::parquet::fetch_parquet_metadata::_{{closure}} (19 samples, 0.02%)datafusion-cli`parquet::file::footer::decode_metadata (17 samples, 0.02%)datafusion-cli`<datafusion::datasource::listing::table::ListingTable as datafusion_catalog::table::TableProvider>::scan::_{{closure}} (23 samples, 0.03%)datafusion-cli`<futures_util::stream::stream::buffered::Buffered<St> as futures_core::stream::Stream>::poll_next (23 samples, 0.03%)datafusion-cli`<futures_util::stream::futures_ordered::FuturesOrdered<Fut> as futures_core::stream::Stream>::poll_next (23 samples, 0.03%)datafusion-cli`<futures_util::stream::futures_unordered::FuturesUnordered<Fut> as futures_core::stream::Stream>::poll_next (23 samples, 0.03%)datafusion-cli`<futures_util::stream::futures_ordered::OrderWrapper<T> as core::future::future::Future>::poll (23 samples, 0.03%)datafusion-cli`<datafusion::datasource::file_format::parquet::ParquetFormat as datafusion::datasource::file_format::FileFormat>::infer_stats::_{{closure}} (23 samples, 0.03%)datafusion-cli`<datafusion::execution::session_state::DefaultQueryPlanner as datafusion::execution::context::QueryPlanner>::create_physical_plan::_{{closure}} (25 samples, 0.03%)datafusion-cli`<datafusion::physical_planner::DefaultPhysicalPlanner as datafusion::physical_planner::PhysicalPlanner>::create_physical_plan::_{{closure}} (25 samples, 0.03%)datafusion-cli`datafusion::physical_planner::DefaultPhysicalPlanner::create_initial_plan::_{{closure}} (24 samples, 0.03%)datafusion-cli`<futures_util::stream::try_stream::try_collect::TryCollect<St,C> as core::future::future::Future>::poll (24 samples, 0.03%)datafusion-cli`<S as futures_core::stream::TryStream>::try_poll_next (24 samples, 0.03%)datafusion-cli`<futures_util::stream::futures_unordered::FuturesUnordered<Fut> as futures_core::stream::Stream>::poll_next (24 samples, 0.03%)datafusion-cli`datafusion::physical_planner::DefaultPhysicalPlanner::map_logical_node_to_physical::_{{closure}} (24 samples, 0.03%)datafusion-cli`<parquet::format::ColumnChunk as parquet::thrift::TSerializable>::read_from_in_protocol (20 samples, 0.02%)datafusion-cli`<parquet::format::FileMetaData as parquet::thrift::TSerializable>::read_from_in_protocol (24 samples, 0.03%)datafusion-cli`<S as futures_core::stream::TryStream>::try_poll_next (36 samples, 0.04%)datafusion-cli`<futures_util::stream::futures_ordered::FuturesOrdered<Fut> as futures_core::stream::Stream>::poll_next (36 samples, 0.04%)datafusion-cli`<futures_util::stream::futures_unordered::FuturesUnordered<Fut> as futures_core::stream::Stream>::poll_next (36 samples, 0.04%)datafusion-cli`<futures_util::stream::futures_ordered::OrderWrapper<T> as core::future::future::Future>::poll (36 samples, 0.04%)datafusion-cli`datafusion::datasource::file_format::parquet::fetch_parquet_metadata::_{{closure}} (35 samples, 0.04%)datafusion-cli`parquet::file::footer::decode_metadata (35 samples, 0.04%)datafusion-cli`<datafusion_cli::catalog::DynamicObjectStoreSchemaProvider as datafusion_catalog::schema::SchemaProvider>::table::_{{closure}} (39 samples, 0.05%)datafusion-cli`<datafusion_catalog::dynamic_file::catalog::DynamicFileSchemaProvider as datafusion_catalog::schema::SchemaProvider>::table::_{{closure}} (39 samples, 0.05%)datafusion-cli`<datafusion::datasource::dynamic_file::DynamicListTableFactory as datafusion_catalog::dynamic_file::catalog::UrlTableFactory>::try_new::_{{closure}} (39 samples, 0.05%)datafusion-cli`datafusion::datasource::listing::table::ListingOptions::infer_schema::_{{closure}} (39 samples, 0.05%)datafusion-cli`<datafusion::datasource::file_format::parquet::ParquetFormat as datafusion::datasource::file_format::FileFormat>::infer_schema::_{{closure}} (39 samples, 0.05%)datafusion-cli`<datafusion_physical_plan::sorts::merge::SortPreservingMergeStream<C> as futures_core::stream::Stream>::poll_next (63 samples, 0.07%)datafusion-cli`<alloc::collections::vec_deque::VecDeque<T,A> as core::clone::Clone>::clone (103 samples, 0.12%)datafusion-cli`mi_malloc_aligned (98 samples, 0.12%)datafusion-cli`<alloc::collections::vec_deque::VecDeque<T,A> as core::clone::Clone>::clone (1,443 samples, 1.69%)libdyld.dylib`tlv_get_addr (57 samples, 0.07%)datafusion-cli`<datafusion_physical_plan::sorts::stream::FieldCursorStream<T> as datafusion_physical_plan::sorts::stream::PartitionedStream>::poll_next (57 samples, 0.07%)datafusion-cli`<datafusion_physical_plan::stream::RecordBatchStreamAdapter<S> as futures_core::stream::Stream>::poll_next (26 samples, 0.03%)datafusion-cli`<futures_util::stream::select_with_strategy::SelectWithStrategy<St1,St2,Clos,State> as futures_core::stream::Stream>::poll_next (57 samples, 0.07%)datafusion-cli`<futures_util::stream::once::Once<Fut> as futures_core::stream::Stream>::poll_next (61 samples, 0.07%)datafusion-cli`tokio::task::join_set::JoinSet<T>::poll_join_next (212 samples, 0.25%)datafusion-cli`tokio::util::idle_notified_set::IdleNotifiedSet<T>::pop_notified (147 samples, 0.17%)datafusion-cli`<futures_util::stream::once::Once<Fut> as futures_core::stream::Stream>::poll_next (361 samples, 0.42%)datafusion-cli`tokio::util::idle_notified_set::IdleNotifiedSet<T>::pop_notified (36 samples, 0.04%)datafusion-cli`<futures_util::stream::stream::filter_map::FilterMap<St,Fut,F> as futures_core::stream::Stream>::poll_next (511 samples, 0.60%)datafusion-cli`tokio::task::join_set::JoinSet<T>::poll_join_next (35 samples, 0.04%)datafusion-cli`<tokio::runtime::coop::RestoreOnPending as core::ops::drop::Drop>::drop (88 samples, 0.10%)datafusion-cli`<tokio::runtime::coop::RestoreOnPending as core::ops::drop::Drop>::drop (58 samples, 0.07%)datafusion-cli`tokio::runtime::park::clone (39 samples, 0.05%)datafusion-cli`tokio::sync::mpsc::list::Rx<T>::pop (276 samples, 0.32%)datafusion-cli`tokio::sync::task::atomic_waker::AtomicWaker::register_by_ref (169 samples, 0.20%)datafusion-cli`tokio::runtime::park::drop_waker (36 samples, 0.04%)datafusion-cli`tokio::sync::mpsc::chan::Rx<T,S>::recv (780 samples, 0.92%)libdyld.dylib`tlv_get_addr (68 samples, 0.08%)datafusion-cli`tokio::sync::mpsc::list::Rx<T>::pop (74 samples, 0.09%)datafusion-cli`tokio::sync::task::atomic_waker::AtomicWaker::register_by_ref (52 samples, 0.06%)datafusion-cli`<futures_util::stream::unfold::Unfold<T,F,Fut> as futures_core::stream::Stream>::poll_next (1,241 samples, 1.46%)libdyld.dylib`tlv_get_addr (88 samples, 0.10%)datafusion-cli`<futures_util::stream::select_with_strategy::SelectWithStrategy<St1,St2,Clos,State> as futures_core::stream::Stream>::poll_next (2,014 samples, 2.37%)da..datafusion-cli`tokio::sync::mpsc::chan::Rx<T,S>::recv (32 samples, 0.04%)datafusion-cli`<futures_util::stream::stream::filter_map::FilterMap<St,Fut,F> as futures_core::stream::Stream>::poll_next (90 samples, 0.11%)datafusion-cli`<futures_util::stream::unfold::Unfold<T,F,Fut> as futures_core::stream::Stream>::poll_next (47 samples, 0.06%)datafusion-cli`<datafusion_physical_plan::sorts::stream::FieldCursorStream<T> as datafusion_physical_plan::sorts::stream::PartitionedStream>::poll_next (2,396 samples, 2.81%)da..datafusion-cli`datafusion_physical_plan::sorts::stream::FusedStreams::poll_next (2,284 samples, 2.68%)da..datafusion-cli`futures_util::stream::select::select::round_robin (26 samples, 0.03%)datafusion-cli`datafusion_physical_plan::sorts::merge::SortPreservingMergeStream<C>::maybe_poll_stream (2,570 samples, 3.02%)dat..datafusion-cli`datafusion_physical_plan::sorts::stream::FusedStreams::poll_next (81 samples, 0.10%)datafusion-cli`<datafusion_physical_plan::sorts::merge::SortPreservingMergeStream<C> as futures_core::stream::Stream>::poll_next (4,392 samples, 5.16%)datafu..datafusion-cli`tokio::runtime::park::wake_by_ref (25 samples, 0.03%)datafusion-cli`datafusion_physical_plan::sorts::merge::SortPreservingMergeStream<C>::maybe_poll_stream (36 samples, 0.04%)datafusion-cli`mi_free (86 samples, 0.10%)datafusion-cli`<futures_util::stream::try_stream::try_collect::TryCollect<St,C> as core::future::future::Future>::poll (4,796 samples, 5.63%)datafus..datafusion-cli`tokio::runtime::park::wake_by_ref (23 samples, 0.03%)datafusion-cli`datafusion_cli::exec::exec_from_files::_{{closure}} (5,412 samples, 6.36%)datafusi..datafusion-cli`datafusion_cli::exec::exec_from_lines::_{{closure}} (5,240 samples, 6.15%)datafusi..datafusion-cli`datafusion_cli::exec::exec_and_print::_{{closure}} (5,081 samples, 5.97%)datafusi..datafusion-cli`datafusion_cli::main_inner::_{{closure}} (5,579 samples, 6.55%)datafusio..datafusion-cli`datafusion_cli::exec::exec_from_lines::_{{closure}} (61 samples, 0.07%)datafusion-cli`tokio::runtime::park::CachedParkThread::park (39 samples, 0.05%)datafusion-cli`tokio::runtime::park::Inner::park (62 samples, 0.07%)datafusion-cli`tokio::runtime::park::CachedParkThread::block_on (5,952 samples, 6.99%)datafusio..libdyld.dylib`tlv_get_addr (77 samples, 0.09%)datafusion-cli`tokio::runtime::park::Inner::park (27 samples, 0.03%)datafusion-cli`std::rt::lang_start::_{{closure}} (6,136 samples, 7.21%)datafusion..datafusion-cli`std::sys::backtrace::__rust_begin_short_backtrace (6,136 samples, 7.21%)datafusion..datafusion-cli`datafusion_cli::main (6,136 samples, 7.21%)datafusion..libdyld.dylib`tlv_get_addr (50 samples, 0.06%)datafusion-cli`main (6,137 samples, 7.21%)datafusion..datafusion-cli`std::rt::lang_start_internal (6,137 samples, 7.21%)datafusion..datafusion-cli`mi_arenas_try_purge (51 samples, 0.06%)datafusion-cli`mi_arena_purge (51 samples, 0.06%)libsystem_kernel.dylib`madvise (51 samples, 0.06%)dyld`start (6,193 samples, 7.27%)dyld`startlibdyld.dylib`dyld4::LibSystemHelpers::getenv (56 samples, 0.07%)libsystem_c.dylib`exit (56 samples, 0.07%)libsystem_c.dylib`__cxa_finalize_ranges (56 samples, 0.07%)datafusion-cli`mi_process_done (56 samples, 0.07%)libsystem_kernel.dylib`__exit (47 samples, 0.06%)datafusion-cli`parking_lot::condvar::Condvar::wait_until_internal (21 samples, 0.02%)libsystem_kernel.dylib`__psynch_cvwait (20 samples, 0.02%)datafusion-cli`tokio::runtime::scheduler::multi_thread::park::Parker::park (30 samples, 0.04%)datafusion-cli`tokio::runtime::time::Driver::park_internal (9 samples, 0.01%)datafusion-cli`tokio::runtime::scheduler::multi_thread::worker::Context::park_timeout (31 samples, 0.04%)datafusion-cli`<datafusion_functions_aggregate::average::AvgGroupsAccumulator<T,F> as datafusion_expr_common::groups_accumulator::GroupsAccumulator>::merge_batch (32 samples, 0.04%)datafusion-cli`datafusion_functions_aggregate_common::aggregate::groups_accumulator::accumulate::NullState::accumulate (25 samples, 0.03%)datafusion-cli`<datafusion_functions_aggregate::count::CountGroupsAccumulator as datafusion_expr_common::groups_accumulator::GroupsAccumulator>::merge_batch (12 samples, 0.01%)datafusion-cli`_mi_malloc_generic (18 samples, 0.02%)datafusion-cli`mi_heap_malloc_zero_aligned_at_generic (19 samples, 0.02%)datafusion-cli`mi_malloc_aligned (12 samples, 0.01%)datafusion-cli`<alloc::vec::Vec<T,A> as core::clone::Clone>::clone (36 samples, 0.04%)datafusion-cli`core::ptr::drop_in_place<datafusion_common::scalar::ScalarValue> (13 samples, 0.02%)datafusion-cli`mi_heap_malloc_zero_aligned_at_generic (10 samples, 0.01%)datafusion-cli`_mi_malloc_generic (9 samples, 0.01%)datafusion-cli`mi_malloc_aligned (9 samples, 0.01%)libsystem_platform.dylib`_platform_memcmp (28 samples, 0.03%)datafusion-cli`<datafusion_functions_aggregate::min_max::MinAccumulator as datafusion_expr_common::accumulator::Accumulator>::update_batch (179 samples, 0.21%)libsystem_platform.dylib`_platform_memmove (23 samples, 0.03%)datafusion-cli`mi_malloc_aligned (10 samples, 0.01%)datafusion-cli`alloc::raw_vec::RawVec<T,A>::grow_one (27 samples, 0.03%)datafusion-cli`alloc::raw_vec::finish_grow (27 samples, 0.03%)datafusion-cli`core::ptr::drop_in_place<arrow_array::array::byte_array::GenericByteArray<arrow_array::types::GenericBinaryType<i32>>> (11 samples, 0.01%)datafusion-cli`alloc::sync::Arc<T,A>::drop_slow (22 samples, 0.03%)datafusion-cli`datafusion_common::scalar::ScalarValue::size (27 samples, 0.03%)datafusion-cli`mi_heap_realloc_zero_aligned_at (9 samples, 0.01%)datafusion-cli`arrow_select::take::take_bytes (61 samples, 0.07%)datafusion-cli`arrow_buffer::buffer::mutable::MutableBuffer::reallocate (49 samples, 0.06%)libsystem_platform.dylib`_platform_memmove (40 samples, 0.05%)datafusion-cli`datafusion_common::utils::get_arrayref_at_indices (142 samples, 0.17%)datafusion-cli`core::iter::adapters::try_process (142 samples, 0.17%)datafusion-cli`<alloc::vec::Vec<T> as alloc::vec::spec_from_iter::SpecFromIter<T,I>>::from_iter (142 samples, 0.17%)datafusion-cli`<core::iter::adapters::map::Map<I,F> as core::iter::traits::iterator::Iterator>::try_fold (142 samples, 0.17%)datafusion-cli`arrow_select::take::take (142 samples, 0.17%)datafusion-cli`arrow_select::take::take_impl (141 samples, 0.17%)libsystem_platform.dylib`_platform_memmove (77 samples, 0.09%)datafusion-cli`alloc::raw_vec::RawVec<T,A>::grow_one (17 samples, 0.02%)datafusion-cli`alloc::raw_vec::finish_grow (17 samples, 0.02%)libsystem_platform.dylib`_platform_memmove (17 samples, 0.02%)datafusion-cli`datafusion_common::scalar::ScalarValue::size (10 samples, 0.01%)datafusion-cli`<datafusion_common::scalar::ScalarValue as core::convert::TryFrom<&arrow_schema::datatype::DataType>>::try_from (14 samples, 0.02%)datafusion-cli`_mi_free_delayed_block (11 samples, 0.01%)datafusion-cli`mi_find_page (10 samples, 0.01%)datafusion-cli`mi_heap_malloc_zero_aligned_at_generic (28 samples, 0.03%)datafusion-cli`_mi_malloc_generic (28 samples, 0.03%)datafusion-cli`<datafusion_functions_aggregate::min_max::Min as datafusion_expr::udaf::AggregateUDFImpl>::accumulator (44 samples, 0.05%)datafusion-cli`datafusion_functions_aggregate_common::aggregate::groups_accumulator::GroupsAccumulatorAdapter::make_accumulators_if_needed (110 samples, 0.13%)datafusion-cli`datafusion_physical_expr::aggregate::AggregateFunctionExpr::create_accumulator (66 samples, 0.08%)datafusion-cli`_mi_malloc_generic (9 samples, 0.01%)datafusion-cli`mi_heap_malloc_zero_aligned_at_generic (10 samples, 0.01%)datafusion-cli`mi_malloc_aligned (13 samples, 0.02%)datafusion-cli`<arrow_array::array::byte_array::GenericByteArray<T> as arrow_array::array::Array>::slice (68 samples, 0.08%)datafusion-cli`mi_malloc_aligned (10 samples, 0.01%)datafusion-cli`<alloc::vec::Vec<T> as alloc::vec::spec_from_iter::SpecFromIter<T,I>>::from_iter (93 samples, 0.11%)datafusion-cli`datafusion_functions_aggregate_common::aggregate::groups_accumulator::slice_and_maybe_filter (115 samples, 0.14%)datafusion-cli`mi_free_block_delayed_mt (11 samples, 0.01%)datafusion-cli`<datafusion_functions_aggregate_common::aggregate::groups_accumulator::GroupsAccumulatorAdapter as datafusion_expr_common::groups_accumulator::GroupsAccumulator>::merge_batch (865 samples, 1.02%)libsystem_platform.dylib`_platform_memmove (83 samples, 0.10%)datafusion-cli`<hashbrown::raw::inner::RawTable<T> as datafusion_common::utils::proxy::RawTableAllocExt>::insert_accounted (52 samples, 0.06%)datafusion-cli`hashbrown::raw::inner::RawTable<T,A>::reserve_rehash (32 samples, 0.04%)datafusion-cli`<str as datafusion_common::hash_utils::HashValue>::hash_one (61 samples, 0.07%)datafusion-cli`arrow_buffer::buffer::mutable::MutableBuffer::reallocate (69 samples, 0.08%)libsystem_platform.dylib`_platform_memmove (64 samples, 0.08%)datafusion-cli`datafusion_physical_expr_common::binary_map::ArrowBytesMap<O,V>::insert_if_new (299 samples, 0.35%)libsystem_platform.dylib`_platform_memcmp (13 samples, 0.02%)datafusion-cli`<datafusion_physical_plan::aggregates::group_values::bytes::GroupValuesByes<O> as datafusion_physical_plan::aggregates::group_values::GroupValues>::intern (380 samples, 0.45%)libsystem_platform.dylib`_platform_memmove (62 samples, 0.07%)datafusion-cli`<alloc::vec::Vec<T> as alloc::vec::spec_from_iter::SpecFromIter<T,I>>::from_iter (13 samples, 0.02%)datafusion-cli`mi_heap_malloc_zero_aligned_at_generic (13 samples, 0.02%)datafusion-cli`_mi_malloc_generic (13 samples, 0.02%)datafusion-cli`_mi_free_delayed_block (13 samples, 0.02%)datafusion-cli`_mi_page_free (15 samples, 0.02%)datafusion-cli`mi_segment_page_clear (15 samples, 0.02%)datafusion-cli`mi_segment_span_free_coalesce (15 samples, 0.02%)datafusion-cli`mi_segment_span_free (15 samples, 0.02%)datafusion-cli`mi_segment_try_purge (15 samples, 0.02%)datafusion-cli`mi_segment_purge (15 samples, 0.02%)libsystem_kernel.dylib`madvise (15 samples, 0.02%)datafusion-cli`_mi_free_delayed_block (67 samples, 0.08%)datafusion-cli`<alloc::vec::Vec<T> as alloc::vec::spec_from_iter::SpecFromIter<T,I>>::from_iter (70 samples, 0.08%)datafusion-cli`mi_heap_malloc_zero_aligned_at_generic (69 samples, 0.08%)datafusion-cli`_mi_malloc_generic (69 samples, 0.08%)datafusion-cli`arrow_data::transform::variable_size::build_extend::_{{closure}} (13 samples, 0.02%)datafusion-cli`arrow_data::transform::utils::extend_offsets (13 samples, 0.02%)datafusion-cli`arrow_data::transform::MutableArrayData::extend (67 samples, 0.08%)libsystem_platform.dylib`_platform_memmove (54 samples, 0.06%)datafusion-cli`arrow_select::concat::concat (146 samples, 0.17%)datafusion-cli`arrow_select::concat::concat_fallback (144 samples, 0.17%)datafusion-cli`arrow_select::concat::concat_batches (164 samples, 0.19%)datafusion-cli`alloc::sync::Arc<T,A>::drop_slow (10 samples, 0.01%)datafusion-cli`datafusion_physical_plan::coalesce::BatchCoalescer::finish_batch (175 samples, 0.21%)datafusion-cli`core::ptr::drop_in_place<arrow_array::record_batch::RecordBatch> (11 samples, 0.01%)datafusion-cli`<alloc::vec::Vec<T> as alloc::vec::spec_from_iter::SpecFromIter<T,I>>::from_iter (29 samples, 0.03%)datafusion-cli`mi_heap_malloc_zero_aligned_at_generic (29 samples, 0.03%)datafusion-cli`_mi_malloc_generic (29 samples, 0.03%)datafusion-cli`_mi_free_delayed_block (29 samples, 0.03%)datafusion-cli`datafusion_physical_plan::coalesce::BatchCoalescer::push_batch (30 samples, 0.04%)datafusion-cli`<datafusion_physical_plan::coalesce_batches::CoalesceBatchesStream as futures_core::stream::Stream>::poll_next (218 samples, 0.26%)datafusion-cli`datafusion_functions_aggregate_common::aggregate::groups_accumulator::slice_and_maybe_filter (13 samples, 0.02%)datafusion-cli`_mi_free_delayed_block (128 samples, 0.15%)datafusion-cli`mi_heap_malloc_zero_aligned_at_generic (137 samples, 0.16%)datafusion-cli`_mi_malloc_generic (137 samples, 0.16%)datafusion-cli`mi_malloc_aligned (31 samples, 0.04%)datafusion-cli`<alloc::vec::Vec<T,A> as core::clone::Clone>::clone (182 samples, 0.21%)datafusion-cli`<datafusion_functions_aggregate::min_max::MaxAccumulator as datafusion_expr_common::accumulator::Accumulator>::evaluate (357 samples, 0.42%)datafusion-cli`<datafusion_common::scalar::ScalarValue as core::clone::Clone>::clone (353 samples, 0.41%)libsystem_platform.dylib`_platform_memmove (135 samples, 0.16%)datafusion-cli`core::ptr::drop_in_place<datafusion_common::scalar::ScalarValue> (12 samples, 0.01%)datafusion-cli`core::ptr::drop_in_place<datafusion_functions_aggregate_common::aggregate::groups_accumulator::AccumulatorState> (31 samples, 0.04%)datafusion-cli`mi_free_block_mt (11 samples, 0.01%)datafusion-cli`datafusion_common::scalar::ScalarValue::size (16 samples, 0.02%)datafusion-cli`mi_free (12 samples, 0.01%)datafusion-cli`mi_free_block_delayed_mt (53 samples, 0.06%)datafusion-cli`mi_free_block_mt (33 samples, 0.04%)datafusion-cli`<alloc::vec::into_iter::IntoIter<T,A> as core::iter::traits::iterator::Iterator>::try_fold (555 samples, 0.65%)datafusion-cli`mi_free_generic_mt (17 samples, 0.02%)datafusion-cli`alloc::raw_vec::RawVec<T,A>::reserve::do_reserve_and_handle (27 samples, 0.03%)datafusion-cli`alloc::raw_vec::finish_grow (27 samples, 0.03%)libsystem_platform.dylib`_platform_memmove (24 samples, 0.03%)datafusion-cli`mi_free_block_delayed_mt (9 samples, 0.01%)datafusion-cli`core::iter::adapters::try_process (650 samples, 0.76%)datafusion-cli`alloc::vec::in_place_collect::_<impl alloc::vec::spec_from_iter::SpecFromIter<T,I> for alloc::vec::Vec<T>>::from_iter (647 samples, 0.76%)datafusion-cli`<core::iter::adapters::map::Map<I,F> as core::iter::traits::iterator::Iterator>::try_fold (16 samples, 0.02%)datafusion-cli`<alloc::vec::into_iter::IntoIter<T,A> as core::iter::traits::iterator::Iterator>::try_fold (14 samples, 0.02%)datafusion-cli`mi_heap_realloc_zero_aligned_at (9 samples, 0.01%)datafusion-cli`arrow_buffer::buffer::mutable::MutableBuffer::reallocate (43 samples, 0.05%)libsystem_platform.dylib`_platform_memmove (34 samples, 0.04%)datafusion-cli`<arrow_array::array::byte_array::GenericByteArray<T> as core::iter::traits::collect::FromIterator<core::option::Option<Ptr>>>::from_iter (90 samples, 0.11%)datafusion-cli`datafusion_physical_plan::aggregates::row_hash::GroupedHashAggregateStream::set_input_done_and_produce_output (856 samples, 1.01%)datafusion-cli`datafusion_physical_plan::aggregates::row_hash::GroupedHashAggregateStream::emit (856 samples, 1.01%)datafusion-cli`<datafusion_functions_aggregate_common::aggregate::groups_accumulator::GroupsAccumulatorAdapter as datafusion_expr_common::groups_accumulator::GroupsAccumulator>::evaluate (856 samples, 1.01%)datafusion-cli`datafusion_common::scalar::ScalarValue::iter_to_array (206 samples, 0.24%)datafusion-cli`core::iter::adapters::try_process (206 samples, 0.24%)libsystem_platform.dylib`_platform_memmove (104 samples, 0.12%)datafusion-cli`mi_free (20 samples, 0.02%)datafusion-cli`<datafusion_physical_plan::aggregates::row_hash::GroupedHashAggregateStream as futures_core::stream::Stream>::poll_next (2,428 samples, 2.85%)da..datafusion-cli`<datafusion_physical_plan::projection::ProjectionStream as futures_core::stream::Stream>::poll_next (2,437 samples, 2.86%)da..datafusion-cli`<datafusion_physical_plan::coalesce_batches::CoalesceBatchesStream as futures_core::stream::Stream>::poll_next (2,437 samples, 2.86%)da..datafusion-cli`<datafusion_physical_plan::filter::FilterExecStream as futures_core::stream::Stream>::poll_next (2,436 samples, 2.86%)da..datafusion-cli`datafusion_physical_plan::common::spawn_buffered::_{{closure}} (2,439 samples, 2.86%)da..datafusion-cli`<futures_util::stream::try_stream::try_flatten::TryFlatten<St> as futures_core::stream::Stream>::poll_next (2,439 samples, 2.86%)da..datafusion-cli`<S as futures_core::stream::TryStream>::try_poll_next (2,438 samples, 2.86%)da..datafusion-cli`arrow_buffer::buffer::mutable::MutableBuffer::reallocate (47 samples, 0.06%)libsystem_platform.dylib`_platform_memmove (39 samples, 0.05%)datafusion-cli`arrow_select::take::take_bytes (79 samples, 0.09%)datafusion-cli`arrow_select::take::take_primitive (9 samples, 0.01%)datafusion-cli`<core::iter::adapters::map::Map<I,F> as core::iter::traits::iterator::Iterator>::next (261 samples, 0.31%)datafusion-cli`core::iter::adapters::try_process (260 samples, 0.31%)datafusion-cli`<alloc::vec::Vec<T> as alloc::vec::spec_from_iter::SpecFromIter<T,I>>::from_iter (260 samples, 0.31%)datafusion-cli`<core::iter::adapters::map::Map<I,F> as core::iter::traits::iterator::Iterator>::try_fold (260 samples, 0.31%)datafusion-cli`arrow_select::take::take (259 samples, 0.30%)datafusion-cli`arrow_select::take::take_impl (257 samples, 0.30%)libsystem_platform.dylib`_platform_memmove (158 samples, 0.19%)datafusion-cli`<datafusion_functions_aggregate::average::AvgGroupsAccumulator<T,F> as datafusion_expr_common::groups_accumulator::GroupsAccumulator>::update_batch (265 samples, 0.31%)datafusion-cli`datafusion_functions_aggregate_common::aggregate::groups_accumulator::accumulate::NullState::accumulate (263 samples, 0.31%)datafusion-cli`<datafusion_functions_aggregate::count::CountGroupsAccumulator as datafusion_expr_common::groups_accumulator::GroupsAccumulator>::update_batch (122 samples, 0.14%)datafusion-cli`datafusion_functions_aggregate_common::aggregate::groups_accumulator::accumulate::accumulate_indices (121 samples, 0.14%)datafusion-cli`<datafusion_functions_aggregate::min_max::MinAccumulator as datafusion_expr_common::accumulator::Accumulator>::update_batch (9 samples, 0.01%)datafusion-cli`mi_find_page (13 samples, 0.02%)datafusion-cli`mi_heap_malloc_zero_aligned_at_generic (20 samples, 0.02%)datafusion-cli`_mi_malloc_generic (20 samples, 0.02%)datafusion-cli`mi_malloc_aligned (22 samples, 0.03%)datafusion-cli`<alloc::vec::Vec<T,A> as core::clone::Clone>::clone (59 samples, 0.07%)datafusion-cli`DYLD-STUB$$memcmp (53 samples, 0.06%)datafusion-cli`arrow_arith::aggregate::min_max_helper (227 samples, 0.27%)datafusion-cli`core::ptr::drop_in_place<datafusion_common::scalar::ScalarValue> (13 samples, 0.02%)datafusion-cli`mi_malloc_aligned (17 samples, 0.02%)libsystem_platform.dylib`_platform_memcmp (395 samples, 0.46%)datafusion-cli`<datafusion_functions_aggregate::min_max::MinAccumulator as datafusion_expr_common::accumulator::Accumulator>::update_batch (913 samples, 1.07%)libsystem_platform.dylib`_platform_memmove (61 samples, 0.07%)datafusion-cli`__rust_dealloc (9 samples, 0.01%)datafusion-cli`_mi_malloc_generic (16 samples, 0.02%)datafusion-cli`mi_find_page (14 samples, 0.02%)datafusion-cli`_mi_heap_realloc_zero (20 samples, 0.02%)datafusion-cli`_mi_page_free (10 samples, 0.01%)datafusion-cli`mi_segment_page_clear (10 samples, 0.01%)datafusion-cli`mi_segment_span_free_coalesce (10 samples, 0.01%)datafusion-cli`mi_segment_span_free (10 samples, 0.01%)datafusion-cli`mi_segment_try_purge (10 samples, 0.01%)datafusion-cli`mi_segment_purge (10 samples, 0.01%)libsystem_kernel.dylib`madvise (10 samples, 0.01%)datafusion-cli`mi_find_page (12 samples, 0.01%)datafusion-cli`mi_heap_malloc_zero_aligned_at_generic (17 samples, 0.02%)datafusion-cli`_mi_malloc_generic (17 samples, 0.02%)datafusion-cli`alloc::raw_vec::RawVec<T,A>::grow_one (77 samples, 0.09%)datafusion-cli`alloc::raw_vec::finish_grow (72 samples, 0.08%)libsystem_platform.dylib`_platform_memmove (16 samples, 0.02%)datafusion-cli`_mi_page_free (24 samples, 0.03%)datafusion-cli`mi_segment_page_clear (24 samples, 0.03%)datafusion-cli`mi_segment_span_free_coalesce (24 samples, 0.03%)datafusion-cli`mi_segment_span_free (23 samples, 0.03%)datafusion-cli`mi_segment_try_purge (23 samples, 0.03%)datafusion-cli`mi_segment_purge (23 samples, 0.03%)libsystem_kernel.dylib`madvise (22 samples, 0.03%)datafusion-cli`_mi_heap_realloc_zero (27 samples, 0.03%)datafusion-cli`_mi_malloc_generic (26 samples, 0.03%)datafusion-cli`mi_find_page (26 samples, 0.03%)datafusion-cli`alloc::raw_vec::RawVec<T,A>::reserve::do_reserve_and_handle (44 samples, 0.05%)datafusion-cli`alloc::raw_vec::finish_grow (44 samples, 0.05%)libsystem_platform.dylib`_platform_memmove (15 samples, 0.02%)datafusion-cli`alloc::raw_vec::finish_grow (10 samples, 0.01%)datafusion-cli`core::ptr::drop_in_place<arrow_array::array::byte_array::GenericByteArray<arrow_array::types::GenericBinaryType<i32>>> (22 samples, 0.03%)datafusion-cli`alloc::sync::Arc<T,A>::drop_slow (35 samples, 0.04%)datafusion-cli`datafusion_common::scalar::ScalarValue::size (71 samples, 0.08%)datafusion-cli`DYLD-STUB$$memcpy (10 samples, 0.01%)datafusion-cli`_mi_malloc_generic (9 samples, 0.01%)datafusion-cli`mi_find_page (9 samples, 0.01%)datafusion-cli`mi_heap_malloc_zero_aligned_at_generic (10 samples, 0.01%)datafusion-cli`mi_heap_realloc_zero_aligned_at (29 samples, 0.03%)datafusion-cli`mi_heap_malloc_zero_aligned_at_overalloc (16 samples, 0.02%)datafusion-cli`_mi_malloc_generic (16 samples, 0.02%)datafusion-cli`mi_page_fresh_alloc (14 samples, 0.02%)datafusion-cli`mi_segments_page_alloc (14 samples, 0.02%)datafusion-cli`arrow_buffer::buffer::mutable::MutableBuffer::reallocate (352 samples, 0.41%)libsystem_platform.dylib`_platform_memmove (322 samples, 0.38%)datafusion-cli`arrow_select::take::take_bytes (567 samples, 0.67%)datafusion-cli`<core::iter::adapters::map::Map<I,F> as core::iter::traits::iterator::Iterator>::try_fold (1,437 samples, 1.69%)datafusion-cli`arrow_select::take::take (1,437 samples, 1.69%)datafusion-cli`arrow_select::take::take_impl (1,436 samples, 1.69%)libsystem_platform.dylib`_platform_memmove (859 samples, 1.01%)datafusion-cli`<alloc::vec::Vec<T> as alloc::vec::spec_from_iter::SpecFromIter<T,I>>::from_iter (1,438 samples, 1.69%)datafusion-cli`datafusion_common::utils::get_arrayref_at_indices (1,439 samples, 1.69%)datafusion-cli`core::iter::adapters::try_process (1,439 samples, 1.69%)datafusion-cli`alloc::raw_vec::RawVec<T,A>::grow_one (11 samples, 0.01%)datafusion-cli`alloc::raw_vec::finish_grow (11 samples, 0.01%)libsystem_platform.dylib`_platform_memmove (10 samples, 0.01%)datafusion-cli`<datafusion_common::scalar::ScalarValue as core::convert::TryFrom<&arrow_schema::datatype::DataType>>::try_from (16 samples, 0.02%)datafusion-cli`<datafusion_common::scalar::ScalarValue as core::convert::TryFrom<&arrow_schema::datatype::DataType>>::try_from (9 samples, 0.01%)datafusion-cli`_mi_page_free (37 samples, 0.04%)datafusion-cli`mi_segment_page_clear (37 samples, 0.04%)datafusion-cli`mi_segment_span_free_coalesce (37 samples, 0.04%)datafusion-cli`mi_segment_span_free (36 samples, 0.04%)datafusion-cli`mi_segment_try_purge (36 samples, 0.04%)datafusion-cli`mi_segment_purge (36 samples, 0.04%)libsystem_kernel.dylib`madvise (36 samples, 0.04%)datafusion-cli`mi_heap_malloc_zero_aligned_at_generic (41 samples, 0.05%)datafusion-cli`_mi_malloc_generic (41 samples, 0.05%)datafusion-cli`mi_find_page (39 samples, 0.05%)datafusion-cli`<datafusion_functions_aggregate::min_max::Min as datafusion_expr::udaf::AggregateUDFImpl>::accumulator (64 samples, 0.08%)datafusion-cli`datafusion_functions_aggregate_common::aggregate::groups_accumulator::GroupsAccumulatorAdapter::make_accumulators_if_needed (135 samples, 0.16%)datafusion-cli`datafusion_physical_expr::aggregate::AggregateFunctionExpr::create_accumulator (89 samples, 0.10%)datafusion-cli`arrow_buffer::buffer::immutable::Buffer::slice_with_length (9 samples, 0.01%)datafusion-cli`arrow_buffer::buffer::scalar::ScalarBuffer<T>::new (10 samples, 0.01%)datafusion-cli`mi_malloc_aligned (27 samples, 0.03%)datafusion-cli`<arrow_array::array::byte_array::GenericByteArray<T> as arrow_array::array::Array>::slice (92 samples, 0.11%)datafusion-cli`mi_malloc_aligned (12 samples, 0.01%)datafusion-cli`<alloc::vec::Vec<T> as alloc::vec::spec_from_iter::SpecFromIter<T,I>>::from_iter (123 samples, 0.14%)datafusion-cli`datafusion_functions_aggregate_common::aggregate::groups_accumulator::slice_and_maybe_filter (142 samples, 0.17%)datafusion-cli`mi_free (20 samples, 0.02%)datafusion-cli`<datafusion_functions_aggregate_common::aggregate::groups_accumulator::GroupsAccumulatorAdapter as datafusion_expr_common::groups_accumulator::GroupsAccumulator>::update_batch (3,755 samples, 4.41%)dataf..libsystem_platform.dylib`_platform_memmove (96 samples, 0.11%)datafusion-cli`DYLD-STUB$$memcmp (19 samples, 0.02%)datafusion-cli`<hashbrown::raw::inner::RawTable<T> as datafusion_common::utils::proxy::RawTableAllocExt>::insert_accounted (65 samples, 0.08%)datafusion-cli`hashbrown::raw::inner::RawTable<T,A>::reserve_rehash (46 samples, 0.05%)datafusion-cli`<str as datafusion_common::hash_utils::HashValue>::hash_one (424 samples, 0.50%)datafusion-cli`mi_heap_realloc_zero_aligned_at (9 samples, 0.01%)datafusion-cli`arrow_buffer::buffer::mutable::MutableBuffer::reallocate (53 samples, 0.06%)libsystem_platform.dylib`_platform_memmove (44 samples, 0.05%)datafusion-cli`datafusion_physical_expr_common::binary_map::ArrowBytesMap<O,V>::insert_if_new (1,298 samples, 1.52%)datafusion-cli`datafusion_common::hash_utils::create_hashes (18 samples, 0.02%)libsystem_platform.dylib`_platform_memcmp (381 samples, 0.45%)datafusion-cli`<datafusion_physical_plan::aggregates::group_values::bytes::GroupValuesByes<O> as datafusion_physical_plan::aggregates::group_values::GroupValues>::intern (1,777 samples, 2.09%)d..libsystem_platform.dylib`_platform_memmove (65 samples, 0.08%)datafusion-cli`<parquet::format::ColumnChunk as parquet::thrift::TSerializable>::read_from_in_protocol (18 samples, 0.02%)datafusion-cli`<parquet::format::FileMetaData as parquet::thrift::TSerializable>::read_from_in_protocol (25 samples, 0.03%)datafusion-cli`<parquet::arrow::async_reader::store::ParquetObjectReader as parquet::arrow::async_reader::AsyncFileReader>::get_metadata::_{{closure}} (43 samples, 0.05%)datafusion-cli`parquet::file::footer::decode_metadata (39 samples, 0.05%)datafusion-cli`<datafusion::datasource::physical_plan::parquet::opener::ParquetOpener as datafusion::datasource::physical_plan::file_stream::FileOpener>::open::_{{closure}} (54 samples, 0.06%)datafusion-cli`alloc::raw_vec::RawVec<T,A>::reserve::do_reserve_and_handle (59 samples, 0.07%)datafusion-cli`alloc::raw_vec::finish_grow (58 samples, 0.07%)libsystem_platform.dylib`_platform_memmove (51 samples, 0.06%)datafusion-cli`parquet::arrow::buffer::offset_buffer::OffsetBuffer<I>::try_push (202 samples, 0.24%)datafusion-cli`parquet::arrow::array_reader::byte_array::ByteArrayDecoderPlain::read (960 samples, 1.13%)libsystem_platform.dylib`_platform_memmove (620 samples, 0.73%)datafusion-cli`parquet::arrow::buffer::offset_buffer::OffsetBuffer<I>::try_push (111 samples, 0.13%)datafusion-cli`parquet::arrow::buffer::offset_buffer::OffsetBuffer<I>::extend_from_dictionary (90 samples, 0.11%)datafusion-cli`alloc::raw_vec::RawVec<T,A>::reserve::do_reserve_and_handle (49 samples, 0.06%)datafusion-cli`alloc::raw_vec::finish_grow (49 samples, 0.06%)libsystem_platform.dylib`_platform_memmove (40 samples, 0.05%)datafusion-cli`parquet::encodings::rle::RleDecoder::get_batch (14 samples, 0.02%)datafusion-cli`parquet::arrow::decoder::dictionary_index::DictIndexDecoder::read (266 samples, 0.31%)libsystem_platform.dylib`_platform_memmove (159 samples, 0.19%)datafusion-cli`parquet::arrow::array_reader::byte_array::ByteArrayDecoderPlain::read (44 samples, 0.05%)libsystem_platform.dylib`_platform_memmove (33 samples, 0.04%)datafusion-cli`<parquet::arrow::array_reader::byte_array::ByteArrayColumnValueDecoder<I> as parquet::column::reader::decoder::ColumnValueDecoder>::set_dict (46 samples, 0.05%)datafusion-cli`snap::decompress::Decoder::decompress (2,824 samples, 3.32%)dat..datafusion-cli`<parquet::compression::snappy_codec::SnappyCodec as parquet::compression::Codec>::decompress (2,966 samples, 3.48%)dat..libsystem_platform.dylib`_platform_memmove (142 samples, 0.17%)datafusion-cli`parquet::file::serialized_reader::decode_page (3,024 samples, 3.55%)data..libsystem_platform.dylib`__bzero (54 samples, 0.06%)libsystem_platform.dylib`__bzero (32 samples, 0.04%)libsystem_platform.dylib`_platform_memmove (43 samples, 0.05%)datafusion-cli`<parquet::file::serialized_reader::SerializedPageReader<R> as parquet::column::page::PageReader>::get_next_page (3,116 samples, 3.66%)data..datafusion-cli`parquet::column::reader::GenericColumnReader<R,D,V>::read_new_page (3,165 samples, 3.72%)data..datafusion-cli`<parquet::arrow::arrow_reader::ParquetRecordBatchReader as core::iter::traits::iterator::Iterator>::next (4,517 samples, 5.31%)datafu..datafusion-cli`<parquet::arrow::array_reader::struct_array::StructArrayReader as parquet::arrow::array_reader::ArrayReader>::read_records (4,509 samples, 5.30%)datafu..datafusion-cli`<parquet::arrow::array_reader::byte_array::ByteArrayReader<I> as parquet::arrow::array_reader::ArrayReader>::read_records (4,508 samples, 5.29%)datafu..datafusion-cli`parquet::arrow::record_reader::GenericRecordReader<V,CV>::read_records (4,508 samples, 5.29%)datafu..datafusion-cli`parquet::column::reader::GenericColumnReader<R,D,V>::read_records (4,508 samples, 5.29%)datafu..datafusion-cli`core::ptr::drop_in_place<parquet::arrow::arrow_reader::ParquetRecordBatchReader> (11 samples, 0.01%)datafusion-cli`core::ptr::drop_in_place<parquet::arrow::array_reader::struct_array::StructArrayReader> (11 samples, 0.01%)datafusion-cli`<alloc::vec::Vec<T,A> as core::ops::drop::Drop>::drop (11 samples, 0.01%)datafusion-cli`<futures_util::stream::stream::map::Map<St,F> as futures_core::stream::Stream>::poll_next (4,535 samples, 5.33%)datafus..datafusion-cli`<futures_util::stream::stream::map::Map<St,F> as futures_core::stream::Stream>::poll_next (4,532 samples, 5.32%)datafus..datafusion-cli`<S as futures_core::stream::TryStream>::try_poll_next (4,532 samples, 5.32%)datafus..datafusion-cli`<datafusion::datasource::physical_plan::file_stream::FileStream<F> as futures_core::stream::Stream>::poll_next (4,596 samples, 5.40%)datafus..datafusion-cli`DYLD-STUB$$memcmp (17 samples, 0.02%)datafusion-cli`arrow_ord::cmp::apply_op (201 samples, 0.24%)datafusion-cli`arrow_ord::cmp::compare_op (253 samples, 0.30%)datafusion-cli`arrow_ord::cmp::compare_op::_{{closure}} (252 samples, 0.30%)libsystem_platform.dylib`_platform_memcmp (33 samples, 0.04%)datafusion-cli`<datafusion_physical_expr::expressions::binary::BinaryExpr as datafusion_physical_expr_common::physical_expr::PhysicalExpr>::evaluate (260 samples, 0.31%)datafusion-cli`datafusion_physical_expr_common::datum::apply_cmp (258 samples, 0.30%)datafusion-cli`<arrow_buffer::util::bit_iterator::BitIndexIterator as core::iter::traits::iterator::Iterator>::next (16 samples, 0.02%)datafusion-cli`<arrow_buffer::util::bit_iterator::BitSliceIterator as core::iter::traits::iterator::Iterator>::next (15 samples, 0.02%)datafusion-cli`DYLD-STUB$$memcpy (12 samples, 0.01%)datafusion-cli`arrow_select::filter::FilterBytes<OffsetSize>::extend_idx (117 samples, 0.14%)datafusion-cli`arrow_buffer::buffer::mutable::MutableBuffer::reallocate (63 samples, 0.07%)libsystem_platform.dylib`_platform_memmove (58 samples, 0.07%)datafusion-cli`mi_heap_realloc_zero_aligned_at (15 samples, 0.02%)datafusion-cli`arrow_select::filter::FilterBytes<OffsetSize>::extend_slices (336 samples, 0.39%)datafusion-cli`arrow_buffer::buffer::mutable::MutableBuffer::reallocate (199 samples, 0.23%)libsystem_platform.dylib`_platform_memmove (182 samples, 0.21%)datafusion-cli`datafusion_physical_plan::filter::filter_and_project (1,154 samples, 1.36%)datafusion-cli`arrow_select::filter::filter_record_batch (892 samples, 1.05%)datafusion-cli`core::iter::adapters::try_process (891 samples, 1.05%)datafusion-cli`<alloc::vec::Vec<T> as alloc::vec::spec_from_iter::SpecFromIter<T,I>>::from_iter (890 samples, 1.05%)datafusion-cli`arrow_select::filter::filter_array (890 samples, 1.05%)datafusion-cli`arrow_select::filter::filter_bytes (889 samples, 1.04%)libsystem_platform.dylib`_platform_memmove (393 samples, 0.46%)datafusion-cli`<datafusion_physical_plan::filter::FilterExecStream as futures_core::stream::Stream>::poll_next (5,753 samples, 6.76%)datafusio..datafusion-cli`arrow_data::transform::variable_size::build_extend::_{{closure}} (62 samples, 0.07%)datafusion-cli`arrow_data::transform::utils::extend_offsets (62 samples, 0.07%)datafusion-cli`arrow_data::transform::MutableArrayData::extend (227 samples, 0.27%)libsystem_platform.dylib`_platform_memmove (165 samples, 0.19%)datafusion-cli`arrow_select::concat::concat_batches (232 samples, 0.27%)datafusion-cli`arrow_select::concat::concat (231 samples, 0.27%)datafusion-cli`arrow_select::concat::concat_fallback (231 samples, 0.27%)datafusion-cli`<datafusion_physical_plan::coalesce_batches::CoalesceBatchesStream as futures_core::stream::Stream>::poll_next (5,987 samples, 7.03%)datafusio..datafusion-cli`datafusion_physical_plan::coalesce::BatchCoalescer::finish_batch (233 samples, 0.27%)datafusion-cli`<core::iter::adapters::map::Map<I,F> as core::iter::traits::iterator::Iterator>::try_fold (1,376 samples, 1.62%)datafusion-cli`<datafusion_physical_expr::expressions::cast::CastExpr as datafusion_physical_expr_common::physical_expr::PhysicalExpr>::evaluate (1,376 samples, 1.62%)datafusion-cli`datafusion_expr_common::columnar_value::ColumnarValue::cast_to (1,376 samples, 1.62%)datafusion-cli`arrow_cast::cast::string::cast_binary_to_string (1,376 samples, 1.62%)datafusion-cli`arrow_array::array::string_array::_<impl arrow_array::array::byte_array::GenericByteArray<arrow_array::types::GenericStringType<OffsetSize>>>::try_from_binary (1,375 samples, 1.61%)datafusion-cli`<arrow_array::types::GenericStringType<O> as arrow_array::types::ByteArrayType>::validate (1,375 samples, 1.61%)datafusion-cli`core::str::converts::from_utf8 (1,316 samples, 1.55%)datafusion-cli`<datafusion_physical_plan::projection::ProjectionStream as futures_core::stream::Stream>::poll_next (7,365 samples, 8.65%)datafusion-c..datafusion-cli`core::iter::adapters::try_process (1,378 samples, 1.62%)datafusion-cli`<alloc::vec::Vec<T> as alloc::vec::spec_from_iter::SpecFromIter<T,I>>::from_iter (1,378 samples, 1.62%)datafusion-cli`core::ops::function::impls::_<impl core::ops::function::FnOnce<A> for &mut F>::call_once (254 samples, 0.30%)datafusion-cli`core::str::count::char_count_general_case (90 samples, 0.11%)datafusion-cli`core::str::count::do_count_chars (1,371 samples, 1.61%)datafusion-cli`<alloc::vec::Vec<T> as alloc::vec::spec_from_iter::SpecFromIter<T,I>>::from_iter (1,878 samples, 2.21%)d..datafusion-cli`core::ops::function::impls::_<impl core::ops::function::FnOnce<A> for &mut F>::call_once (41 samples, 0.05%)datafusion-cli`<arrow_buffer::buffer::immutable::Buffer as core::iter::traits::collect::FromIterator<T>>::from_iter (1,929 samples, 2.27%)d..datafusion-cli`<arrow_array::array::primitive_array::PrimitiveArray<T> as core::iter::traits::collect::FromIterator<Ptr>>::from_iter (1,932 samples, 2.27%)d..datafusion-cli`<datafusion_functions::unicode::character_length::CharacterLengthFunc as datafusion_expr::udf::ScalarUDFImpl>::invoke (1,935 samples, 2.27%)d..datafusion-cli`datafusion_functions::utils::make_scalar_function::_{{closure}} (1,935 samples, 2.27%)d..datafusion-cli`<datafusion_physical_expr::scalar_function::ScalarFunctionExpr as datafusion_physical_expr_common::physical_expr::PhysicalExpr>::evaluate (1,937 samples, 2.27%)d..datafusion-cli`arrow_array::array::primitive_array::PrimitiveArray<T>::try_unary (24 samples, 0.03%)datafusion-cli`mi_heap_malloc_zero_aligned_at_generic (18 samples, 0.02%)datafusion-cli`_mi_malloc_generic (18 samples, 0.02%)datafusion-cli`mi_find_page (16 samples, 0.02%)datafusion-cli`_mi_page_free (15 samples, 0.02%)datafusion-cli`mi_segment_page_clear (15 samples, 0.02%)datafusion-cli`mi_segment_span_free_coalesce (15 samples, 0.02%)datafusion-cli`mi_segment_span_free (15 samples, 0.02%)datafusion-cli`mi_segment_try_purge (15 samples, 0.02%)datafusion-cli`mi_segment_purge (15 samples, 0.02%)libsystem_kernel.dylib`madvise (15 samples, 0.02%)datafusion-cli`arrow_cast::cast::cast_numeric_arrays (36 samples, 0.04%)libsystem_platform.dylib`__bzero (11 samples, 0.01%)datafusion-cli`<datafusion_physical_expr::expressions::cast::CastExpr as datafusion_physical_expr_common::physical_expr::PhysicalExpr>::evaluate (1,979 samples, 2.32%)d..datafusion-cli`datafusion_expr_common::columnar_value::ColumnarValue::cast_to (40 samples, 0.05%)datafusion-cli`<core::iter::adapters::map::Map<I,F> as core::iter::traits::iterator::Iterator>::try_fold (1,994 samples, 2.34%)d..datafusion-cli`datafusion_expr_common::columnar_value::ColumnarValue::into_array (15 samples, 0.02%)datafusion-cli`datafusion_common::scalar::ScalarValue::to_array_of_size (15 samples, 0.02%)datafusion-cli`arrow_array::array::primitive_array::PrimitiveArray<T>::from_value (15 samples, 0.02%)datafusion-cli`<alloc::vec::Vec<T> as alloc::vec::spec_from_iter::SpecFromIter<T,I>>::from_iter (1,995 samples, 2.34%)d..datafusion-cli`core::iter::adapters::try_process (1,995 samples, 2.34%)d..datafusion-cli`<alloc::vec::Vec<T> as alloc::vec::spec_from_iter::SpecFromIter<T,I>>::from_iter (1,995 samples, 2.34%)d..datafusion-cli`core::iter::adapters::try_process (1,996 samples, 2.34%)d..datafusion-cli`core::ptr::drop_in_place<arrow_array::array::byte_array::GenericByteArray<arrow_array::types::GenericStringType<i32>>> (9 samples, 0.01%)datafusion-cli`alloc::sync::Arc<T,A>::drop_slow (9 samples, 0.01%)datafusion-cli`core::ptr::drop_in_place<alloc::vec::Vec<alloc::sync::Arc<dyn arrow_array::array::Array>>> (16 samples, 0.02%)datafusion-cli`alloc::sync::Arc<T,A>::drop_slow (14 samples, 0.02%)datafusion-cli`datafusion_common::scalar::ScalarValue::size (17 samples, 0.02%)datafusion-cli`datafusion_functions_aggregate_common::aggregate::groups_accumulator::slice_and_maybe_filter (19 samples, 0.02%)datafusion-cli`DYLD-STUB$$memcpy (28 samples, 0.03%)datafusion-cli`__rust_dealloc (11 samples, 0.01%)datafusion-cli`<core::iter::adapters::map::Map<I,F> as core::iter::traits::iterator::Iterator>::try_fold (132 samples, 0.16%)datafusion-cli`arrow_data::data::ArrayDataBuilder::build (452 samples, 0.53%)datafusion-cli`arrow_data::data::ArrayData::validate_values (452 samples, 0.53%)datafusion-cli`core::str::converts::from_utf8 (319 samples, 0.37%)datafusion-cli`<&str as regex::regex::string::Replacer>::no_expansion (50 samples, 0.06%)datafusion-cli`<core::iter::adapters::enumerate::Enumerate<I> as core::iter::traits::iterator::Iterator>::next (112 samples, 0.13%)datafusion-cli`DYLD-STUB$$memcpy (28 samples, 0.03%)datafusion-cli`core::ptr::drop_in_place<core::iter::adapters::peekable::Peekable<core::iter::adapters::enumerate::Enumerate<regex::regex::string::CaptureMatches>>> (25 samples, 0.03%)datafusion-cli`mi_free (112 samples, 0.13%)datafusion-cli`<&str as regex::regex::string::Replacer>::no_expansion (24 samples, 0.03%)datafusion-cli`regex_automata::util::determinize::next (16 samples, 0.02%)datafusion-cli`regex_automata::hybrid::dfa::Lazy::cache_next_state (37 samples, 0.04%)datafusion-cli`regex_automata::hybrid::regex::Regex::try_search (7,837 samples, 9.20%)datafusion-cl..datafusion-cli`regex_automata::hybrid::search::find_fwd (7,526 samples, 8.84%)datafusion-c..datafusion-cli`regex_automata::hybrid::search::find_fwd (93 samples, 0.11%)datafusion-cli`regex_automata::nfa::thompson::backtrack::BoundedBacktracker::search_imp (76 samples, 0.09%)datafusion-cli`DYLD-STUB$$bzero (46 samples, 0.05%)datafusion-cli`regex_automata::nfa::thompson::backtrack::BoundedBacktracker::search_imp (42,705 samples, 50.16%)datafusion-cli`regex_automata::nfa::thompson::backtrack::BoundedBacktracker::search..libsystem_platform.dylib`__bzero (54 samples, 0.06%)datafusion-cli`regex_automata::nfa::thompson::backtrack::BoundedBacktracker::try_search_slots (43,651 samples, 51.27%)datafusion-cli`regex_automata::nfa::thompson::backtrack::BoundedBacktracker::try_sea..datafusion-cli`regex_automata::nfa::thompson::backtrack::BoundedBacktracker::try_search_slots_imp (43,543 samples, 51.14%)datafusion-cli`regex_automata::nfa::thompson::backtrack::BoundedBacktracker::try_sea..libsystem_platform.dylib`_platform_memset (632 samples, 0.74%)datafusion-cli`regex_automata::meta::strategy::Core::search_slots_nofail (43,848 samples, 51.50%)datafusion-cli`regex_automata::meta::strategy::Core::search_slots_nofaildatafusion-cli`regex_automata::nfa::thompson::backtrack::BoundedBacktracker::try_search_slots_imp (51 samples, 0.06%)datafusion-cli`<regex_automata::meta::strategy::Core as regex_automata::meta::strategy::Strategy>::search_slots (51,998 samples, 61.07%)datafusion-cli`<regex_automata::meta::strategy::Core as regex_automata::meta::strategy::Strategy>::se..datafusion-cli`regex_automata::nfa::thompson::backtrack::BoundedBacktracker::try_search_slots (53 samples, 0.06%)datafusion-cli`_mi_malloc_generic (12 samples, 0.01%)datafusion-cli`mi_heap_malloc_zero_aligned_at_generic (16 samples, 0.02%)datafusion-cli`mi_malloc_aligned (108 samples, 0.13%)datafusion-cli`regex_automata::hybrid::regex::Regex::try_search (59 samples, 0.07%)datafusion-cli`regex_automata::meta::strategy::Core::search_slots_nofail (24 samples, 0.03%)datafusion-cli`<core::iter::adapters::enumerate::Enumerate<I> as core::iter::traits::iterator::Iterator>::next (52,763 samples, 61.97%)datafusion-cli`<core::iter::adapters::enumerate::Enumerate<I> as core::iter::traits::iterator::Iterator..libdyld.dylib`tlv_get_addr (32 samples, 0.04%)datafusion-cli`<regex_automata::meta::strategy::Core as regex_automata::meta::strategy::Strategy>::search_slots (57 samples, 0.07%)datafusion-cli`<regex_automata::meta::strategy::ReverseAnchored as regex_automata::meta::strategy::Strategy>::group_info (28 samples, 0.03%)datafusion-cli`DYLD-STUB$$bzero (23 samples, 0.03%)datafusion-cli`DYLD-STUB$$memcpy (14 samples, 0.02%)datafusion-cli`core::ptr::drop_in_place<core::iter::adapters::peekable::Peekable<core::iter::adapters::enumerate::Enumerate<regex::regex::string::CaptureMatches>>> (158 samples, 0.19%)datafusion-cli`core::ptr::drop_in_place<regex_automata::util::pool::PoolGuard<regex_automata::meta::regex::Cache,alloc::boxed::Box<dyn core::ops::function::Fn<()>+Output = regex_automata::meta::regex::Cache+core::panic::unwind_safe::UnwindSafe+core::marker::Sync+core::panic::unwind_safe::RefUnwindSafe+core::marker::Send>>> (101 samples, 0.12%)datafusion-cli`core::ptr::drop_in_place<regex_automata::util::pool::PoolGuard<regex_automata::meta::regex::Cache,alloc::boxed::Box<dyn core::ops::function::Fn<()>+Output = regex_automata::meta::regex::Cache+core::panic::unwind_safe::UnwindSafe+core::marker::Sync+core::panic::unwind_safe::RefUnwindSafe+core::marker::Send>>> (92 samples, 0.11%)datafusion-cli`mi_free (95 samples, 0.11%)datafusion-cli`_mi_free_delayed_block (34 samples, 0.04%)datafusion-cli`_mi_malloc_generic (62 samples, 0.07%)datafusion-cli`mi_find_page (19 samples, 0.02%)datafusion-cli`mi_heap_malloc_zero_aligned_at_generic (77 samples, 0.09%)datafusion-cli`mi_malloc_aligned (125 samples, 0.15%)datafusion-cli`regex::find_byte::find_byte (87 samples, 0.10%)datafusion-cli`regex_automata::meta::regex::Regex::create_captures (56 samples, 0.07%)datafusion-cli`_mi_malloc_generic (18 samples, 0.02%)datafusion-cli`mi_find_page (9 samples, 0.01%)datafusion-cli`mi_heap_malloc_zero_aligned_at_generic (21 samples, 0.02%)datafusion-cli`mi_malloc_aligned (135 samples, 0.16%)datafusion-cli`regex_automata::util::captures::Captures::all (412 samples, 0.48%)libdyld.dylib`tlv_get_addr (26 samples, 0.03%)datafusion-cli`DYLD-STUB$$memcpy (55 samples, 0.06%)datafusion-cli`regex_automata::util::captures::Captures::interpolate_string_into::_{{closure}} (48 samples, 0.06%)datafusion-cli`regex_automata::util::interpolate::find_cap_ref (74 samples, 0.09%)datafusion-cli`DYLD-STUB$$memcpy (13 samples, 0.02%)datafusion-cli`core::num::_<impl core::str::traits::FromStr for usize>::from_str (32 samples, 0.04%)datafusion-cli`core::str::converts::from_utf8 (15 samples, 0.02%)datafusion-cli`regex_automata::util::captures::Captures::interpolate_string_into::_{{closure}} (154 samples, 0.18%)datafusion-cli`core::num::_<impl core::str::traits::FromStr for usize>::from_str (95 samples, 0.11%)datafusion-cli`regex_automata::util::interpolate::find_cap_ref (302 samples, 0.35%)datafusion-cli`core::str::converts::from_utf8 (94 samples, 0.11%)datafusion-cli`regex_automata::util::interpolate::string (1,197 samples, 1.41%)libsystem_platform.dylib`_platform_memmove (479 samples, 0.56%)datafusion-cli`regex_automata::util::captures::Captures::interpolate_string_into (1,508 samples, 1.77%)d..libsystem_platform.dylib`_platform_memmove (92 samples, 0.11%)datafusion-cli`regex_automata::util::interpolate::string (62 samples, 0.07%)datafusion-cli`regex_automata::hybrid::dfa::Cache::new (16 samples, 0.02%)datafusion-cli`regex_automata::hybrid::dfa::Lazy::init_cache (15 samples, 0.02%)datafusion-cli`regex_automata::util::pool::inner::Pool<T,F>::get_slow (18 samples, 0.02%)datafusion-cli`<regex_automata::meta::strategy::Core as regex_automata::meta::strategy::Strategy>::create_cache (18 samples, 0.02%)libdyld.dylib`tlv_get_addr (37 samples, 0.04%)libsystem_platform.dylib`__bzero (14 samples, 0.02%)libsystem_platform.dylib`_platform_memmove (215 samples, 0.25%)datafusion-cli`regex::regex::string::Regex::replacen (57,245 samples, 67.23%)datafusion-cli`regex::regex::string::Regex::replacenlibsystem_platform.dylib`_platform_memset (154 samples, 0.18%)datafusion-cli`regex_automata::util::captures::Captures::all (116 samples, 0.14%)datafusion-cli`regex_automata::util::captures::Captures::interpolate_string_into (40 samples, 0.05%)libdyld.dylib`tlv_get_addr (41 samples, 0.05%)datafusion-cli`core::iter::traits::iterator::Iterator::fold (58,157 samples, 68.30%)datafusion-cli`core::iter::traits::iterator::Iterator::foldlibsystem_platform.dylib`_platform_memmove (114 samples, 0.13%)datafusion-cli`_mi_page_free (9 samples, 0.01%)datafusion-cli`mi_segment_page_clear (9 samples, 0.01%)datafusion-cli`_mi_page_free (10 samples, 0.01%)datafusion-cli`mi_segment_page_clear (10 samples, 0.01%)datafusion-cli`mi_segment_span_free_coalesce (10 samples, 0.01%)datafusion-cli`mi_segment_span_free (10 samples, 0.01%)datafusion-cli`mi_segment_try_purge (10 samples, 0.01%)datafusion-cli`mi_segment_purge (10 samples, 0.01%)libsystem_kernel.dylib`madvise (10 samples, 0.01%)datafusion-cli`core::ptr::drop_in_place<regex_automata::hybrid::dfa::Cache> (17 samples, 0.02%)datafusion-cli`core::ptr::drop_in_place<regex_automata::meta::regex::Cache> (28 samples, 0.03%)datafusion-cli`core::ptr::drop_in_place<regex_automata::util::pool::Pool<regex_automata::meta::regex::Cache,alloc::boxed::Box<dyn core::ops::function::Fn<()>+Output = regex_automata::meta::regex::Cache+core::panic::unwind_safe::UnwindSafe+core::marker::Sync+core::panic::unwind_safe::RefUnwindSafe+core::marker::Send>>> (36 samples, 0.04%)datafusion-cli`core::ptr::drop_in_place<regex::regex::string::Regex> (38 samples, 0.04%)datafusion-cli`mi_free (128 samples, 0.15%)datafusion-cli`core::ptr::drop_in_place<regex_automata::nfa::thompson::compiler::Compiler> (25 samples, 0.03%)datafusion-cli`core::ptr::drop_in_place<core::cell::RefCell<regex_automata::nfa::thompson::compiler::Utf8State>> (24 samples, 0.03%)datafusion-cli`regex_automata::meta::wrappers::Hybrid::new (11 samples, 0.01%)datafusion-cli`regex_automata::dfa::onepass::Builder::build_from_nfa (10 samples, 0.01%)datafusion-cli`regex_automata::meta::wrappers::OnePass::new (14 samples, 0.02%)datafusion-cli`regex_automata::nfa::thompson::compiler::Compiler::c (19 samples, 0.02%)datafusion-cli`regex_automata::nfa::thompson::compiler::Compiler::c (22 samples, 0.03%)datafusion-cli`regex_automata::nfa::thompson::compiler::Compiler::c_at_least (21 samples, 0.02%)datafusion-cli`regex_automata::nfa::thompson::compiler::Compiler::c_at_least (12 samples, 0.01%)datafusion-cli`DYLD-STUB$$memcpy (32 samples, 0.04%)datafusion-cli`alloc::vec::Vec<T,A>::extend_with (27 samples, 0.03%)datafusion-cli`regex_automata::nfa::thompson::compiler::Utf8Compiler::new (128 samples, 0.15%)datafusion-cli`regex_automata::nfa::thompson::map::Utf8BoundedMap::clear (127 samples, 0.15%)datafusion-cli`<T as alloc::vec::spec_from_elem::SpecFromElem>::from_elem (127 samples, 0.15%)libsystem_platform.dylib`_platform_memmove (67 samples, 0.08%)datafusion-cli`<core::iter::adapters::map::Map<I,F> as core::iter::traits::iterator::Iterator>::next (180 samples, 0.21%)datafusion-cli`regex_automata::nfa::thompson::compiler::Compiler::c_cap (158 samples, 0.19%)datafusion-cli`regex_automata::nfa::thompson::compiler::Compiler::c (156 samples, 0.18%)datafusion-cli`regex_automata::nfa::thompson::compiler::Compiler::c_cap (140 samples, 0.16%)datafusion-cli`regex_automata::nfa::thompson::compiler::Compiler::c_at_least (139 samples, 0.16%)datafusion-cli`regex_automata::nfa::thompson::compiler::Compiler::c (138 samples, 0.16%)datafusion-cli`regex_automata::nfa::thompson::nfa::Inner::into_nfa (9 samples, 0.01%)datafusion-cli`regex_automata::nfa::thompson::builder::Builder::build (23 samples, 0.03%)datafusion-cli`regex_automata::nfa::thompson::compiler::Compiler::compile (206 samples, 0.24%)datafusion-cli`regex_automata::meta::strategy::new (259 samples, 0.30%)datafusion-cli`regex_syntax::ast::parse::ParserI<P>::parse_with_comments (26 samples, 0.03%)datafusion-cli`regex_syntax::ast::parse::Parser::parse (28 samples, 0.03%)datafusion-cli`<regex_syntax::hir::translate::TranslatorI as regex_syntax::ast::visitor::Visitor>::visit_post (18 samples, 0.02%)datafusion-cli`regex_syntax::hir::translate::Translator::translate (23 samples, 0.03%)datafusion-cli`regex_syntax::ast::visitor::visit (23 samples, 0.03%)datafusion-cli`regex::regex::string::Regex::new (322 samples, 0.38%)datafusion-cli`regex::builders::Builder::build_one_string (322 samples, 0.38%)datafusion-cli`regex_automata::meta::regex::Builder::build (321 samples, 0.38%)datafusion-cli`regex::regex::string::Regex::replacen (75 samples, 0.09%)datafusion-cli`datafusion_functions::regex::regexpreplace::regexp_replace_func (59,574 samples, 69.97%)datafusion-cli`datafusion_functions::regex::regexpreplace::regexp_replace_funclibsystem_platform.dylib`_platform_memmove (344 samples, 0.40%)datafusion-cli`<datafusion_functions::regex::regexpreplace::RegexpReplaceFunc as datafusion_expr::udf::ScalarUDFImpl>::invoke (59,576 samples, 69.97%)datafusion-cli`<datafusion_functions::regex::regexpreplace::RegexpReplaceFunc as datafusion_expr::udf::ScalarUDFImpl..datafusion-cli`<datafusion_physical_expr::scalar_function::ScalarFunctionExpr as datafusion_physical_expr_common::physical_expr::PhysicalExpr>::evaluate (59,578 samples, 69.97%)datafusion-cli`<datafusion_physical_expr::scalar_function::ScalarFunctionExpr as datafusion_physical_expr_common::ph..datafusion-cli`datafusion_physical_plan::aggregates::evaluate_group_by (59,583 samples, 69.98%)datafusion-cli`datafusion_physical_plan::aggregates::evaluate_group_bydatafusion-cli`core::iter::adapters::try_process (59,582 samples, 69.98%)datafusion-cli`core::iter::adapters::try_processdatafusion-cli`<alloc::vec::Vec<T> as alloc::vec::spec_from_iter::SpecFromIter<T,I>>::from_iter (59,582 samples, 69.98%)datafusion-cli`<alloc::vec::Vec<T> as alloc::vec::spec_from_iter::SpecFromIter<T,I>>::from_iterdatafusion-cli`<core::iter::adapters::map::Map<I,F> as core::iter::traits::iterator::Iterator>::try_fold (59,582 samples, 69.98%)datafusion-cli`<core::iter::adapters::map::Map<I,F> as core::iter::traits::iterator::Iterator>::try_folddatafusion-cli`<alloc::vec::Vec<T,A> as core::clone::Clone>::clone (11 samples, 0.01%)datafusion-cli`mi_heap_malloc_zero_aligned_at_generic (12 samples, 0.01%)datafusion-cli`_mi_malloc_generic (12 samples, 0.01%)datafusion-cli`mi_page_free_list_extend (9 samples, 0.01%)datafusion-cli`mi_malloc_aligned (13 samples, 0.02%)datafusion-cli`<alloc::vec::Vec<T,A> as core::clone::Clone>::clone (37 samples, 0.04%)datafusion-cli`<datafusion_common::scalar::ScalarValue as core::clone::Clone>::clone (103 samples, 0.12%)libsystem_platform.dylib`_platform_memmove (54 samples, 0.06%)datafusion-cli`mi_malloc_aligned (18 samples, 0.02%)datafusion-cli`<datafusion_functions_aggregate::min_max::MaxAccumulator as datafusion_expr_common::accumulator::Accumulator>::state (140 samples, 0.16%)datafusion-cli`alloc::raw_vec::RawVec<T,A>::grow_one (13 samples, 0.02%)datafusion-cli`alloc::raw_vec::finish_grow (13 samples, 0.02%)libsystem_platform.dylib`_platform_memmove (11 samples, 0.01%)datafusion-cli`<core::iter::adapters::map::Map<I,F> as core::iter::traits::iterator::Iterator>::try_fold (15 samples, 0.02%)datafusion-cli`<alloc::vec::into_iter::IntoIter<T,A> as core::iter::traits::iterator::Iterator>::try_fold (14 samples, 0.02%)datafusion-cli`arrow_buffer::buffer::mutable::MutableBuffer::reallocate (54 samples, 0.06%)libsystem_platform.dylib`_platform_memmove (49 samples, 0.06%)datafusion-cli`<arrow_array::array::byte_array::GenericByteArray<T> as core::iter::traits::collect::FromIterator<core::option::Option<Ptr>>>::from_iter (87 samples, 0.10%)datafusion-cli`mi_free (9 samples, 0.01%)datafusion-cli`core::iter::adapters::try_process (202 samples, 0.24%)datafusion-cli`alloc::vec::in_place_collect::from_iter_in_place (202 samples, 0.24%)datafusion-cli`<alloc::vec::into_iter::IntoIter<T,A> as core::iter::traits::iterator::Iterator>::try_fold (202 samples, 0.24%)datafusion-cli`datafusion_common::scalar::ScalarValue::iter_to_array (202 samples, 0.24%)datafusion-cli`core::iter::adapters::try_process (202 samples, 0.24%)libsystem_platform.dylib`_platform_memmove (103 samples, 0.12%)datafusion-cli`core::ptr::drop_in_place<datafusion_common::scalar::ScalarValue> (10 samples, 0.01%)datafusion-cli`core::ptr::drop_in_place<datafusion_functions_aggregate_common::aggregate::groups_accumulator::AccumulatorState> (21 samples, 0.02%)datafusion-cli`mi_free (11 samples, 0.01%)datafusion-cli`mi_free_block_delayed_mt (24 samples, 0.03%)datafusion-cli`mi_free_block_mt (9 samples, 0.01%)datafusion-cli`<datafusion_functions_aggregate_common::aggregate::groups_accumulator::GroupsAccumulatorAdapter as datafusion_expr_common::groups_accumulator::GroupsAccumulator>::state (492 samples, 0.58%)datafusion-cli`mi_free_generic_mt (9 samples, 0.01%)datafusion-cli`core::ptr::drop_in_place<[alloc::vec::Vec<datafusion_common::scalar::ScalarValue>]> (12 samples, 0.01%)datafusion-cli`mi_free (20 samples, 0.02%)datafusion-cli`datafusion_physical_plan::aggregates::row_hash::GroupedHashAggregateStream::set_input_done_and_produce_output (546 samples, 0.64%)datafusion-cli`datafusion_physical_plan::aggregates::row_hash::GroupedHashAggregateStream::emit (546 samples, 0.64%)datafusion-cli`mi_free (16 samples, 0.02%)libsystem_platform.dylib`_platform_memmove (43 samples, 0.05%)datafusion-cli`<datafusion_physical_plan::aggregates::row_hash::GroupedHashAggregateStream as futures_core::stream::Stream>::poll_next (75,562 samples, 88.74%)datafusion-cli`<datafusion_physical_plan::aggregates::row_hash::GroupedHashAggregateStream as futures_core::stream::Stream>::poll_nextdatafusion-cli`<datafusion_physical_plan::repartition::distributor_channels::SendFuture<T> as core::future::future::Future>::poll (11 samples, 0.01%)datafusion-cli`tokio::runtime::task::waker::wake_by_val (9 samples, 0.01%)datafusion-cli`tokio::runtime::scheduler::multi_thread::worker::_<impl tokio::runtime::task::Schedule for alloc::sync::Arc<tokio::runtime::scheduler::multi_thread::handle::Handle>>::schedule (9 samples, 0.01%)datafusion-cli`tokio::runtime::context::with_scheduler (9 samples, 0.01%)datafusion-cli`mi_free_block_mt (9 samples, 0.01%)datafusion-cli`_mi_os_reset (9 samples, 0.01%)libsystem_kernel.dylib`madvise (9 samples, 0.01%)datafusion-cli`core::ptr::drop_in_place<arrow_array::record_batch::RecordBatch> (13 samples, 0.02%)datafusion-cli`alloc::sync::Arc<T,A>::drop_slow (13 samples, 0.02%)datafusion-cli`core::ptr::drop_in_place<arrow_array::array::byte_array::GenericByteArray<arrow_array::types::GenericBinaryType<i32>>> (13 samples, 0.02%)datafusion-cli`alloc::sync::Arc<T,A>::drop_slow (13 samples, 0.02%)datafusion-cli`datafusion_physical_plan::repartition::BatchPartitioner::partition_iter (57 samples, 0.07%)datafusion-cli`<str as datafusion_common::hash_utils::HashValue>::hash_one (49 samples, 0.06%)datafusion-cli`datafusion_physical_plan::repartition::RepartitionExec::pull_from_input::_{{closure}} (75,911 samples, 89.15%)datafusion-cli`datafusion_physical_plan::repartition::RepartitionExec::pull_from_input::_{{closure}}datafusion-cli`tokio::runtime::scheduler::multi_thread::worker::Context::run (78,392 samples, 92.07%)datafusion-cli`tokio::runtime::scheduler::multi_thread::worker::Context::rundatafusion-cli`tokio::runtime::scheduler::multi_thread::worker::Context::run_task (78,358 samples, 92.03%)datafusion-cli`tokio::runtime::scheduler::multi_thread::worker::Context::run_taskdatafusion-cli`tokio::runtime::task::harness::Harness<T,S>::poll (78,351 samples, 92.02%)datafusion-cli`tokio::runtime::task::harness::Harness<T,S>::polldatafusion-cli`tokio::runtime::task::harness::Harness<T,S>::poll (78,393 samples, 92.07%)datafusion-cli`tokio::runtime::task::harness::Harness<T,S>::polldatafusion-cli`tokio::runtime::task::core::Core<T,S>::poll (78,393 samples, 92.07%)datafusion-cli`tokio::runtime::task::core::Core<T,S>::polldatafusion-cli`<tokio::runtime::blocking::task::BlockingTask<T> as core::future::future::Future>::poll (78,393 samples, 92.07%)datafusion-cli`<tokio::runtime::blocking::task::BlockingTask<T> as core::future::future::Future>::polldatafusion-cli`tokio::runtime::scheduler::multi_thread::worker::run (78,393 samples, 92.07%)datafusion-cli`tokio::runtime::scheduler::multi_thread::worker::rundatafusion-cli`tokio::runtime::context::runtime::enter_runtime (78,393 samples, 92.07%)datafusion-cli`tokio::runtime::context::runtime::enter_runtimedatafusion-cli`_mi_page_free (30 samples, 0.04%)datafusion-cli`mi_segment_page_clear (30 samples, 0.04%)datafusion-cli`mi_segment_span_free_coalesce (30 samples, 0.04%)datafusion-cli`mi_segment_span_free (30 samples, 0.04%)datafusion-cli`mi_segment_try_purge (30 samples, 0.04%)datafusion-cli`mi_segment_purge (30 samples, 0.04%)libsystem_kernel.dylib`madvise (30 samples, 0.04%)datafusion-cli`_mi_free_delayed_block (31 samples, 0.04%)datafusion-cli`mi_heap_malloc_zero_aligned_at_generic (35 samples, 0.04%)datafusion-cli`_mi_malloc_generic (35 samples, 0.04%)datafusion-cli`core::iter::adapters::try_process (399 samples, 0.47%)datafusion-cli`alloc::vec::in_place_collect::_<impl alloc::vec::spec_from_iter::SpecFromIter<T,I> for alloc::vec::Vec<T>>::from_iter (399 samples, 0.47%)datafusion-cli`<alloc::vec::into_iter::IntoIter<T,A> as core::iter::traits::iterator::Iterator>::try_fold (399 samples, 0.47%)datafusion-cli`object_store::local::read_range (399 samples, 0.47%)datafusion-cli`std::io::default_read_to_end (363 samples, 0.43%)libsystem_kernel.dylib`read (363 samples, 0.43%)datafusion-cli`std::fs::OpenOptions::_open (47 samples, 0.06%)datafusion-cli`std::sys::pal::unix::fs::File::open_c (47 samples, 0.06%)libsystem_kernel.dylib`__open (47 samples, 0.06%)datafusion-cli`object_store::local::open_file (54 samples, 0.06%)datafusion-cli`<tokio::runtime::blocking::task::BlockingTask<T> as core::future::future::Future>::poll (470 samples, 0.55%)datafusion-cli`object_store::local::read_range (12 samples, 0.01%)datafusion-cli`tokio::runtime::blocking::pool::Inner::run (78,880 samples, 92.64%)datafusion-cli`tokio::runtime::blocking::pool::Inner::rundatafusion-cli`tokio::runtime::task::raw::poll (477 samples, 0.56%)datafusion-cli`std::sys::backtrace::__rust_begin_short_backtrace (78,881 samples, 92.64%)datafusion-cli`std::sys::backtrace::__rust_begin_short_backtracedatafusion-cli`std::sys::pal::unix::thread::Thread::new::thread_start (78,882 samples, 92.64%)datafusion-cli`std::sys::pal::unix::thread::Thread::new::thread_startdatafusion-cli`core::ops::function::FnOnce::call_once{{vtable.shim}} (78,882 samples, 92.64%)datafusion-cli`core::ops::function::FnOnce::call_once{{vtable.shim}}datafusion-cli`mi_heap_collect_ex (13 samples, 0.02%)datafusion-cli`_mi_free_delayed_block (11 samples, 0.01%)all (85,146 samples, 100%)libsystem_pthread.dylib`thread_start (78,906 samples, 92.67%)libsystem_pthread.dylib`thread_startlibsystem_pthread.dylib`_pthread_start (78,906 samples, 92.67%)libsystem_pthread.dylib`_pthread_startlibsystem_pthread.dylib`_pthread_exit (22 samples, 0.03%)libsystem_pthread.dylib`_pthread_tsd_cleanup (14 samples, 0.02%)datafusion-cli`_mi_thread_done (14 samples, 0.02%) \ No newline at end of file diff --git a/versions/55.0.0/_images/original.svg b/versions/55.0.0/_images/original.svg new file mode 100644 index 0000000000000..6ba0ece995a3c --- /dev/null +++ b/versions/55.0.0/_images/original.svg @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/versions/55.0.0/_images/original_dark.svg b/versions/55.0.0/_images/original_dark.svg new file mode 100644 index 0000000000000..532434f9214a6 --- /dev/null +++ b/versions/55.0.0/_images/original_dark.svg @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/versions/55.0.0/_images/samply_profiler.png b/versions/55.0.0/_images/samply_profiler.png new file mode 100644 index 0000000000000000000000000000000000000000..08b99074585682f7d86f0b98faffdc10ae39912f GIT binary patch literal 605887 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%0Dy;6$4Sgqv8ijJF?zw)&%aRIRC8SH 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 #$rQ9tzq2 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�zG1{yMp=X41OloFBDT41iW>8X=Ce9qiZM4`XEDS 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%0DEmQ% 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�aubY8xNTr-{mxb=1lVjlpQ5vI=3hCJK?8=@eA+2m`z9ZUq zqaU>xv>IzQ+%kfuKWh1D>310-Tj9^P z-nkZeyUC2l=i82_;m=yV%{o2**Y}Vymr-1eX5XN_zN^7ye|AnvJdKwA+;1+G zT(zooy3`&#=?w5m-9h&R9K-Bv7h2fSwZsMZQpe*zZw+#R;UtO6106g44QZ+Q-z^pG zX$u~IEyf?omRI=cZ%=gao&dbc8G&~7w4)a5rR?6+p>AvwTTCbnpicC0O;=|s7KnZLuNZU5(-YIkxDgPbI*y_4qr? zwtb4^_pJw8UHGr0m>}v7T7&8R+c>aZrsf~!*CaTK;+x zy#1GMpT9zkR*7`sS&HcfdVKXb*6n?v;Y}k|hW@LhmDB(g1jB!t)Hao}KD6;QsOC^S z5$f5y|5HakaZ(6YTP~l@I+_AnJ0Fgdqz|}Z&4r99V`P#wG|c1}f{zz5TN)6p&F(PpNZ@s=7E6S!6`g0esVMi~buT1Uq@JgdY>i06 zTX;3C3f$f|i79j+vMWrw>-1 zS#mIrlwr2cZ=BwhCosUZj(Nl)i;YIcyg2FNp7(W)e~EC4srr?~);0x3p2&8kNB7x& zhX7Yrt6ywf%=D18# zEDhfIi6_Uutjv%taJoVjf@}uV*_mI01r@EqP{D5^A-<{le2B^tI>*hvwVDf!oD#Z? zssH=F?M+)4oN!>py-N=sf7eR@$gh9_u)1BP4EWjNHNPeJvbn^@Wjv_^hgM@ z25bgcYKP^Rpuqetaf)uAe<}H>#R8J#6vWGT7EY#6n)UAJ>R94V<^3gUcUM=1^#NNHgo%`s$PRFt`idcB<&eV3`j3<&o^>N=HyrJw7U2hVDDfJ1j*b z&_(=|$b|=- zE6wGQZkQ-sP5a)Dan{{J*Z@oES0K-Bh*e6?_k71p{C_Ww7V?=W0&SfQ{0#0JJZ!31BFicggl79_kyr*5 zn$91etg%{X{q*T%-tU${v&iy5A7tHDHvz#$k&PO>>$gLi(`8#kM(&o$8=kAZIC^u+ zeo0)1YoA8D8}_7R;GI%|QD0(crD?bbxQ!!2EC2sL`+uIsKtmMo;Mm=6kwXWDR>sO1 zmm-(1OZB?18*K~>R_uh8iM5tN%A;oyKG@&2nh@k#T?`&bK>6K z({M=AWo#W&+$v>y1S0~y&VG@S?g2XEi#jVMvv?UBOeN3VgotDJUA0|LL7j5#@$`CJuG)IJ#wvC_5gje2QD!`t&K`86 zCoSFC=x&zyfYo|zB9JxD;vE&c6_vJYDd0`j2U$+mqWJc&8I^Z_X3wan7X=T(dV2;l zFXig&DVE$8%y>t}r_L1@7D^3jG1n@X2!P?LVw>k0t5l0(6xN#kBKeM-DPdN>R6S&Z z%3QG@-Ez|J{ZI5if9LW~V5f~LwX(Wi_b_g^W_SLBYpEJtY5CuGtI&{{7Nt`_MK%J; zB9Sg&^+=WbtorH3(N^xzz8>G%*V*fM?%`|-uWNV4qlh{WylWd)*WJ}B!^y=RTrf~mVkIJ`X9JbZ?uT^0czu7f!pK4qY43~*607^ z|K@K*kSK-B4%hl@+Sbyo5sd(DqNy4|t#?0=+KzLUF=Ya}c^BZ8SW(@-mbX3y{OuAV z9p6SE8NS8Ng#sFs2moxUKHbXA!Xf*aNGZA8)@kq%(gG06azQD_AP~IJmI)|w0*%cO0q2G%GBuw(eiO5Q^x zMeZ{ngK0{z_0_xSGcAd_q!ntvaLy_*a((8_YF?N_4e2L3jpx3S!E3sTU3Wfrva3 z>}91tO_mHqhGTDu-L(`oJhVLn>7mB#+rsf^fJfY~YE8S<2wX_|nU4S2FwE4LPW!LV z>Ftqwc&KY|2!3-}Gv;m=K#436H+;*b1x(nmg#2xw@z*c=wrNH|RR}bw^^#jTF(*HO zb~C<>y&nmDKn*L~cglbzE&9~S5qI%;XQDJ{Ezl90GcSMTsf>$BGpz*-P|inZVs6t1 zd7U<)R~yMUvvZemB8Gr@;L!)VFMw!D57yQ5*~cflQ&9fZ9iv%3JZsCFDikVlPT;TI2Yce_WE)MU zexjsO<)S+pN5qw3T#K9xrg?UY3n0Rq`K0UTU}BzzySsbWNQj&{R$Zi$m|gd3X5>mD z=&@9h>FoR0W_}YlU9*G!-lLQ|jk{wm3ca(jC}?5P#^6&A#MSiIMl#X{M`^%J`6>O- zoIwA~#61pXv}VAz;&9(05KORr&do|g)|fUNGF7YhI>rG}!5S-6_iqtxyA9wjR71pgNa+zkDq%rm2)tgAqVszl!kd8-s#S#S?g8UOh9Y?yl%Q0ZdF< zfG5*l3n?qT+&OP{~UQ&2%0F(p@T77Gm`m3^QNKlrXBbdn$y~OR?10=6;DoUB( z2lqqGvi!S$0aKE2d3ouXff{1CNC4v+i+=2vcqVXFqE=E+o49hz&+okuY+7AA=x?Q~)C!dE)CwtY9MXUd*`5!kqOh&wiwhVy>4tRU=R0bep0 zuh)D9m%LjiZ>G*w)xPm&Nv_OP>_Gqb5KT+s$Fu&QE%ox7LmR^z22Hrpl51JDBUi@8 z#tluu)R-X!TEeSCLrmzysZU=b17db**khp@+!`#*b-|azg8nCq^)x4g+TyQav#+n& zRrt8#bMJwIHal^(-BTsTsQBiK7loNuNMuDtycQa~wec~?hvKD3VPFafIG#`jW5l$- zU^`m9Hd1;5`y0<2P0{`~=Q!K}V)LXZ z-H+xm4KKHGl%@I(R^!#!9{t-`0TcDdQ-Gzao+TY+1QFUy1iXymPqdq8>b4)ih?cZz zvuNQxd^T`L-eDVjZ5asEmwcPqAy=z_e)ed21*qGT_W(JPgQ?@-v7-e9jC@wHC$0a2 zI@UPtllK7;D1QZbE7{vcO?0&y2S9L} z>MnZo0-v`BK$f33?U=sUYWaa-S&g)NiDZeM6Yz9RLO+c$(W;GI?XJ z$FJ8hv4t>a(G{HBGI%^uU0r5+RKyqa-FKiY%e!nLhyVh9F8(6^ zWq95prsgd~u(%>#d9B+w1r?xM)k zLq)wB5A~{AR9c+_+iGGU!q8oOY<#nviox8i`sn_MXbxG`76R|eiu~WEx{{R|gjRWT z!pOY5<+3~p)(B|XQ>rrhi#UN4ERt0#sn=rdx`uCKcm}f&}-{8##5j2No3VH-VA1tAL@}lK*s`Y9D(vAorpBe^w_oFf~R6Qk!0EJM@voCZCA7$zB z?s@TraZIh>tdvq@>4uIhg1g){)5b%}4Y#rf_lrW{V!RmuT~oYRw-3m5^($x}(0OY} zo*~HGQ9k4MIQ{|9Oh_HJL1AMN;-Xsz|0&FBr5%<9K;tE~P`-iul(a^w1lL+zk@kfx z|1$$E+f}?b;B0sL86Z9?o`xhr2GAPN-iv{}>?C=?7ejU?POaj&zu1x}qL!6Tqe9$8 zzd@T%qw#Z=^&sHhk)iKANpV{?}ylx!E;)wOt$h<1<*fW=CvB_tCzgghx zdfR@s`0w$a>K*AvT&*&A*N^mh0@r3& z$ki{Z#6k7+PrZ~nQNY1eFb&Lo2?gTv|&w+1~L^4U@-;}9S=3k6IXFm5LU?jO#)2iCADwZjIw?( zW|RG8HizHKxkkSK9ca!!+g=cSO|cl8?1KZ_c_FIsQg|6hxp7iQM`~mIs7xAHnOO#@ zgm?pLTI+s!ug=WBC4W#C7y;+Yg^3hn?myGD3pm!Z+hFUmI>0@4gIGhDw((xq>Dg$p z+7~AV>l2}X#eS1;IRJPqcn-|WUmfj$+jvGroh%$FA7n0%&xpIxN%EU)-`FNaWSr{X z--ecice%p)O`?v<=Tr3%y)7U!;Ne)1>9&Q#fzQdHN&Z(x{a@jOdPAdW*-K+tlS9|T zQMoU+B8>Ze@1ur5okh`Bhfg)I|Dw+T(u;u3C-6k-Kh^v(w_a)*oPNOZubQw(cxfx%$IrYH%}!XQ=J$*EG*=^pkaRAvT_6g27EH z@>qJeY)aQ`rmD8WT@{q-NaxAh{m&=&5vf%+g?W{C%}U2_E|W3U!Yw(ySw_;;!)k5_ z>3mSUI&HTgD&005+olWd6P?^$>cP5i;;K5p{RFu&Bx3D9BdIA_%>DJ6o?crfJ!B3e0ncOn$p3c0=Ls%|ax3eTfmS9|VDcjT+* zmSq%1ikXt6Ny&5vy>(*so+8au8=)7YSI74pVge%@{FRHY=&~{u)Y=$tlU0hgCD%xS z?lm)dfM`u&-JU$?(VQ*EO0PMj%8r=z9@82%)%XFq9XX91@9GNS3#HjC?0RNNMzru& zUZS@4vJEvJu3dP&zI=j+x{UB}mt=7{`s+Hnd!TH_Gyi{kRX@uf|KEG(PnOe~Px;iP z&#C$K-IV$1`9D4VDfRLhymIGzxErzQGcL7u$WV;@Rk(e*q-dg+?EV*ykA1XvCn)%9 zX7be|w4k3e^gd=?ey6(PGR5xK&spCP>h+FtXrPeJH)FAO2phH=7QX8PifE_LMQcF{ z&Sh?J!GkipQOm;r?MTq5aOpsn8FZi!OgJ=K4u(MMcU8Zeny8`D^gts z;e|SU%rdyvWIid@eB&L7xdNYml(TU@rrFu*NQw0eBsd_*BYV+x!PNq>9@O%L*|MI_ zK5xPVSUID7(9WM_vPsK`Io@2ck1oB3=gqQ9w7ibfaI$#*B~~=K zpk^;(r;Xvq8zMtL9#Rkxr087!xZ)j~zvPC_jH=8@Ou&<6O;-4nq&k}+u6Yo%JN|(K z>;t+OzXDs}dMOH{j1PQ_kHY$r1Tyt-UEMv{nAYlc+mo`=lX7WLm?L5jWObRGASZF$4RY0cy`|UK@z@EIw5$hZ<{wPQbyv4oCb`+!U22Rc!0m4Z2>*Vh_}R zE5sZBtq`HWfZb|N6lWWRpb{+%a=pd>&XxzQ5zvsz(dsDgs+ z4RVO{sTAzRa?5r~fn%1I=*^iy{o$&4BTB(6)?&M#_x_3Rd7@Bu2oouRn-K&(*eaL$ z>Q~_u0_GafoZAG(CKm?U)jRZ5`N{iX4-DGvmrF(U-1I8+ApGVVC4sDUrvLkC*rnO^ zBYm`Jg}hwQ(+Pzxj{^c{Chc2T41x_Hvm$C!6y9XB}^x)`hzD)<{qt<(_E zWsW!ZsXNiH$L`5?YuHe1b*u>>p#5vIf2X-D2RgtHPFy6~g1qK?$v=$)ZSUf}JW$68 zj2{`bL>|YG8NPx@w!OAHzea5vS|Rt|d#X>^4X1@;(!I=4Ych}ehs0DwR3tV$0|l-d zc#qnM2t44*vupg;uzCLa{Cky~{@t{&=hP#&)z0)3@TghkA36UvHw3EAZS?ccahuM# z#QIs2Dm&DW9VYm{>$riPSsD*=`_&W)z%up-B)}nIVs>_o@>-WbK-GwhWKn^o1G0p- zW7^AtcOT6_$V%IiMTmq554vQyVW?gMF+OxT7n?)>lS@nquE{}Gg6}SuTG=JA%1V~w zXhKinHxSF`#wZ`hXnQv9CJ_<^Jygf8UchQyL3D{smIuNDKuQJjy`C_D>#zcrC!q%4 zO9rLOgeM1iGn?MHp5I-iE}ss5Mv4 zoGZq)7J6ktfI51_rnyx&^|m0mkQI&ixsMRu8Tm?k!eUnfjeE4DTZz&#Iz}mX^ExoJ zB}+o2vJ5C^)BlqH#x2+$*yEprpf09(%Z8HZs?;+ut0oWCXm6i6AsE__s9uhTa?RJ5 zS+UuqT&21mj-cLcdSXWV%)KBHe_jNX&4Z@Z&V7O%H?ZpjKa=#+^XAfp68YAcvj_5g zk(H>lIQ!!zRNM5kz(m3}BkP-cvJQI3r{L-}8|taTF7c}SBqAxZCi!Eq?G?%Lz~UO% zcXT#*qpy8qLRSO-J3?{1J|X^Rux6dbqsA)T+UoB{RwIRfYNJ5yI_h|Wu$hPzo71ha zj~$YF3g7ZmUTQC4*L)zVf2R1CJK>2?0a8gk47PHcO0K8{P}zAkpV6bm6OW_i?#3=i zSD>?>4pzq^MZv8(#K3`}zile-L!A(}?|*J=JbLs72npucQvB0f=VM-7;h*H)f6YiA zR?S49!<+yXv23lmtA-7tDD{EGw(D>vgZMpFRm2#r3Bpe{otIAy=L9rSHAxHo=AY%t z^HdSE(U6)%>{+$=b8`#gvK&M`Y$j&;&a7PAuSxi^I%JTv<1|gOx*v$RY%r;|9y=HE6dK z>L4M#ilHzN0#fiRUuovC6{+RWG`ODvEara1~q?5|)*7fXZff*#=8Nj@| z`^D)s<3+T05X9ksE%xu<;@yEZ56sTaMr6Li8Z;U)Ah#6T$Q6Elj=Z1NzY6*mcM$D* zu9J$G{`81Z1HWI_&8s@RjqWg^HXc{LCHLGYHHdNBou`lQWJgsa%ojyZG$M^HAUP1m zKM8`X(fbX~nyXdMk1r*2YU&AyaWLNq=gYto^~n^-;07a|FL?54vQn{iSB#xpkv+pm zYWNpBcX}y|b^yK`B&?mW|0~U+`t0lTHZu2qGdkziHafI(BBZyE6MXJ2{?6T$y;Y~U zXz8PW1+I2QKfytiGTFKk?bw>*wnjX}5kTO6o4Bo~iFEs&g8tN&fCQObsYTYq$ZCsQ-a8x=Wwh!1gdFf;>8~Yc;ck@=2IjJ zL-H7vMwh=s^RaQfe=J$?27CJ>^`%mkAllE`!=XQEqG3UQ=om?EDaDJh2rXZX7i`S< zOEixLP7O(%zV0l1O!EyMf%6@48PjoxN{HeGqgP^V8NyPH4I@}OW&Xe8$_D}lgs5wY z037Q*5DHEIhCShkqqvi=Q!BdE)aYcVQmtF)yA%ZFXGYz-LnU7SB+`aiqd#^B{gzgh zR(>KkZpZ$n>5@7wF~4-9jQ$-w>qMRn>;KFJAGqGwy^xeEHOFFiR}+0J`Q+fczgH6* z=F$$Nx=tHL?xE6tz$xv6tTX=&(kZ;(q0wJIT~GX=b}!91>Js!x_bJtc(j?$C>HfCBmHH=I1uO3&hAGJbW;5vpXW zrTFD8ruVH$xtCpCa_mD~nky|zf)p0~dYUG_l+^giFMc>#Xpk%EH}@dQ4#t|g1o%kB zi@DSV5fIa$lN_Y)1{MC%3BelR*1TrDdCNw4X2;oZfMjB>eLcaq#MNW%HKt^9)TfT3 z%fE*0zBN{>Zb6&!L#bJJKgtyDUvTASe1i08S;#FJ%#sCRO%ZDBf40giVSdD_Oyg^m z32$p6E^uFk>Z7yHV|t4cPg{(WsJW~O!i4-iTr1z{vhc8Uj0)QtqD<%)YS=PW(L}>VEZ1|1IrgYlJn;0 z1EW497)fb1r=-Ezj*(?Ut@qY6YMT@P?N&C)r%C+xn_d3*n_Zp-h--lcA<;xfAssN> zQH{XhEN#ZJ&4t(h!>+rBqB?9k`|t2wv%c4M%-_ld>FrSqdG^^^fZTA#S!!f{I198*YlKw3oyfe zso~)@eev5|IFVVd#ZR?x$zW4ozHY54&{qsPL@pSzk_| z{ha5VKjlL>GyAvq+G}0c_rg8eOLwYrbx^Hua(}~fu;S##uS_sG+47&4yC7H_Z-u@0 z{EHOqbhA@kp7tG>LYH;|_gak0i?+=nEsxoH)cOJ2(Vmt7n0)l8cY*F%d{Wt)B{Js@qrRpW?fvN|G*# zBu~{Gha@obIryeXa9F9TE+=-6KIh-rdk#GWpL>l^uu&%fW&XNJP$~xUnR<6XT%+Ei zR1%}C9(K)ODl3#G-l0T|jV4j*&U5g{N6^h(bL|?qc;@v*N$tA04RxR;7@Fqe{IHv= z6OD$~3kNnhh-87ztbP8woUP`!Cn_)B;A+;0BG7BoS5S*$sA>7f@|SB#1W*qj8V~IG zRv$zupSAy%CkG1Rs?(LxR~`h1;N{hg!2Z@zYVW?5Xk~p_4k_v%jvA~0XZ3p&NOb?8 zme}M2@SDNlhF0Gl?6M2y|ke<#Fj-e)@*vxxmN4LGWS>k|VsLcMXq>=Eg>w zi`F~@g7bM$#q(JT>2i5 zaBBJI|Fh25dnX=g+<{GN5KtW&KU#ntkQbFUzHtOxSyBK;-RJ~AAB|+~+iVm{Jo!m! zama<9Ysyhj7HY=f1!?8iYv-yRUX*}n>2Qu8BKaj0kBm(lwOb6Z|8DG#AEbyltLji= z%FzN#Mkf$ojn0z|aS)3B%|r_^US45R=wW+`m+@?1mbU8+xE_YM^PUIXfT>5CTL_3* z=k@DrE8Nw7YJk#gXC7?a8kba$t0>wt-wy~Kzn*^iuoazE?}+M5c-&@uS+LgfWBl=k zhYcB>8Trk-K%3VMk;3BkyKd*}7C99;=XR<(spzImL+jtHkPvWV%Q_t|TeG6i)K*sF zrg!_y{SYlHxy@Ca=nep(ygnDd7HaJO23A|_wHt%5WnRytjn{{}QkgA=h?kcDxkHZT z$vk>LRzSx^kVmcodzQFr1?ZIzeNLL_U-nm1%IvI$u;9Hb3_Q2#*Owp}`hMNq2R%n1I(uw(Lg)GSYx2_dsPW}#L`VfM zj~?vFmc_QXmj#`q*CU@jn$B)|#9P?D6`my4-V>*lo+U7O{EOxJo0wUS)E;qW-WZ=( z;SYFvMDSO%lS}%cQh(7Z5y5HG(Y64k#EZ2w-^=A7FyF1Uad+-$ioxu1UkekG%NI#~ zCO*H;8SW`8x}Fmg3`k?mpWYt1Bs)r!D*A(`qMAcbikx`V;=VqVeam1%3&;Z^K14RXA-rzNAEfm;m3nm% zT3vDm=Wna&49ZR#7!*!#0$u!HLVp5t36$IzXgmOc_C)QU}bN(@@0s&W283#270J&u6?rPdzEhO%46S^`JQ@vBW-8oivtRR0L;@v z^t5?>jIhvZY}AU(;@|V+Ak^48<{K2D)On_DWl#&6vAL&rvw0RLL3(!VH74>O`A=EP zvE4$o>l27OOD4#1Yn(y{^|Hz8pBQeF=z9V7GV!v5!A`W_QI0sTztfb{Eae?Yph6O&R5 zjLX^@3=Ga|9A>r@dmy*kEm!hu%Rj0qRQHLp?H1Vlx%7R`;^e%tt2_<{>+ZSdde4{) zf3=KBb*jF2`bqyKT__*^g>|F5_a+#7+brZy3Hj;+G-As76p7{fRXIB$&`iS!+;6!vc30z zE^|LH*gpW%{FZtEU({$&*`DH41|__m^f=Jzq}(Q<0(9sLQnVDU+EM!(=ey@nhH$Z+ z(L9 zHcQwZS5wl;$ZvMvED;I;5-*OpNk9Posdm_q|Fx|Ana3^?ljtQ0{-$Xk39U9Jdh`%% zaP3qIW<}{BMrZ(Zee)u#9j1ViIyx(w1(_9cokvpeCf;BZUZ~ZbfEGUWce&xv$kRB= zUMyYHuh;$P@(L*l@0+=IdVU%g7h>EoyDe^DK__B>TIV_-r}sTo!d(n`mUM6aS+_e2 zx3~|FIhsWlU?Ds3Xl)P-Ct8GmeivPOV8HkD5J`voZTKDKGJ1o+#P48^u81~Obc&oAt&b!A1 zzqMx}qMLlJTKWF`wMd+L52Y1^h&|m=luGgQ&rwfj`sC7i zlE>wr@HjTnA2IF-r;jx41meTXK;6L$HDILQ)3_>Z->Y^S@;y5;5Z|{M?WfYDl$B5p zZn*%m*V%dVb3yT3rq(6KPZpnnS(tWT0mp-5l`3JzdS$q(ZS|sRs1b*S=@Q(ZIm5;G z3rOqyz2+!bbdo2e(Ztq@n`_@290krz#B_@&XyuA4Y=ZONYN2B{L2xn30KLS#O=vA2 zNj+SB(ucg-Q_=|B2Ut&i%&jodN<2~U4vrq|D?sn2TP|tL&nVRl_;;)44;$oyyy)(n z<6N81FNO|EDdly=sQss}J)k~L^mgDb`@Tl4eZElb!bgX~) z>rY2@mQC(>9KK_gpCI9>=jrc2YHxnwK7E=fsddhx!ma;QcLK>J&UrYeoGlFVJY;Vj zzGCTo;lD$e>GcXh&T`r#zbF$?S{ki=*>gQ zBA(XE+w0`&TSHPeWg!wLm)59+)-Ck*&up-XYQ=0q? zA5Z?qX24MWby6TthGruiJ?cV|>$@9atn}Rfy|neK&OZ1Ckor|U9%Lgp4UjDB3K574 zpu&AQGzXfDilq?5U5IM(5>wzI#$8^s)W|Lh@3&_GejMHr-ZOA0=BsJ{EgHAJ$EB)X z2FrxC+Srj%Ql{~1puUb9c_0uOk&xa@@yq=EVm|{6h|Y4L_ticz_yWwIlq$Ok&396z zFtlt{^J&mM`-^Qi1*I?G9OAOQe0_}OrV1V$5$p|geHC_?jT#gl$&A;{(_SuU+c*$q z$PE$Sv?;{|`4T`YeqU_s?#Zz|`=!lR9QF*5&<2B3qlScCr!U&COq44G=*O5iLWo%? zhnj&duR`X6^S_*RhZf6?igVBYfKp={$+``CeXYIf_8Pb__3QnvuR(u0JDUkChhE)*^m@POT)*DMoN^~sJTs4x( z6a*#bpJ>fwPVG$%;8QsGB)6`P%9$@B{j7Yzu~+qr{PR`J$Jt@(g_~=lVDzxnd)`-^ zvH=h~L^}bKFi}wW>f%JzF_CxB0EiPz`Qb)9mgy$g^?+c?+RyP=bR!!iu&41A&*xqz>t@VOxJYS7$gKJWhQ$b5{C4EQ%YnIihO3qGZeeCQlpD$ zK3xyZXm$qQo6ju6{>ww;YTMo`*DH2szYYj-^Td~cNbcp?Z$%cVKDkRWKXlVFxo+_3 zgjhJQ(Wwc^d!z7Jo-#Y?VSqjtrMzo}yHs9V{^J$zKLchcH>>Rj@NwUWQR4e+Yqij7 zQ(Br{S1BxjPigeyq$jqTdl#ieHLnd3@a4zb^o^but7f#{TU<`QPy0d`ndWpyIs!Yv z3Bwd6yD`hFM7EZ$9PEjGUUB&z2i94IpXWYe&4ypH!ngyyW}V-}hj9?uik?Rb+F+6_ z#)(!|_ySpF^lPdz1tq9-l5(h|xqvCEj3yL!Bwn8S-kF(gzPBZBscwl?lke5$!aW1` z-_O?(Re$25un~h0kB%XJwWKBsg5uO8dvCpWy%w!tI=qgtpP$}a{A0EX#c3u|&3K0r z8hoDUaJBr2Xx_egs6KS7OK`rZ&p<@cAT3A=F0@-;Mq@J)%XL@%hCJ~6k&1dLTKe{2 zPybwJE&yLp$u!b}TEVbIa$AS)td6*-wEwLbis9YHbvi&=d~Px}== zD9-OZh#)xEYV2kRaBtc<|5Kgk)E8UBy;N){)k!@tk3`5c2C?-AbCt%19)Ki)D3|@o zNX9bOPplVsa$(JKE1f{c5mkMS+t%@7U_bjGh>o zAQA2z){@~p+Rxh>ou1uvH}H?U!*B@0?$k0QR^J!&&B)ii;QAMAaKcJ9tl3kdTNt5? zjfU%d1Ne(v%`Kk~R4|3(0(vA-hgSP5E+r8g0>|Kn-XC)ZTCuedCDV(nt>x4zvlW06FQnN{G>v1f+DH{Y)^SkRzCJj>;ZA)FO0j7o)&H@W!F@} zUg#=7T>C0&aej^cV$t`1{aE5x4O`v2x!93-jkjU6-iO0@x7Nkwm1*~|A5@g1`kex@ z(ngYB%@}9hhD8zWL8>bfv-faBp(@Qo&cXNT_xexezu5!i{qJutMIu%P18{8tQ`t(u z6lxaL(LM#UF~NRp;%E8;GgZx#+CUe=1Ln~LEDd3C{#kBtSKuOW#|UQc7>pvlNH<$y zq-RTWajJaNd^plSv-+miQIp@lIol`hj{QB8I^lt`bT+mYSL z;3BgOE{%U+sVS2033Su6D(Ksjc|ds9GeM9BkM4^?%oJ&P@%~YuVbZ$%<9p4=lh|wI z+?Unw1)Ln_7$lSv3id!7^+Gz}C`K6aaRLfw=l95;_h~YA?Z9}MqRu-KuaEZ<7Aeg{ zr@8OXqvgSfk>oC{H2J2JRZnD39D^NCgMNnOg}_;uk7fpLFC5AT&`@n~i51Q38BK_R zBT=FlN+0y|M%Y4{b*ypr%-&_%R%IT9dP4ZVV7gny>{8t|ZU1Wf*96>;dA6z3nKkss z!@M@;ssx#c#P_75i4<1e?W$o|Hhsd0lO|)>E-Wfl*$sN}#B#$x zv}{m;{cf?4tvfq?57R@CP`2{ArMFBHNBlM63dWn6Q(e_cvg6W6T~vy5Ru+OGf-*)u zxwJ)_sO9e5W2hm$z*_r*jPp8Ofe*T^8}>_*z!gBvpX$E!rT4C4I@Dt2=X^D4?!zyM znkLRYmNLQ2CBYp^J=^1IkCR-Mo8Ri_O8cKiia7JMr`3BPNwn&-xnS8;ejvV-)sd%Q zZn@&j51g@bGrl@S%+?2cvlt5h3`hMmjx{8E+n5JD!-(lH^B8cRABV>7{qDrRC5_Jl z#fS5H#X%P3!l=@g@<>~kg1JI&&dY8d0#R=oY28pYUk+(t4UdUrmzVGP=#Q}7*ztpO zk%L$VJXl)HaXEykky4c8_f$pdaTCklk=e|hIJTPR6c-U`a?=cB-dm}ayAGFhcg@?d zx+?N>z0oup$aIRQ-0ZRPJl9k9i*}(`^%4m!5hs|ZZAjj(xSi>fQQV)-kO+u7NG5-p z&uDNiX3s%^S7{$m#K4f?bYUI+`-#*ni#}cn*;{-f5wTlgUmUGa8IpUg=P2+X4lUBT6 z1lM^T4xBxMeW4!*KHpI*@gj*|ofQ3-$kJ6_fg!Dljd-i)@_6?bD0@)znS3}D71CM7 ze{|(~f8n3KNn8q2WGH+L zrCuS6XvBb=ei%mw@E}R|b;p%_!u{8abkTYW58)s)W%f0rU9+_o&!_agq`%m6dTu)!S`-ExKT#F(2GbHd4jHFZ~&y_TlV+0M!uEeC3U0IhlE`TK~s9Ts$vd#YSvoIOn7l}Do8!EMa2idFH8(;G#MLNpFe>k^9=>|PlXh4m zZUvJXxOlx7BAGy(Bk^E}0E@l+VZLSlUc6BE2Igi@(1r&<&?!;WzmnMash^Dbr8dx* z$gf&P{|#?5UjY=hPT#Xm^?I?N?!6P7*Bg#6`P?*OQ%`%zDDfNp@OLzK{Tn^jUaoIU z?&V55IUYB2f_PBEccZO#_$!qnyu~}h{vG!8L4`>22q0q^#?8GGl)aGj7xjcOt^Vo4 zvkxbKGCCF!J;0i7{=(|Kh08A zBV745iHhBSjEyKKXe5v_OCsQ7`Dzl+&aRnEz||Sy_egLRRT?Jx%CI<7V$;ij^dVJ3 znD3m|dvl|+a=(0rN9znq6l80XBevQ0**ocY8vsrm;c__)EM9N9Yp0ghgIEdFK3RSs zQrh-L3IFIcZOh6^GVXqm;#am-3>q789Gx%N%L?EAR}+P@{k%-%2Zb>ddAl1%wvh5o zN~#7)u37>?=&+#pMQ*4Ui+siyr^`)tW@h@BKYJ)cpLbYoRUt89x`q|Aj`;o;pvHd} zO|OiFkW}nGp=4{ZzOG0zR$TRkh{H40%oC7{8hEE!J!C(qt$+(pA>_H1cz9=~CVAlU z3e7j*G~8q$E#h9T45kr9Snu~|>HWGcoYyVSmAKZKx|K)#73a6xqM zKAe;i0J(}_V8W6K2)@Zt8Ed;;^W`4pEg9^$f6q=rsh(ToSVl2~a=IBvyL` zn_-b2s`%7AofUdW?^wXaK(koH8Dbz$_W=iS&T6RCjN0aNJSD{1ExmUt%5H0(rOLtU zJ+Zb9y3Y!zJNeaWTKiq?Bzr6fhYMi||5y&u^+3A1jRgM_BzbLZmV2$mqV03l+qX=_PeBL&L8et{!qU>IcUOT?@7dflZNh zlRj%D_lq(Jg1w|u*+@0XzKmJLT9(ay>V@z|qO+u)U}}v2DjJ5|+H)9lr(fB6=esSW_L$w?$Y!`2>n)AaMUuBgu(4=XF)0p{EeU zp|Z3J#NZI;*sVuW-xs@hdh-atakqbWD6a``{PboJl%Bp$ev3~4CU}c3(KI60=aa2@ z5=Jy%3wD{i5|0tLg8rl<*y-_Kuj6ffhg+hqc;s4+H*fu5QVwW49+npOYTc{nw_D$Z z3z_vs4xd72ads3_WbnL0oI)S3yz>x1Nk?OGCCeXgty{qA)uKz0CGANc9!CML32CTVc{@Q=M zigNeyvEjYpan>!DAC)VCi){d{qxc-G!SHM^`yqQ?;ZWRvR|*x6rs|DQn3qcxCN`x~ z+WTWrgV~=vZ{7RKrK3wbiJsT+P4EMxB{e|^GphVUB?mE)LzhqhjlEInlMEpCHdjOq zrSxDB>TcO0JUqs}5=7Z6=qgZ((YoF$hzj>7#P-HhnoW!yTC*Ti7LOiqGhI5vS5TKD zlaow**}GjJdC-W`PL;ZtzF{RIr_Sc7onqqXx82}gEXH2xd{*FcL!U5$`%qy4^+`~@ z%O&$O^W)~-#3$WsFNQAr+Jl=Am3xB%cZB^sisW=ZTMF-Ww+V zLWT+#z$X&Eqe%Vyj3My3H;vR!aMDW4Z|3)=S;rc{fG~bP9acey`CexcvuUY72FWPh zj?q&6^IX{P0g`c(PClbRo4)|lkgdOxw-t*uZh@Vkw2$#!iuE>r&(|=H<$du3$8R*d z4Hkyy_Eol+JAW0eAoIy{8uR;*#8K-%-+0Ax;;{j%FNvoFpo%ZDy7o)95n8=jjfGXPc@7Wx%m;$&KiMD&7(o4pCX%m9nSm6 z8SSQV`SFmj@O>_cWu;7&v-c!W7?gboOiQ6K-J(KVFHGs=dff+Ijv;+i3LFbF5jCYI z9uSiy!!Vndj71hUV{eOOF0fsh3F!<>C&}6Bw*RAfQ0212-t*@egT9@Bsgx~JsF)MU73U6C;J%i7)c zu=c_+CLNiNLevypP!zl_ZRx*QstpH@oU54Obcu=~kCP#L(#!{aU2#FM7Q`d}`^ha3eRat|1O4w?;YRDr#G2yqOj-QCRO%9Q2<((dHgx~Mn=euQT{wpk|4WU<{>5+(85lS5 z2GQ??;i*e%h@b9}N%Y%a(i-mS5K0P(vT~glV78yUjg5LWvp6xDraM)~%8se?g1cty z)fW!77cU63_Flk7JrFOAD4vSw&M~E&ol69_IxU?saUf#9sm#6Bjg@|ZgV?}7UD8GF z`!7X4OFRnw&QqN4sOwXLS@?$50F;Xs+|*uSoz23=^F>k`PI#~|Z>wqJwx1cb2U}?# zQ#PHa-0P=7 zjiQkVvWawe(@i|wbTh{=Y3{#`D~t(8r$>UeT zu8)~T0Z+VvY2YWydAMx0+45#JX=XN09 zDv%$Otm3`K%|3o}`A=*z5mD88;gVnHfY(P6NxtA;nE%e~q$dB=P6tlM{k*e?dzCn5 zI^f-O%A}uc+Ae{L8MpoZR~zHO6-tk>HMsW(1|h1T5`IF14X?KSQPC+*>HcOv@<63n zZ*{mT7UMkSsAO5pFF*Yty@r0V0KoU+a1Dtmu%(Ia_Ebr0?7w+M1zh+f69cJrkQ`!S^i}%GfNA$nZfJ_cKcEJ@5F-9)z&lgcb)I>u{Y%|czX|Z#5 zH9k16ZCkQtQ*H+&I2iNRs>F5xtm|Xscb#WAW-`OZ3(`3H){rK4`(^dwY+J2W#l{tO z9*&6jd_QfRW?=uhom7Ki4FD}CR?>K9GjY3TcC?s3c$@t0N}d!@ukjbRJSga7&1;w)D_Ihu}r=55{l%8&YPFlc(JjPx^&c;VNoG z5nka$P9<+Iwetp-@E-cbQIXxiU4}OdNZcLn7wu z>wzvP)OeskplPux7)9SYcWrYG3xsBnb{jtdeJU8vMvWFNToF6p39XxcZ~9uOb$%fHGc3t>H~G zw@zsH#lRSUr z^$5&y2gjCwRIgbP!mxiu&OGSu60UEYw(T0+`LbL!BpNtWOcVU6C#3f?TjDO+W3lw| zCjM^=MK|_cI~I#YB3u!b63Q#)L)G{G)#jjROcW)WDSgkUC;fN*K6E#?GwI)Up1;$~ z=^pRexg#c6l1|sKaUfS!zHw7;0K&YJX!kWeN{ieI9WhlKJxkARu96C8kK8tIHXU8b z^;^hMiA8Bf+x2%f)4Ee~r(f=rjs)*{(v&i%8CtV-PlyPjSt+XXrp>{H!QR$cjWA+^ zszbKU!Ii?m=k8ZYfZaB~;#Bn~#ov{DeoG(V?`F$PbPh%6Ovf+Aj~{)jFBgTX?Z*~+ zHIDZ`xE8vTq1kwSaBumW4t7pTuE4jwa|tWV*B3`(0;`OsQFRFhJ}hao5QpGDn~o|< zJDxh9-NvN~*T)u05ZVy}|B5I*!7D?%@02o(oukW(GF#xkjBqLNXy5~bjvC# zf!k?zz@893uu-$4`?zEZeHTx6{;xB!^_NX5pTSGQ$%R32*Oy-x;u3}rP4CnecB{J| znWhUC&u{7*NtmVUj%M4}zFHFvsoQvWU%X{M{@=FjrR|zM$zB}#p9?J4`Z_PouXp%2 zwM%0GpbXn~t=Gg1M;J@9ChM~ARB5s3J{rLczSpW-pkG*-P+w3WMEpMkbo8z6v0m$c zOH>!idoJh94^iFA!9YOeyIeYZmNf8AfJ*Gc+ec0-)I1!m{D=rWGW6jtu*{Kre(~SG z>_=)tO{iR$r@;TRInvI&o=E8A?XtfH_$dvQ*WkYcZo+6W?zh*bCab@qVwV12D%E3e z0FpufM)4~VOoOUhW{cJv=&$gbB~SOBp>~@OR8VL|S+sWoTQSkUjHUlIDpqF$mmqkt z2Zw*wabRbB$3V=c3j_F`FuuuHd&zLxmyYBUe@R$hD$qNZ#RxbRp$4`NcYSY~ygzzL zrSXV}Jn%(dg*+Xy_Vv5nWBrNdx%x}NUuWTHZLHMx((nOOGNZsf&i|1vkH#Uc3n^~a zbUW+S?l2IlY z=!h!m#OEwmdZIcrpMKi-zTipq$XFHyX7X;ybw5FO{Y^6vJ{~RFDZ+rK#8|>TxaaK| zXqygf|lm(7?fS^Zya~17{6zG5@ouMiav7MzK#D zfpBfewa23iPJi_MFD(6EpU|F4_>&kTdj`5irubXRzX{P z4M3>t^RRk$o7!2YqUwfg!Jl9-AGco(6pCbAWz9;CpdCC;zb*~<3i2p`vuXT0S!Qsh zwLG6>kv@G(LkkGR>`?s%KVEY;?=f`_3f)xgcgZFLw`?B*i13Q13_lTwK|W{8g*_CU zb!az6oUQSafpE{Ig6wd+!hwD?fhEcF8RR1vSy$BqpjC3E$P%iXe)sVX>}ve8@z($F z$E(vUHV4|xxw?qbvrm_*h{N0fR$?y4MQ~pIaN;TNC7N#GP)rCz>nmcgY0}ULBA^j0 zz$Nddakd$5a@+OXGez4w)ZPL&poCU%#)lcJPS6g2M|3?g3N#YIK+DB2KE7EN!Cu<9 zQMj~O)?Zr*@?`IWpHbKj+ga}*QRS?i`_SaMM>;HVg3k>mXM=Q;_c3EZ@)U2$LEdKw zK1h*KxVRLl^(i6&E|*OA!(sW&@87~)YgCeHs3bgH9Q!!($r9c=^*(?ST@5EMfjX;r z;e2ggU3nM#%MDPYas$Cn&k_K5>^MZw0EhrAVlz_(ppl#%kH#k-EDNk^(MPZ*fE-VQ z&fEn3H$*B}Z^f2^m{TyKN2Be4q+tnUMt{RqeKOsTF1923IscW&Tcbtw=+X4~!DC#6 zKFrbK5$NZUO&a)mKQbRkTms><>{G|7Rzvbyhz&-aI|v5Bw4z_qSD@jSj&<6N=q{g z0Funi%Dhr15HT9icv@!2oq`>~pcqIb6{7$|!Xn@8-K3Qk4#J%g-62&}$F&?M{8T&d znPLp$VpUzAe?g@zaK}TY(e4EBawHfHM~LNV)Gt9=r)8h_`60(nQ(4thtVQeD$k)OD zpQg+JCRm9gG!sRl4Y6DxHYycgmd3k>WI&>CA02Hx*wBV~3g~mHqo-&qZ{C?uPxIzn zq45bgtJwuXDtMQ#bBf4d;@UKWlGnQS@@8;{>NEln+gXPy_Kb;DcFgAoMhe4M^HZIa zD$dqOpdwD*;0O%6c}FbLIgbDN(eD06LCmFR{)R0xk2PuQxYOV8SbGThNYtJ944;$of;*Z{+TOg!`mzv>{Ey(B43>r z%)f1SxWk945k&yJ=Nmbr6L~BI`XMS$Mo7%&b3^v7GT42dk^^&E|0fgi%zdPtc!e=>Z1qEAwtXGCWY^gVWes+@cjcb+y& zAn&GM5b}F|vf%3*c!7>ob=+zA^W?f;qo(9N@BZf^v+QOG?Go)tFwoUPfoZ}6qPBJ- zDe-ZOC%*GmIF;k=H)lh7_t+_NW=&E-8aH_`m<+cN^G-J#@XGJsvY!x49#3W4S%&A4Vp^4#?Dj&Jcaj5_XTSOunPKjrIMQUXq%da?B4!yWc|;wR{QT| zZ8HmM0{<@GzIqUa`o{|MEerJs8qWO;T6us*1mXWM?x+Ifp>Nf3VKq2AKf{ ztzEuk`QlTJ7zi}n&KL;46@z~Q`cA=zUARj`)=K85KCdMMn+ld-Ai!g|s_Vs}nFryU zQ#+2~L&)v%YW2JerbdS|I?P#W&Bqz3i&ch9kIiT6cyA(a1 zG7;l52?C_3@^2sO@{#22xQh}Fes#ANZ_pHHGA>1Q$3$MZ+uLP>&$pnGDNYvvLOU(hSP_m-p zN~H_25Z3ZNU^7tq4fItXnwmFRmuVn$cU8_Ufu^bxsFUNC7Q^ZQ&N-(cpl&GRrdiK3 zz}N=}pg04F=+m$DHWfAjLW={uSwot1`G>UZ`(C()9nL<;}fW+?u);;Kw!Pn8z+tT#RJO-g70*n%hb>P9@49 zKfhSGp(AV%Svxi2ZWbf{L&?U7OPWu3m;1}|zPwc7E&*M$Ud?j&kwl22N&yHrJQU|r zD)k^W9vQ=ju{eIH{ECIxnsCKpL*5?}`I9=1Lv5o5FMH;H`-sc2kq#JEeka;t7=VpY zHKM&vnFPoZn4!X{2iH!w4S+Rg97wS#;0Y!@m$n!NG(8aRDB>=|h2Kd4s#FAeaYB%I zAlKmx|0P4y&1CkR{)^?0-RF*bYwDkn5kMo=q?ex1CL-02FQ5ewsS2`h(Czlng1cB) zFQkJs=%k+sI!zZN83}cw5)d=?W{%uE4dT|#D=XKa2z*08^|(M3oHN_It3Imd3yP&;Yq@4<{-UIZNQ+@0e=?>O z`8qS56&g6x3x+_bfJU#i5KU*3p2kv~YUQsKfhT33jTPPEv|6Qcc_}*ySp*X7oMy-h zDBYL@XCdh`Cp}wqYF0K_9o85IeWfi)xR~VA(DLV$gF@>c?Vw+?#-^@-Y@FV32o$sA z$DO4RSIsxoxR$+L64?YzDpJxM`_cVkNRditVKuJlJEJs6j+w!EY-psw(5O%* z`e4{;0kqObf973A2Md8QgLwqE(nCH;B6|Rc;Z)!g5WdRc(Z%ndybcLTtv@a)nvNaY z3aYWqB#G{8Peb8@mg8WN-UpA|&#|YN(9?U~*FYqe(Bij#vi*5z3M!|sq5cZu}5G{9a-f8?= z!M@5~nOUcEv&!pxe*vrMV#mu22t^;T-|I^?%b&+dn@6D`ZxUn~>^ z)W;yQB~?MOsCfUv!KUUL#2_2ffQAofVGT+N#rKdu1#qjgnM7D-H2--hmGK>gok=lY z!1WQomM`>e^Q}fYAO=VhLi^=hOD#j}KYV<}Bzyig$vq~Y{jj^H9}HpYgmy}Qw)Z*E z8`o|S|VoU%%sa0S#l_on!)O-Ca#@XY^>xNpehjsK5|f!B`FWVoMk1B zQbIDR*qDYsNa(3C&v8|5iEx!!qXh+wUfFK4qf$y0r0O1x47Gi;qj38!Kr2mU#xUH5 zKc!-|m5yCg4}n*+BwRjs^K|{}RyBL3M@bGu#tv=3dgM6{Fp}iN%7QpwbY$Qx=Q61% zHeRFCTE?M8~MSgv-Xy&D%`7!*7j;r~l;O>O{ z>Z7lH>^%8=r}}_gWJgbId`=prnk5Jg6)D4Z34zSMFKWg58I!Z?P+in2?cltWcRf|@ zwT95e!HOJ}8?UL2lw~!Wh(EOLi+s=~xcy8_e6!fo_N$blp(Y*tB=(oUh~dc=Q@^VG ziWZrb*<)&q3CusgKJdX#_x>(d`C>0wxoya}=>K?n2mO$Mb4i0-S#gmoZGKI8f-VEt z-`>`7SNPNed#jg7evDY|aPxfggbxBdIV3-3SmC`mZ~UkP`31kTo%Wp|POkP^rriIY zshkx5o*iAI%_j1yP$Ux8q944TuwZMQrLZolrP29?Ba4!lI(#ov^dzIkrAaFC;SB`8 zC)r@;D4+Vhbm?e|XxnDHj&_t=y2siVSM!e_Ncv;2PnXnwA69*83ZH%RAXQs2-1E2W z`lc5R4LLBF`doh4VwC^L{Ay2{RZKKathX(R`O86tVrpQxdnL*I59{vxGs8QUvY%ef z|1$ifenLZT@_qi2^*(bF@bMWBFXUms!U$oGcU8r#_XnI?(SvT!=5??n+j#^=UTuJd=E&5;R}76UIio=FdoF|z2B+%O`)?DcdXZv zEW%MED0kb%elgZANXgM;*&mBr6w$~8#hL=D%-eiqRjMD}XKkLGMmiPYHcj5;`}~fr zf>B-ACek}7cw`m&dLY;)e ze-a17r96~yBe^iXc@XK0ku!wOAQMFBy;QTv4S5X}k|3_zyQ;(A)#bql&^t-<7K{9q z4^n%kw8zDezl5H;Cj z7{FO!In#yH7K! zQ|)hm==AE3x>cz&>=!Ax+U%aZ9lFI&%mEAEhTS25D1L3@4f;_vz8!0t)V}iJF#Z|F zrHn;u_YDr}RMl{j4|LuxIPanNao)Ih}-d$9^8S5&*J2%u_eMOG5dOXzDD zYmFcu$3bZ6d#p}<$AnOdILX4Ffy@0 z0}vv~@+2_W(Z(=^?}m2#K9x(Irdhp3k$>}~*5ijT2!Q`&0!oua^JJ^+U9v~9lX2T( z3uh{_o53#nKnVWqA&Jbw=u?_AMCY;xiNcwfC*`!!ir$-*2Z4^0I*Ve-M~SwmxR03=*?IZ>L?&uC zt)%Klqq-o!=MVdZEdIK3#qP`Jk`QT+Msp#`H0aAQBMdl1LKMfc0GHSnf^~2|MFq}j zf{BwNJA~v|c=F_tP^-%!P3HGRPNfiU=HWMMYFm9#A=ga9T8K|dz7X0)$`tJ`M~RI{ z!E@hL8~Ajw{!(C@bS-i)t<8}v%+#17Gbz|H*OwNjyDh0&!b-TYNXe(=S{-yBh< zC`msNm&+ew_VS((JKJo$AJDMp8j1!U>x$EUEpS>}NRkvr8jozx*!cK;xWrtHZdzXv zPWHo)O1;Yhz4c0qp(^`x8c`R^oXtiNzI~sA0vR*MrBBr~t_p&a&sSq~=PEtmv4~KU z=hL!Z8Es`LtHoRwsSRypQ*b-hQiq1a@w%Y(hs?SQYtzX0=T4-#1=9S9M8T&{b6pa< zIxe|07c7x4l`T=ti>0PTRh&AoZcI@TV{4D zj3*GED4Hlq-DY}Dx24}t3)}ntXsFUpm5B)qvGlG3d48vI}lEM0|7RR-1Z@w9(s+N! ziH(p0ha#~+nP-ggGod!<`z2gt zDU@WI542VT2)AwQcjupDpQ4X{-@BPejISC}qYsdi)I_|R-j}C!g%KHvdCaX>i`C z(!%RXT-`EfVoDUyu;!v6HyZ|q(CnGiwj90)N#=Y7t`mt2b*p`75QmrpQBM@H2+i;C zXPSay-}Y19Fd<%Y(y9xCc7Jz8yN3;P8fCsRg~&m!wF&+b{G`>sf)SZ2@~MZ>6r*Y4 zjdiEJ8J_`D*@>)x;|TLUluqn9Oz2xt5!Xt5I5P<1+rog_$G$j6EVH(yN-!eR6~FNp zMu1*jjndDJyp-$v^0X9EJf7>MQN$Eh-SLn8Nal?$;P<+gQytxkq^>P_ml(6tdgCV{itX~JuKlluC>r1mGx=%E@dcL3xl@#K zIq}uS_PukOgcWa4X^q((h7YRtcyFL_ae>Y_!to4L@Qd>Hjcnd1|JQV8C1L$i*+T1* z5oeFf;ywHthoP4sMDdtqfOigkw`a28V?f{pt z!_Hhke3a=8ul*z{KOLW5lIimCL{0{eyotA68e?J6sKh4e_5z0!Xko!lIUz<&$l{3V1#n4p z4)xZ^3$&?SZFj#!u^ETO4l!82_^vvkHz5Omn3C9QexD+K~D@C!@Q$W^O@;uNJ*}l|xxGUH_Z+=}6e;}1jYY`ZVmsT2+q@PUv zLAV|Iapw!E_5Y#jyyKeMx~(0Ef=UxnkY1%66$F&t6c9Z?P(cwXkq*+NO9=rRAP}n3 ziy%#U?*T#Sz4u;1AcPV+fp6uU_r3Ri|AF{}WM}WS=9+ViXQ0HT_qawTWVVnB_%RD< zOzL6%al4_`9j_irIkij_IL8H6t5YZejDxP6h-Ocpu@~(F#+0lz+*ZWYvZ#wtBt}5( zwv>XOq4YOLpwz~BQ1&@w;#nRS7@jRWSbT#u(u;EonsgX0agTZov_QlcEt;3#+43ds zS>&^+a)z_h9sD=<06lA{6566%facMd8FFF z9@cBYg2^TFNQ*iC$bvG_Oh8|M@4t22pfVJCAR{MX64zaY5}WT}Ylg?W+VWkoZe;GN z!ks%GFKIVi^8K++-98!I=Tjc9@}R0y09BWlN79N{GwQ@Jq@{;Pth}5TA$I?41WCPd zAQ!wl=Q8+It)<9TJ5Fkroy?LcL)E%CjmqE0ZQTHQ<%ua6AqEFX-+CjG_WRPVqBIXa zx5T@)5;>kfRfoPID|C^oU8w(}3YKo(*D=>TUdmpKr{q5mrH<9#15xMDg|6?@ej(tD z;ZRps*FTPCz9K!+(_`WLA1LWav!A%PrC>9n`W!pkd<^!k-8FZ0K+60lMkwSMG4wNoJWg{ z?B}U{cQ(sz(NoaT)ggiVA?B^&8))2f--5QReEjAv*Q{l}rcRZ8) z9iRn{WS3#zS6#GNy8><{8$(|5l1al>dQpkf@5i0q^<+OlJtfeH2vi3MLldefFL#*Y zdSvk@0UQi->3A-6pJO?^$B6vQ%ZkAASOE~jQ?^h^gcWOCK(?h8ZZ59;*xP;l$CtH1 z_t@#<|a60UW%(%y&6m>{ivqmm4 z2^&;`kL*+CRW0jTbu`aSwx`QAS*W{&JpDe-4ae8V`O!A1O5QhJ7h45PKlBmz6)(?7 z?V|7qX%3o1$7bd&A2ReMrVZrAh6E2egVJKWfv?EAm6C4+KCMTrBljy_#mh}&|A#-I z3g8bcB>Kxa!BNM1t)rq>^~+L;=0mh2v8(_gk7htw#uzsraEOr`-Kad@I}XS#^LMY$ zsPE(U0Z~9y;-<{)X7}dteKDt7JL66rc|4bBNc9mapg=AMy~z~)=_;Dge{*PaPOBNe zM{G1*euWg#<$X9fV{J3T3E#jI{^y;7^Vq*{nTk$sd7oK&dsXO?_uR>F^dlO2X4vO; zhqc$VR`I2WIlgAAVSD?D?r#*oEwpb}+a%enXoG8YY8)N|NI4Hx^q2414TrynSo^+Y zcxOL7&^(bs0LlXvYjed#kJry>U?Etl@mre2&-;j*)IlugvCwgF;+cf}B+!gjka2o% zeh*U|U>~UCUyCM)Tyjb#40NrWq6sj;VE60C?f-0-@>q5%K#w(-><6y5apTwVHK1du-}}{1NL67&1WM ziMVV#B<}c714;gzm}o@0U+jJM>$Gy*r*n=L-H8ax#v>Nf4xl;c-Ks9!+S?UsJ93!W zwT5#BXdo*Q2m`4V?ZoDst+~K(?pV7YY^JR0EuZ37Z@9B$7OlN=8k?(JPNzn!4jtpR zLvDuJoJPidEf^CBR)^o=Pt>60hl`)NG4r2yk8_^4t;ek`bS%gqJKg%x3guo4+Xk7g zQy+_6j+8|ZMMoPVdXC6%2$Q2u((NyNa`@V`S&1Cnq2Nj?z9ht074Gf&{RaJ`;?Z|o zf0%LrU?&&!dq=!(hb3+ufRr0TAzfjWJ=rFpROpamTMPd9&WHeh-K5Qm_+>r4uUl;b z$-(sqX-dqam|1Nt`D@j06f7N=lIWVNW()vZgb8@(bCQ7n2DR$t_J$Ju%+l3rZ$f@# z6@?x+_5M-d!MWl^JpZ4M_w=Mh;gVGv9sc)5$VL`!Lvh+C#}n7q^mF7P^~Um)>~;?C zUhcHdP8BxMT`eTN`z42b-L$Q@X!!|gd}GIk2D+0RQXln;>{^0)B3GE>u$tZsV0n{G zRYc$bFFps9dZ$}X1kcdhBoNwD5%5;}&p{FW0LbIjqW@XqI3O?7bfCUy(D71pzpjzT z4KsH5aV2qYA_vR@YDc_K+F30aCez^Vt)mc0BRazqFhuzx-Tx!k*}CHxKj#rIPWksSMMU(r(8|Ye>9OAwn*1wTUSv2f@dxB5 z0G6l{_quEE1;c$mu)r%>5!s`yfK}!Y}6kp^7L+(_GUUGOP=_XPDpbM1?B)$ax}eKs^66Eh;#dyibTCmCXg-^ z3`zCb3WDntpxXmCjSEVN-QGK%`gDne0pS80f-4vbz91#DNYzZ>1(1Y3{~?e~^9onH z1MqiW$f!Zl_0kpa3inHE%Dr*kI~)k;U}rR$C8BLSFfpRnN5Z@@aXp=3z|W`EKNzu@ zpg~H*?9|f*`lYN78YS7L=DU6iB*(l<#9?S2$9S>Zkz&WtE%**nLl{9KX|e8DFUf6W|=yrFXaHZ&0)>D2A(3!6YUrlF-b>6}PgHi4dIXHchl=L|GL zFTgOuGW2$7a)-;;DYZ;Gz!5ZR_~Cw3J+kPD*nV9G*18uYynzV&S|}JHr9keLC|-|D zyjM`vwtUaC3M8bN;09Lf%5A&W+U!^>fzF~)A)Q7rQ^dw^FJ=gq6RVZE=5te)m7Lsi zY@+90vO{#m;R#qnt|Bl1Nytf5EAh75h|)Sf`)6I2yQ2p;Bo$4dvvMN$*iR&g21OpE z4wDGFhYEwA%tsfs&Y$RcZ`~ljUcxY7|D=Xe@o;eg3)oVZF25Jw1W`_|w|b>cz5(6L z+b{1b5@(c5f`n$Lfc5$%yX#;$_ZgV#(ZeG5>|KKFVIN2mx#_CSMk?rHvFWJ)JdnUW zDFbAHhb)XSjCi*Z9z1bbvi@S>asg6atU;BkZW-p|p_$jfG&OEaGx_RGcKXlrj4!zo zUNE9p{(#%fo4t>Db}=P9fJ6jI3!5=NsT=n*5Y=irK~`TlLk zA@}aBDx4^H?!2;&V`YJwn zL}Ys+_H0I z_VgC>R^oo!f}}^=VpS(Yq&e_E=76Fjp;AO1WoK=6)bD+?nyF0DQ;9c{$s=BibEv3W z6Utdz=2_LT{iYn(PW8GY_?!6kzZC90v*>uLkF+TxSlNh{ON(7UCtBUBXqDf)01Fz?F_2pp5k>12K+!tzXSs-7!@>Ye3z5ion{wT>v`ZB(sBJ3;cZYu` zR*A(HCA@kUms6g$&&w$jNl7F}FDD(V8SkI!Dio*ZCRS>5BnePi&=J(NaK#5xrE0F^ zxrQJ{GIc#aClbh5dHT1}gt1R2{lkTaxY=JdpKhD!|6nz5J>^;Js#%vZzfr$Ztl?uB z72!$PGIfoc%{)oZ)9BhhJy}tI0hSk|);v20Ac!0SC_hHAvfW`O^&zE3VM9g7+3NT~Pmdxsi4kpJg2sVh7*t4*(jENr zyT07eDB0bJ;zzs#)+5HCxTIp@kjf2Bnr|5Ke@^-qd3x}HmN@oqlkYtG7RZHkTX}Vd zw_6^?BHJYo4F;sd91{Uq|ENE;=XYmL-CpdjC>C6!dfk0B5+O^a>KD>Muc;m9JlA;N zbnXKw`)#yn(%LfJup>zg#c?!8w-THdb}Lnd$MM~?Q%Bc##UM%Rchegyq>wkuxs5Zx zsi|bT^Rh$Cl^o7EKnji81^HcR)rZT-=U{@|ICuA%zy`2fCFd|`v0agUCp|p+d?N74 z|6LQ1!vbK%PA78$Ll~dBn6!bHGDq~53zXh<$My)7=&57)VY@C=rHH%f-~_0p~cVdgJ`<}ghLe;XbU=9-*?pY}p#N@UUOfeB-7B+~N$EyE6;I|uxo2DkG~u|i zk?B4y2N0Sxgd~of=>X)S`g%;oQ;VVJXIE(-t_GcZ_Kn%{gQ>LVZvHRz3LCUw(0XWk{(^gKVk@+FAn@99>pQ0QwRMx=Dl;fdWC$y<*eBq=Kh1D zEqmIRt~nn_O)~`?T$>LIf>FFDLj^w9b}sE)jJLtO16;fi(LT9gRQae$P-}}Un00#Y zmr#8&8U{}a#Q4qT()32@w@VN#xVm=xCmWeVBqh!M=x4!PKG#A(98yXABAj+pX$dTI zoaXl!2dV@`%*9veW1Rsu@}b8t97)WAN@y9eW~<1>LYEBaD_@I;wTfGp2ssP77J)kD zPX@OPH`7XC)?HTP!6_gtW0%p9C|5%R3Mj8cQz5rlp$C2esM~M*792fl$;<%~b2$my za5|~CTYr#ru?c2Mw4QN5|70R;(>dY;6c6DLbkqtSSN$9wxSxBaMhy7gN2%dyBH zmoS-$w@ow)?)vVDFXIOk129k-YqfS;`82H|`?fbbdgJ`Z-__J~Na4SI$p%Q`F4=IT zdTbd3!WRv=R%_Ke%;#(Qj^Sfvt6=+(9;T38FH}C6Nh-<6MbVIwGSlE!aZ$4`5coON z{nQoj);gYG%c!SEU4Q$CClN<^X0vPMNU>qpi9Y=p?({ znnr#gX+)+n#938(`>?mDRnE;s`_l2>vbsh~{TZPkfBGjF5;Q-vwu;nq7_ZzfvplRj zm`66+uU>h#nxwD@_Q{|C5jSN^{u8kG}aq3&E+Jhbx5FQ`)iZH+^Sdm!rSbWl&D< z9?(3|18xIyTgWz~Ud2CNx%1vgXd{`X0k2nQ0he5i~bI@ zVVT`L0VqJAFK0_BC*F4|(nBK%~r-Z*`$ATvk8k#MA+rjaK zlb*$FsdWY@Ldj>Le>+|n2&WNCI3vumyl!l!rqw?pH@NFZ>uPoj+6>tjbMTa!52$Z0 z!G!^B1MlWfXMCp42f3$oN!g*d~O2h_L)xq>?HI|W6p2>DUULYU&G2M^qWfZ zWJYPr*nTlsuIBlQc~w_f^Ih4W;&m*=k4Cp(I4(oQ<QuE-u5%4gi+5gDN~@0MFf8G8okz^xs#;Bw zC)>(Zy?jUYxc#&uuL;)aR?3Ya(;MX7{JmVp5+Ehez~KZ9Dy2lQJ-LvQMvYc}RFLGD zOjEc6*q5>mx!njv*WwQwnQ@n*;_km^1F|f;ihT6G)&t=4}r}4tT%k?HISQOjAnuZx+2HJecpE!DRMcZ@)}g# z+|Gz}E}8YJfVjl@n&QHX3SWb@C$3Erey@o9)iV$Gix zAcMaa^bW>HD?m#|^`QGkP*m`jd4o+hf#6S{>PgbMThvlIw~}B|bH5?JQgrLB8#aB4 zmtP)!T>xDrYOWw{2cUW1xW4u0`;S`2H*A7z5^O4vrULJ!n{&5HFBBKXgJQrmvv_5q zVFb*=uUcQ7&5<0DwfBO;-G;{hvgwu7GU`(L8*@oT%J2)9=QI`$j+-UX^br57^S?c)f*E#ID7syJ zgWW^g`j`236YfqF*Vt>QzwS6oaBDt1hn8ZXq+z*QpKeJhH=pu#26VI-RwY7D(*?Nv zJSbaC&^=>9586Jn{5I~~f-&})u&w&CkEk>TK4=wH;t)d0@c6w~O6SAp1M$b!L`_&civ&VAz-K)eQ4o^t~Z+2oX|>V7Ywa+M0!C zyGiXWt`#1Qxx6D!Yo|onB6{4u5GyV(aSt?{R0l(=VPOR#v+7e&KdEowk7$(=<^0lZ zSskw)3t#U&KYQWUA-G7)keIf@tjo0jByk@NSD@Kp?vShTQ5Bw0dn+angW_uD=Wy4= z@W)rfT_SunAdKa8Ktj`O@Y@~D=JiMz54nSBWYcoFTF2|*MQ0POWWK#bw5WMgeD`cC zRNXZHWI5iIV>xR39f%?zm%0-E2tS^ec7cpbu%o|J?T7+HRxUM3Pb`FUT_$2`MqkP7 ztYVg;;ej*l=Um8rM;9r6tH)EGyg5@+ffQM(5@`h#c%rMEK@Qk1Fk{in(2xBQzC3&3 zLUkK|yh^;8`hcdS;g7W7)KLuG=*Q6vd}QEkgndmi_IDCDtJnQ@Tc2dyUB5d@L0Mc4 zbn&|GSO_puT$87-J(51ko!B}yzB#PsVwIL6$nE-P?+P_EP{qrq_0^hKK~VyqtfCY@k3^-V3!p2CP+m(`Cv2wc6FqymFY zCFuJ7@yik#u#1DD$A1NtD08 z43l@RMQ;lZO}ifXj8^RT4-=9hLx4^mA~UayY$-qWK}6`Im~)X*yS}U&Wqu-RYL4HI`A1o7sK2;BC5NA`!JeJ5#uEw>T#ugb1J%%6uw09ub(8<;b;lT|x4f(}zBiM^wJv8v4|p69llXgE zlbD8)ILE2cHFlK!xr0&LlCF7a*gIN%HBD*ZEpX`l!cJ(mbh6;YRoM7IwWO>g*)new z2a3b*GFVnDoG}7x@Y_*OJjmm9?6f<{fc8M>eI=6G9BO)>+Z$}vdxz-SD(Q_uP3Q>L z8x7X#oIH(fPNeo8srjfPB`90?S7J}U*l8;z=0*g0U(MmQS@ppt;OJ#hqwSZK{jTr% zU8I4Dxs7u2$|1KM5Uc+7K7ZXflJ3eMr8DeD+hrlU*&w-oh8z5E1UllX-_|p1stU({ z?-Zc7tTh#t#~){+$RjEAY_F0(vW8>zet#;3NcleK6I>M2O4F@hm$TWf)TIL!dVG!=p99_lR+Hd!w3_B^yV9rTzdLFc zKgLieyL@NoH598AtLj8Dv~DR-Xx!NRYTl<1hl6C1g>gmR9j3z$)4Y8o4`#^BRBf{)t`u zHj?2WBH4+&n8Eg`&u)R;sy0NAkWXjgt~pieC$dkVl8^*fK!DylJ0H^`J{;Em7dGN_ z5o)V3+fkI;zqwvX4)3>AY>#Y+t_3#lav(iGYr<26(^cJUE|@bJW276Rt0U;t_-8vD z{SOmDzVt^Mk;4~9lD^*8zjqpN>AYFs6=!)@!-o$T~Z{ zs(-B8+Avb-a@byX>)`wOP+s7pWizF-tk|L?Yw*=bc<)xtTHl(7Eb&_aoWn1rpvTs@ zaf}Rgm*TdYSK4rlVwoT`p~A(PUV7om7SN)oNC`iY?p=-Kg}7j!VHGNPyWPUE%^ou=an>2J z4}6{)#J6>9%L`2((2L_*fjTn$`M2j`zjyS@u@`%$&Qh+wRg|aK%Tp)wH&%J zdO>1j9`w_PZ*t%+JL z#qe|1;jh>HJlCKXZEw`;0J@+Op=9 zh+k`!T0i*PZbELN?NS0UX(vRd?{U9MYMR@4_GvSd7;+?9*qsU<5FdNWd< zhKRbwZ!hU z+oIBWYFBD^&blOLt~5lPeWg?4OMSu8UZ5DI`_NlR3xQ98u#A0?`9^UEc8=w+jI)91 zwVr(#@ZQKplMcD`?bGKzyWArLN{K$OfqW5F)S8X2e(H>MUeVdpc zq_!qAbzTVEZ_VrJ_a~#SQ-A16C1={R`_x}nh0~Tub)ZqGft*1Bpa<1KesWtHlQT3+ zw7GKbwBd77PL7He*Xitq!9>COjWuid)>Lnj(biP&e*}G%=k0kYs93HFybnD04f#B$v44y!eS$d)@otSgPc#LLpHK;=zuSM|YVw6LX- z`zvQ2W!TUv@-mFR`${*>{;lm>-{PDGgBG({4@_hk%h?`PDStinH9)hs1hzfAZ7~oS zhToHXGBmUL&%pQ35&u(h9*5>hwLy~1Rrn?1;#irxa^;> zB~#C|D5XT}fPd@zC8FKxO1E6Yx!1pnf|Rbd@YS4=dez%b7EZ+$gOz8}Y-Dz*@%iBM zSiv+8N`bn4X@6BT6Vvr04PuQuhYFi(=fE^qJO#^(<`g*kaxi&4a`e7 z{2i6=zTb)yXOSXTF#toW>X!H=9u{A;YT5Ma|W2I8t z&Lu49oR1>oUb;>0dkSIoMa$sRO>;XOL<39Rb>=|x^x7fhY1EYDFHEBY%3jHWL%-$w zn{JC8T94qb*eFB6pjyAz*uazaMb^Ku+0vAvY9(X-nnkx;==y0IK6H|XrIVIB?WB+t z)lxR{7!m<~j`|zFzx?dOL^Hl~nS}vL-Of(V-cZLMD}tKN5AjZH&PGu5XtooX-j60d zYBm=;lC7ocdP_;Qg>F)&gWIFfb(RYY9(I0{izc#VM(!8is+~(a+6=jc&(iLDZ?-p? z9(|f70AD@w$rH;CGIVEWb5@bj2W>`E1zYj2-XK(4aZ2LQX|Uw*^>YNv-uDDeenl>k zoz2=R0rKfbk~l}uOI|&cxV9Oy5iYh=vENF~XDsxQk zQtdvN)_rF0&2hWI^YE2zkR8UKzzVszW3`)yQk^Fg9OJwbtYIroW_>98Eb?TW=J;Cr zGKZ{L^7<>IM?~miF9u;_`Hd06n3CwGl{sD|1C1-R^eE&;e77XX`!dZ$lS{?ssCR53 zzHGJ0OzrhC=lUY95ew+hO;?(wZp8gH8JHygk@6UqeV*Fb5bh(UC`$uX2UL z2}virV?0B?fhb>{%;8|{{a87*8KpW;LswJCX%cr3`ijyIcCtK%gZ>rrY|>12w3W-V z-BpHy1?sdrGVHJoDPKwEI)J+-phUH*+HNBjXbj)I_z~k4W&YPc>k2e9#9I66!BL7={=stDP)mWE>rNnP zbvcoq&Bvxm3WuE95ESetJtD~AFDV_2&E`xU+x^V^BKD`i)4^o&AXzPSQJzY4BrZrP z=v)vPRfHe4=Z%}3dbm`bJp;FE#}>$&JUE zw?9~By3JAnhwAI_q!CQesg%L!bM1<@|E~ZsA(H!#S0YLlaM)eTo-8A;qZoUIe7cRF zK(q5_!%{8%hG7E~W|?&w&U?2$Z(e#KLZRPbgkV@uyN$IP+0AL@S};T{^nbN@NGHh9 zJ;THIjHAcIdh|IyQDX*vxKy(JNFd+oox%>cen!t0Msh3;f!r}_&!6lU<$omE5@aF0 zjhH>(B2p#wEIquqQl#)t zcI9$=%uZN2^-FY;%{Wx;-jrox#E|&5qrCKt!?3m!8hSa*awpN^FwXk!n#39Y?q|uh zi;_684)MQHp4wEb9eB(^viD-Um3Mu-=WGn+odbAWEK+;VTL39=`L6;C;$585c+zu` zX}faP`m(cN`=+k#;~BW`mo$2^{0|f~SmW?ul&^B)wiQ;$99Ed7$B5hkWi0RCewt_o zib=;ozROpA!=2RQ5$_5j^ZF`^dqXVa%9mu0eE^6$MJTec18U=W$Oz_q(|K*p01i zjd8^oB3Jp=BHh#m?`De>Jlo0PoWoBU{<+IN z7U*O^e9$kF3SEL*OQ7jsa?J&|BT*$_Bh!51@O?#)Q|$%h*LaWXs{<5{gXmfvy-&6I z+&Oc5=9jLFZ0{V~zC)8##Wm;g#&>eDC4%6WjU`4t4RKy)xLDd-b%B zWI@G@-I;x6_$p!7_PEXhy|4|-^on&5pV|{Ytk1d^uF{N$;qNXvbPNgAEfdlCl4Axb z12HqY(>O2FNMj9rX@TT`RiSEIgR7KtNF9P(G}j=*XL z#1pr11hg01Vyc0P#TT8&jLbaq=@)~`2Uge4&^{%yPXkg5*e?F1N$ail@XMWZm(3Iw zBH+id%ezHGTH5GkXPhPRJaxcThWl}|)4&|7pI5t$_e5Q@%@mc`&`H`Wt5qnUN$h83 zvR72hxO-GRy=ruPw(LqhNgqNPV;7xGvTS*}&o}I?Y?|e|bH$C`-QmuV@wMYyXwa zh@GTQ3cWZ8>TtWk@|p~eV=wvC(p`3HbTQ?pB4Om8(gu9^Z7u0D!gBYVADWdFlqIu1 zn*3Iv&HO)7Sb=Nt?x1l>YT^orOi&-COHHDCnUdCF$n7@E@o1_L-ny;N_@{CcXt`gD z@#l!KM*liD{wtgQ$0F>f29#32_$Y8d(b6#UD7JjDMx?*9>(hll`~ z_CJ455uEh?`u@Md?f>(i{_*D@Jp0eD+};6NRd4D8FtOd}CVbe-jD|eM}rU@c0fCjrE>j>7VBvH|60JM1n7s=@8+?$j$qBm*%CqZ1w{a5%5wqb^&y& z!r?ifYdo#6Q+Qqn@Txd{`G2kQi2T(7t)CT1vc;ZjtMb(z1*ntdfn%LyZ^EnNt{xhB zklZ%}5K>PNKt_YpUr=d&PYNj=0+|gG? zAID$Xd`_B0L`LscfQ3=4PYOgR{{#+li;j)caj|BJ^CW zZ(im6-&>R56asg3Wmg=Z$AVcymIY9RVq1Abu~ox9_=5#CB%Nkg0~0%7mN@+6Jbl}Z zRa1=1lvzn|_S}fQLke&+j^Dr+wIs1KDg0xr>s3l2W!yG@1qzc4UbySf^EnlJ5sV@c zDBQaInSLsG%0n zc8Z_9AkI>X|Cvl5IN=L)?)2_C=js%;C$fLQf5e@g51A@Fa@Kb}!VUI}ak8f{dx@6XDMI~7|pQ({*R#(sia9x|5 z-zc@mxt@V>X^t@ZVBTni|2$bah{Ni6Te`>87gX#T7r)2kcmy;_Xyb+PIiO3Ihppv; z{!lR%B-OovCs~m{QaoP&-4hOaV8hst3@~be3<(10+LUrKTau>$Rn4g>8$8#Ll5RSW zH)fE@5Kx+jm@N}XH#%PdU`+{NX~_I?x#{Eyg$iWOyLKm}l$9Eff|r?@ogg{ZGinEr z&M0Rmgal4gQxf_glc$4gULV+qd zNwA5O*ljc-a3ebt$Tx5M{s~OyR)+l~`DFH%0(tCIiHb*7G)5tTkelnjNz)c41ydW?saEBzxCApgih?6%QaUeCj;Dz4OK3N2K44jJY@NYUGYfSbR zF{MfFlwv}75a<)Rak1rMQOpbgqtBng81jIcI`0h_xP++gfQb#g5_JQA8=oD}5@lP& z+wH~F3Xm|?L!__AyrNsY_+9Pd;`|CogvfETNKKQc?&H>(=O4vT{Z=Gem)W0cYi#1) zCOV)wTe=An|9XL-e(Oq$FQ>pmAhQjaFtRdjWN-%>{nVdH2}SXi%!$cy>7M(2V?RTu zS$8%NrjE{(o8ZA@?d~uC(DlBXc4qmd!}<>Tw0u=(iH}43w_T(ZyEA3AcYOG@{%gcw zIc5HZ%@{Wp$w24VV6UNTX$1YNXLf+|J!QJ5Wx8(FJ}QfosYkbpn@KRu_e#z%1EdH< zZsvG;KTToP+?%bq*~|QmEMAIz^#`KvvDub==1FjnroTM<$_B=w+GSek493S9A0sj2 z9aMM?-}{*Sb^ka2HPU{Q17t;MUK`r@%B2Lm_Eq&=RL(N^ z;z&2N5Zww`R_bg=DaPW>t+8?wq5Bx&wO>2ZA`hk$!wd(pD)McST`o>oCl$#FMLmf3 zMQ@T)eSyYXsNyt~=tK^8)^re<`NMQpC$ki#?FqD#xO90=fr`6v#Mqe_+`V0Vm7DB5 z--4OLc*RO_UayucOE791?LB@NS;X$~8a5M*3AZSbw^)2}rAl@To`FL&Fck)_TdOTT z`8=r0>J2R{{Y7r=xd9%fK$p6Ss6Tj%!zlQ8w)hhH_}k@Wul}~gWJ9&3>@s-+mZzm8k;~_EZh*x&v~JTH)Sfy$KLcR;A~E+OAtQ?U}=&wo|>5kP&gNlZ2s4t;OBV z{iP-6_-N8}rhUplAaV&TKf%SMaAx_DV;(UfhXLB+>Cx=+*eG))Y{?kB7eBjk_^Kq7 zP@`Sv&J#Z93g0qz?SCw=PmX@p?4}q|t_thcv>R-W@>+<=}M)E)_Oii zG*l*fl%=H z$QVt9_yyF4Y$L4+<*vYY`_aedm~Ch;ZWrr0PIWlGu#n|bha&Gn%eUd9h?NVd9Q)d$ z=~k57OmnKfR!jmBb581kh{!x9k+xa+>Xc!KudiW*gnn zJ5yNnF?LFd0!5k{Qs77tsg^B26B*a?Ym78Nj7t(wpEc!x?KD#$&ksiN3#<=0E65rL zpS_Y#IeSV$8{masUko=DTaL5YFsjJ~r;?gCO*E3&q$k6&YZ2hbdr5(Q7en_?>;?cj zE%xmS%G>c4bXbf)~`3R{Z2dS zAY2{6&v-DucqOp-_Z7v^lW_Lh)HIb2mU?FcCf6b$Ui|&86u3%Wko*nv)mu}?^#^}M zazG^ep9f)0rhrTkY&8iowsS3wZRR@lK7lQ3`VGM31Qx;Ib)X78|C$>fuE*v8;G7~q zflh^Z&~<2;791i0$sjp0I(ap`-A0XDl4xkzKBjtHp0xpSO93+h&^OJ@$jb}o)u7B_ z7r3p@wxeCgk%t|_Y0@%Mqf(Dmi{%KtO+^eN6v@k|0|3d8aQox*4M2vk%z3kF>xBQ= zAyM(F;}CnC&C4n0jv&^}C5J0;F0+!IYE44r;4^i#ItW3yt~8&?w*{M1K)>pl4Aq_j zoY}Oey7eGMR@n_W2E1oeX!uC3mmU-b(2QjW^K}3%XJiLzk-59+Go;bPKN@wIt6fbL z1Nq|xUT)q%GKUjI&bK!Bl=J`Xu{mZI6af)BE81fzo*mizSDs?nZ(chysFspc}!Yj}TDjfRq zG+0+nSBO+pEx^uBpvo7LYyCNW-bn?7NnFewz>fyzbCfzb-*%o}%V%Zji9L?12W6A@ z_hhh5|7;=4Nq#ijL6N_89zD1U=y*^V$l!eD%daU$Hz>(tw-CU4I-Xa9sBwL#)U)AX zw+h(DU(iGOhc2kdHIilzLy1C&wL_o$NG__8XLrW}5^HzJj9Gt18P)6SteN`Z=RDS< z_Gry|D`Jn2aUO?soJK1m?5-c>EO%x=0d$SlyRWVl@tWf2@enuty>!5(pLA9XS|5{L z?AW?rmCmh5Z25<^D99;Ius))e=|h1{f5o2ttSpmG+r3{WpFgmYVJiDLy$q~tVRxvN2+9Cq!cEWS%5d0|@-Em{?Yo6rs#CB0 z`@HaEpEyCjhaBI9X3|)ULUFup6 z8o#bl@5SB@^EzF(8I3FFsb&qQ2@pU!k?r&^k4p4XlI`V^g(xm!OQ%)F`RdGB0L+Pu zgL}^nn3R5J(9U{g9-g~Gg8*}42}V5S{E)leN&|MF+_FYVgB4MuWNF#E+$($_eZ|zOu<%wLQ!>NHx|}}>wE8IwND1W+IIOKFl1!8P zp}qWtHuWMp zKJ!9t6RIrIqVya3v%$7FB?$_Y7TV{Zv&Ltqi4>{>Wb%8%Oh-3lhyZ6kT*Ca37+ZoO zHDoong(iS@zH%TI^A-iE zqlU4)f!vLJ5os5B+(RWazkGaLkOab>N;JAcjJqNFPx^UQwM0|@5OKF_BeINYD(4s|LsaWORdz8mdIkwsTiv3N zmXPvQ={Ud%ese&qOg^v928$ISW-NQk0BzUvytwJw^hfP@wd)IvW=&wsb)($tZAfMOk?umrlvo5v&Mtb-*1MIxcDYQnlTphbvkN@El2@4>ct=z!BZ=BO!BPz3MD;9 zCa6O0bUT|v4!}70Rk%W?|CV^mi`CeI$4GE6rSeafv6O^_2Mf<9aQp8sT5IgdzdUnJ z%F5wOp|)f&@nT~zd!cKKTJR%!;3ezz6kO&4z5}fN*Fts~hQ~`R2ogJ+yTt2Mk_j@) z-Ayk8nA}Vc;ASt>k`_iG6#QgAfiQgmXq#V|e|?@xZ8iqX3OTOBpk@_@@2P~c8Qjc& zxn`iZ8oqGNS{C#=8t2UBN7Il!ts}CJL0Mv_QgTzk*BsF0uz}S2CV@A26n`&Asj`z_ zE4>e6U3vk+B(Ox_38gi3oa7M@&N9WdW}h4X{t2pwLD>d4PqXQwl_t*)lg~mtBRDBD z2yLz57Yr6A%_tirP*Qh`8lnU8%v#%o0i#2?j%-y2?0xyT`W&wmb8OJg&<651cW3M^L)3i)uh+8D zBBa7Oao~llTE8*f_%&a>C{_WfxBTqByYz2r{Dq}4ds7vt^}b~(=C2{OM+#11TF6Rp zNIZX#Ii1k`-TU6#T10NuVp@SlO)4GHgq1*P)k`Nnvu#jv8cfh^28NKUN~M)CBk0A1`$DTXg}ES4wD{w%tUe%uW?l}N z3192)v1GL~YtDpZLL-03W3%(Vn4q%ThlS1yu@b5rp7-pp?>L!$lK5PbE8)yTyCjzp zT9O?3LJ#r;V(wHZbeMf}7n69dEcnwnEHWyRhH&1yNEGERk&nM)!B^O-u$ZHP`mEj3 zW!1}9-6Qb{Lxvd)4z`Y18~;hIkjTe<0xAKKo`%fNv5HniFjTERAKkb()J6Wxar${H2Yv?W`j$~AP_Mq1- zphsUP&xPZ3&P!X>epVzSYa|kteMqCadz(m*4HH+i{5}JkhXcGckWh(y?`FXT-EX0{izHVC z4n6?T`g+m=Vq)(x2kQ;YON8OuU+3opBG8UW|BtS>4vOmk!+4hv=L^yz0^JY=FY8|`wzn(>g3^SmDU zt=+&4HY=3#t2~~{cO6Vo-o*@JZOwYeGRGTIBWR~8x7~-Qznu5p(qTRKLtSWVjzqan z-(UG2NO5aI8TK_S9D`og@3;7i`<9Thi|=%D6y90}Z;pe}9erQQu{c|=Ay{V=r^fK| z6m<)_V@%m)fSiCPJz4##7*?2ZqGW;CP0EvsJ*@y+EgZ|cZ%!>^vN`eUgydLy#cKgdGMnk=m%xwgN8H?FUyz z+!JQ*_S*}ETM4+Vi#n@%58(L9nYDSRO)Y1$wz53saz(Hc&wk{Z=*f2_r8ii%xBPmW zt&X8+P-z7r^OI^>F%6!O82hza&;rO&2|(C|k&2UWcLSb&eg5BVxN{rsSFqv z4}7o!_D}sdlSMT@z_`O7?AUZgH$EW0P!g_2_PXfq(2GRi*~f?xbU{>p;j7`;WUK4| zBrfu$pVax^8Yl$aoqO!v&p9kr$-gg*ziZEBXy8V+%Z2Eu&3y2E5-<)JHXo_KX7?H> z{V1tNW%{8#{SvoSQVw<)133i|20l8+TZLCL)RS{8KOS zqpiQn+1ma8jMEgYX>;PbL7)(jri#+5hgo@mj$HMUy@1qVuXQfYH%|FJAXsnRiQ3Dx zTR^C6cY3KvZ11VjyD-FVbLdOA-d>}Wm*EnyZtC-Os5Cs?`XA3%Nx8~PJ}hgWgq=Hg z3$&L0wnhq-WU#OXrOfHTtoV1m;a zp)K4x62alssg$eux%E#0rYpVdE#Bk^^8Ix++=3S*OJ} zID{c{o^RRQagfIg{c94mcM0YotHclrDRsE$!7(GXp9cw7X%ze{K*eG!!TlFrQV3#5 zn$XI=Fhx4Q2_FW!ZRjJPUAZFj+;^@#F~0T(`Qt z{FK@I*k9F?_{wSmYwJFi5k%M)m26Bu_9n4+Tk5n>H}U<4i*On|mA&VBGK9Vk1@qc< zt;smO@%j*FvU8=NKrN<)ViyNJz$jxrYy4{{v`8mQBDdOO46IlbeWQeocb*RIe@=$E zcs!K2rvl~bqIkQ2RB4NR`90E52c7^oB{ z$w=(Yw&f4c*1$||KhEB#AVP@rYQ5R;2gIl&-v9k@`M5H#I!vT8%OOePprJ@J)uR-P zCCh{fhO)Z!$zAJAbK^e6n@P$9LM8yx=l!-f9mWRY z>*oMbxdDNHCxw)xXd!p|)yGdo<9q*pv)CZPd->t3=hrtnnG~x7&qfmrVjb|jIYvVy z&2jGO_;2UbTRU&>xJs9VuxG#bDja++H^a||!^jXymC)U+yTa|1c(2Hz4BsTW44`}m zNK)*uhk~2@LSIL8SoU!S3%yw#_+_9-PRj=dNo zbd_#e#2a;!A64bwCihTqYAV}Q4}lqmV`CTqj>4h{^Xg&^vGFG}wN?%s z0l>pL?J5_8yD2Q)3uSLbeFTiUVIRIquXks&#pQpacJxTM33Ll84l4oU3PkcLI`=bh zg@Vbd@T~@YknE40N~)xCg{1mSg>tN%pT_-IdnK6a0-msdSC(Z`AnRxTvTI-x5y%%C~PF}xxBkzy?J^xs-u4>d0A z7%W~k1y%30+qde0CP}94t85qAEacV<<2(mK8eYcPXeoZ$APzBN{_Ojrnb=|M6ks2h zl$`b(_t#D6kA%;DM@@r@F|#UT+zgovi$p<}OR|p7I!Vnwc=pFxGa>Z)B&5+^PGCaL zg^`U4(~>Z8w@I#$5J{es3Qf<9HtOr0?T{GkpT+w;nH)18k>N?wyE8|APs48tj3vZB z*}QsUJTX^Ri3hc9RVMV?d-9NfoEn`1u*fZw(30AsH^JM_+ke>cih$F$d2^uKN2sZfz)>o?@4@>l#%%!Icvuw4fC-M zFVB%Df(%syixGfH3(nSU67u=U&gDUq0`k+|0iK@9Escd#>D&jkC*zUW8P|vBm?k1< z21+#N7_i-2unDS#dN5pPpLK&`-Tj|`S8(|l>nyAfo+rB;<%(8DzFbPMFuB9{;4uhs zRYB?ymEMuHpNng5`gxr`Jd>W6R^-uS+*SW0vLO+OGbCGbfKaq>D7P^@F0Rbin|XNt zzPL09w2YZ3+3D%~5lk3VAdW5qzYK<3gxq%l8Cg+&mQHv>d5)`XL(h@a``$)b+m&R8 z{=Bi4nx>1(y&`a26V_)G<~_ZkL&3@pbBFcZPBd5QWHzN&a-fMouMP@fp_MrSASB)S zyu$J2dK6fNIsSnx^2^O!+R$?>vln3}%{{0b^_J_fRP%^9f@>jDJgJSbKcW)ZoFkQ--6tiR`ADuSK z-Z10Ia?YW-u4JYt0B+)nJhm!{*Yrc?-AI~AUhItgBR`+-t1|dsa0996c%GZ~EWc!Ld=vK!6yl7Iffd*PO=_xlU(KPajA!8r_$ z71B*K)qdN|b^o1ER?jT0L@Qt>{v^Vl!-q+#{OnguF1gC5i1k+l5k{_1os?GE$y2TC zC>Cfk@B+H^-W1P}{u#+d{3*#nm63?&Zv#)J@MM1@dTw>TSUu#v1$b43XK-Kol2Pm)Oy0Z)MN=J5sji!xp#GNG9Q13bzt-DTw5ot#`7X&Ey&5 zRhDb6M={ddpEk>N2}Qr?GRYJ+XGr}Ih+Zv#|C*6*Jvq{cN}pE>9D1SWrM*j{l5os`?nFO4W)AQWLS!c4(|Ag5fl`rDknTZc(8(?FQjO08 zZzAX%2}k2#^EMzy_^-rS+F3O;a#~dVJyR2B;P5Zc?>{AmnOp|!!23x_8kgIPo}Kn} zciA~2x!b=JyrrWjnOnxcpi%$<;|U;8osy;|cd^F(%?66eM+~?h$Crnkv58?ecu)9{=88zE-?D_gL$`WmeXbBt4Kvo#r5e z*|IEwV6tu}j6dFHm(PbZgP5nY<{#v%n&fFI!!p?AgqU( z1=5oXBh?f-jLS_Asun!+11w}xaxcHv9h-P|g(hw7s4ysZ>pTE$PPj^9RikLqHdRNC zmw46(Zk>g8)fjgcc0MtRo`5zr1r=A)ty52*242T`9WweX()zbEOky`-Sf$ts+>`(hAs!&|)d+8)N zAP{3xWNzqnSUfWE+l)CLYpdeG;;Tvhdl6_8IL^bSt|mWLFQqzsZ!@DUMYMV!giO(+ z7h&$Zz_HYm-+VY$k%Oc>nag!!M3{ISfyQGC)2@GDM~1*{sxw3SLuhzT zjlN%13E+xiNLbRCF)D^^(m3JzVcoooOo?JOS7hdchuu^b$G=Er1~}`{&9`8-B8lK6 zI`N$~&MXV@QfjFd^nwy^L5y)fX|vX9 zbu0~&SkTVd94i1Mx_Hboa3$)ZME@|DuVm>tuU?3?S#yvDiCn5XXGx6{okL!fbal*p0@#ywh_LHs`ch;msJA# zPjVHz?di2{kag=}W?Y->(>%xd+U-BQJ%8p^oaCa)r3N$11HF!|^|DYBN*l0WPGvXh zK>-2_kdMZG{CPDD36-}eCfFhEnv6Flb;W!c>IPIJ+b8hLO^1Q(Ky?FwO~w(tqoHq% zq%u{j4Wisq=+BoyQ@iB%>ozgKO>oJIEUQjcex$thz!+!VhzX{>uwGAH$zF#JLXe{|C;_(vlk_ZQ89%~pNUm{>osP+^ zvB-6=RmK^;6_8;5`H;$T9MbWrOD;b=A?n?LekUSFKe4No=mS%S7>!VMJabzZv@c3Y z@q%VPL@Acv6Z}S@k}c}o3$Q8L=)j52*EAd37>^P~;X-^M_wm1BG@cCfbMQYeA;aQ= zf)raFQP_BOI^)p%Ede_Mf(mBax%xWe_MyOpBNMflp#~Ye{4*l@iu4Q3hP#C9OL2nUX%?Rs zAd+KoYO82B5Xnj9b)kK6gNTLw=$ra2 zH%}~H;H!q%cMKwD)GMa)GlZTpKIaoSYLKf)duzr9HYb7$L|T%pwWVG9BMg&bVV+%^ zb+k2y=q)GSK1a*!V9LM-38Tjkh3Wb++bB)^NzYok@ew?9;#)jq={NA^z>5Km5wvRw zuir-2p-9Y^O%3@ubsQ5K>cfG*@#cKU+?lV~)v69b-I18rMepcd5L)8yAWY(G5nn7T zQkj^1K@UgiFSgFVGpCVftmiN|OMkORsw~x3%QX8uVqJPEu(_k=h9I@p{CGZJ=&z#K zA^Z7Ru)}q^E=>%rL*PJLUeA02UPzG^_VGzZVfLf3X!)opFbODEr zu=#nIJh#UEQJ%O``?kK*GID##K3ItB^CqqC=y8HEPT|7Jeyuvsp==zXgQr<;|BMv# zdUN^yIBrmxu}$oOlkXY5{5W~6$*?8R^0HB-wdxy{V3K>V362YK%u_O0{eZFHQV-}(0OpB`tcX!!OP$I{P0se! zYcqKEYsT@iy@J}2J&RhreYwc&$ySU;R`l@&YzX%Gns-}J)6?(*T@FQ5bW8q>8^RPt z_eJx8DlvR56TPzA5G)p-BgSIBs_{GhjiGp8s%JkXU9Snl#k$z;s9OB7%~Q>$lFKJ3 z5nddh{UMPP*X%x6r&&gH+h)OVO=aEMHwJ~bFLv$6#CxZWDx2Yqh(JE({iOitM&EV0 zv3zS%?}#touLi0Q|FzC*4I3k3PIH9qvs0Mk6G=|Gh;WfwQ%$F=3NDI)h>_#hx!h>` za{q${`|bPZ?@6b?-GP7hijB|a;|;1dW4QEOTXq<5Ty@(bND4(D64adRh&f5SLGR}T z@rKflf2S>GDGCPRfA#~5!Qrx3r@Asuazo}{H^%yOq`M(3WjgYkBMvS#v2ML~kLg)u zlTuHplN6@SqMsM68p#A|L9yGHw*|_jX*QElp^}$U2D!ZE@=l z04vWxEbR~%b!OMtcZ;K3vJycEAhqOZQ6$%Hnuo!@R>J$C3n|=EU>v^UZ}~PZV*;t} z;#~7U)1=-_v1v%$aU%wD($(FNsGy{Pp0(bG^ERgstm&`L6{Fgkq{6*&&Zo`AUu$Ry z66};5OPvd!*^USsa;tA)T6GA!Rc%oip|W?sKo-6D!?>0|3hX_Xx2#M#ivj{P2{OKo zXUy~}Iy}e=qa3GMJkh7)O5+T8uhZ?4bjj{tthxz4N zTO*=f;BAL>FA2=*>!5XSuYWR)i7FDwhD;f;eg1~q&4xF^C_d$v^#@IUm*QSkCf-T6 z!u;5rqeyLgK{E|hWMPmn-Pg)Q>{b+%J8m;$u+f%hx0Iv#$3#%`SwXd~7OjX1UK2V^ zrmg8yk89rR>4EZE$`!%X5}fA3@`0eOu7EQ8iQOV7t+*C!YYfa3nE$00(*3NvA`z-i zb=^OaGr*2P4eMWSRgS!BHNynfo3XfER%8`sEo%mC7-%dv`e|8=lBb;wZhCM8Dabu< zcwPgI13M)PM4C*8`OYbD>XCrNg*3neq4Q1d-6JeMj-HnXLGCK!yt^82mjKYGZJu#I znb+1#C5hQw%E=5JO9?8hn(2@fv`_fZ=R$F*R^Rphj{5YxS)l8HNZ`_Q>%W2Oby=H5 zn>`+~Y1jnmxgyu4WMP%1YOKp5-|kPXFoa85e_fgv;78bRk-q^Sy)Yy(YZj#O=@*}^ zjNJ)ntrMh)%yNNkwlK$;W#s?TZAJe(c6)xYAr`#t6T;Z04;M3939Y{>b}niA8X6k9 ze7kg-$Z=gun1Z@Kn28$zx7SuL8s}Cop7*U_@$&6l+ny%|_xYsOlx1OfCaj}n0iQ@o zz{=L+A>9iQRq%NSr<^6Dm#ujndm(g5JYJZmlo@cL!s$I8n3wu}{z6Jxk}dp=n!D5j z76J3g^EuM|XVCpx*V4e%N;i?o&oPcD-+j z!a%hkSDMQP?D9r_q0P`=X|{KN$7-`7D5GkYAw8zATQaL36f3TUQwyL)i)R;YVhJd^ zsXq+bv0Lsx=Zzt1`)|(l-9*MEG{<3;$*jaI|8OHNF6IjiP_pUuz`^c@x3}B3S1Xb_ zea+68O&+)R_#N`>bsN<#kB)OAAsw zS0!*3xk3sV-(I50>Z<+`;fvR7GPFTgfp&)o^rNNYu=ZR5hcWPDos~!w%B2nmTawmD z8Yx}{x^_wL-4_3ivmUazZx^d2aHKAnRw^?d-e5h)}Hdi^Jj{@S&yB+k4sGfFChh9$|Ys@UDpw%d@u}<`mdh)Ma}c z+L&{BOQfmZTQ}mcMmF5n_2qI^*8nRHN4LRpLxGMWFK9s{<}##7mebO8G#g|=(w)Su zN3_~^9ra{o>%kXU0fvg9-A{y>Lm&O5L!><_NZhXk^CJxp$Ee@xHt7(jId01Lf>b(HPks7vvk6w=WsW>t>fA9-(lv&@Z+WinBdn!2L8d{^C z<+jn?MIgdr^l>gEf7<#Y5|mr$1^9!)xoOFLd-B-!CG{Sm`Bw)CcydQTPZ8`PwLVzl z74+zMFgnPKp<##|fW8@`*C9+|J7APPQ%ys=N-(=z;C~cRX|(GpBoSKVk}O8J7R?GV zar_^AETM6|+!u+tur(mGdv{BrNh#;`>h9z3K{A?ZNl(#Y_OC)KpQk*1?Y~vJSZY%G zvdAy0-)zVu3jX<~4I1P+jIeObGz7ugH*=Y|Ds5lFf@~{A&q}8{Zm_3sr@PwcS3WqJ zC+W;2+3Pyi4oJ)+$W<%SCXSaUJ)-gwesSAo&_lV0h|prmJ=9>uaCdR89wO=W`NNrm za9bla(C_0#T#Xil4#c%P+E^-9%mSdKZeYoM+%J9|a~U(ps(yzw1E)kPc!GzsKIPwa_!LD)pc^~Zxqq5l3(asXWHpYA zmXhy{PY15}7aWBIarKka0I(ce-BqpNDJF8LVc}Z3<*gPT#eoeTXg1qFfT(g}Y)0@* z`Q_P#!@cMC&XMw!?|j#oZyiE^9vsb)#$yw-E5qr48YPIx!(*@fTR5E^|KR?qRQ(Y> z-ykn^Fh)=z#mk)R4--5_@Rc0N$-y5Acp=mSv+wXZVbhUg0!ghVbDiA|#u2uE56MP14 zMzgKWZ5ed{xOP&x)u^*xlV!}bqA5|U2+-_87t zVLMBJKSA_lp*1`xcw6SnJ)>7heD9TMr|(xr=?yNfKxhw9;(9PS_&k&R**zGXFIx14*#Gan`3wMr@w?r7z6kw1 z>XZf?Us(YM4cxN*zMrnw$kW7uFc64uh>)v@V8F2DP}{!iD+GGgy_MW@?{Y@-Fi*gYaup08KaTOyEYuGxe%1Mh#sFt4G`W?JQ&zl3U3{ zLx5J3zS~XR*8d=)2C$&9jVB#s)79VI4m`#dz*CWFarIq}4gEce>ofP8k1$!zC6V20 z0G#w_KNNOngd74s#Bf*(b}1-B-T08K0geEQHtM)OXM=;ONpn9t+@V^jkj%>);4e%A za0f4j0tRLF&7nQq{@e0u7aN+gmJ9IA5|qa;GVWF6B2XchPGz(KhcA7QR|_a+n(~4^pH1L5Dbg{{uL; z)P!X(7qx~I@|*}r4}`^75kV3q?E)PQbKLy{*NPsQC5L#Zi`FU|I|R17>uhP;gLk!l zP+>su(TMVA2J0iA*0Ux1f(N}pzpJ3$cv3Oqx11}=L+XvJw#%epa?GyjQ>ea@vXOBb z$A%Pt@ftw|aipQV@NtMnWQ0dYry z%dfu1kxSsjbm5rz3H-0`cc_nr*m0mlO9A%=0;;eH+Zcwu;LhJ9vevy8&Q)C-MxFfi z4D>!<8#;JV7l@#Zj4ORiyyJF2uvR~2+edTBefmX5GtCY^NcuK)zrjU4*4ibGb!hu$ zif<|cl*#ZX0%P0?>4%1|S7|q0)j`eSV{x}Hsc6Bi zBarG~n&>!VA`gH!{Q|lgN|*^)kpKAId5ixhqOQWxI=Qk^H*gtBi-SslmAc|djN8gu zwA@0+^&>2$lD>Bbu!_Gg*3NiXjik#T&(Gfjf-MJ0R-%X#0s+q0B`gal?OxWA%b%}$ zdadU8f6H3(p?!N1!|;DSPM%>f((~_nyVT9QbU*P1{ox3BWK*1TZhEucQD^csHoS)$ zwIX{oxsnwuFXQ4|r)A%L9L}5zklbb&ZKJd3ufNMi{}IUcf?w^y`-}iMSbdip9-Q+g z?UzX1^}hY+qiq0>8p){BA%>2z5&ZIj^#JgckBU6H1=d}zK$yrl>?kVZUbP3`S8I}N zJlEWrp^-%!@A2G}5OR+SAhoH!a?xH)LoX+h^v2#$-HrcXEl&I-=thD zlC<$5teR)T;Z)zZOJw=lTQrh!Mua!)WU;Qg z6W(uN2)*A?2^=TjUDQ#=4T_m>m<1X@+2T#}cN2Z*C1%e7Ra_0)i^piKz&#UX=9MmJ zrrwykbv?;*7Y_HLPVRrF)X+|l7wtPdA$RZ>bwcI3o)v$W(%_Z~nMv=~Ik#81G|t}M z{Vt2)oq$l&A915qZ)p zZ)*bHPc`Wlpyv4&?z*bxDk}&0^DYTccKFh{F1dc#K6qc2!g90Z#lArYu1Gi!vFOMm z<`=xQ2LiddaF!vUk2C%1f0sJbEirNoAu9L0IO6j$B93-L1*}~D`|V7!d#2Mm3eFSW zk4|&LG*q=G;4xqC^!yu54WDy;3!F6;d!Ibtczu)y{8Rqf){q)ixM6W*_R4j`h|BV< zu`y<5sD8jlo_C{&UhSs7@B&-p#$xrGhBh9^CCZe^`ux*#Db4Gu07o71f zLh~AFNY(Ch@U2(MN1`HV#p@O|?H17I86e*(3_o&h7632FHbRp}5Fcw$IrR_$>7c7a zH^-|NjjO>upZ5Toa+P;~oyByZM5Qb2Vi2;uBLnN4jYs`iyFR6-&bR0`WJYHEO0mp8 z%kY7(rE{ut7cFA9Dw?tEXTdain-;|)49ncQ*^+fQ%P4e0b|5msC z=JHa%U0%O%JljyuSRxNa?G-fH?gb^?lBc+y1vugzGeq5^8N?(XUQW@kt{ACqe?b~F z$(}@|ZQy6M0lt%IC~)JmSaT&0!p_^tb$8{M`2wx^mqCuf*Sn-{bN+Jzmw^&A!8bph z%*l|2D-v7v*Fft~e^+#Wqd$I7NwBK5>Xq=y-}A2CtAxdyJ1lJ<8PL359l)Rd)uKw} z2jN5J+(W#q0Yvt4Br05M1N`+@lYOZ3bknB$q=N4-J6AnA;uoGY#nXW8#VBNRIQ#Vu8u@NQ^T3O<1 z*5G(xZ1dj8x%&gkYDfWM^4$-4DB}-oP_~g}!QZ6-AE=e{+2Al+_`tE10BJ&y!ucxR zkeQE8tlN@lJ5B3mqLyxs$Q5U|D8UqTkYw zB(deKJDiCbmRcQu7pOM~43tL0`BEBUh_hr9ZdU}4d zA38HdYmja&IRgb_>Ko>#`_;JypehL20#mTRM}90wR?!0ag% z3f>wN0uQjodL9J;5C+ietJM9!lX;U8E0<-a810Or#Vw*2wTEzk664$*$IOgc2>O;% z0o?-1p6!7fUa!$~g6PLRoM^!9>Asn@vc}&!P5WI{71J2pR<@3zLffv(}n6Fv; z-xgZ`*Zl#WkgX|P=>L9iPW|@-$ii_c$@PC9BCIpvO8?s-aA9GYiTFQCT^XeI!?6te znXKevm1Df|VEdarDPT{4aD?VNpQ9UN(5qp$z^^4SlPxB28 zT$jCM?#r4pWD))M1*vdartClS$rYO#RZuuu=r5EQldye`4`c?@VcF+UGU&`cio{b+yl?Xq7 z<0t()0m~9w-JP?)eSE>-IodvSdo{9~b&ncq=&K@FcX)6!o(5wGMBc4d!#%^nhqRO~ z>qd8dDZR^aDJTVXr`J;olV8Cmt&0pqn8_om*d@P`DZOyYUxopfu)z|y8_+6~f; z8*W6;LBkV+5P!LPPiQ&tz=J=e2-^La$zu6(B|)dH5MTx2wQmq{XH&Sf9_4i4olfF$)SHxh4$2CRxC7 z2BHjw-nG4wfTyW5xT)Z2yxiWG*uY)fxCl!Fusl*QZaj<(;4!ccOBGJ9>{q^NR!i2q zL53wvFsvnN2}Cy2nwlY9RE_1Ww7kJS}=YKX^W_b}Fn| zI;AaJ3)b%iG$-M*DKUcxTw~sP439*d%++&XXye6=ex;o=CQywM>0v*^rwkDV_A*FV-aKE(B{tBQiX zkLr(-@VZZ-1&ZH0v7P@G1i3ojV9ymbvm)L5&MO1>LMMZ^WY!QVPEy=#KLWO>EE=X* z%?BO(zK~26HBJX7WyM4T;Z(6hcRuV!jcEn!Se!B$mf11_BNeKo0M3OR{s=5q7ck$+ zf*$iU8yv=3Tno$lG2y*Imk!RSLVI)6tEJa+?nfAS=jYsXy-nhq%Bu4Ps4r^JMj{G* zCT#BVOV7@?-=-4YluUNi*7kfc6?Q?@25z;F++3oV)h~gvk61`IO1&OxUH-~@-y(PZ ze#TScz}Kzw^-(>C&sq?kf|=J4&a6?J{&&&aJ^^Rq^yS6QoXe=p4a1>y-hIloeYa+mzJ&4x0ae$ABw z_QNWpKbAlo6RYb#=Vy7VBh7RquKoYgYY)4#M1jyU-{hvedOBf0yJNgTqry$HBYXn> zAja$TwzMLeGnEAXtG|_Ga5qa&A8u`-Dj=QcQW(w?oWIFlm;3c` z8`wee5Ze$*OOlW%{o%79@b9*dLd(LvQtPQo@n?f~b40=1BP$~S>$-9Nth2Z4x80sy z`PF2Jco41gh0jq81Zw@Bwk}$Tlk|qSLX?Xnt^2XMND~9y4q$V>m2HM?5$5ZY*&O`` zij(c_G1bUOX@Ag7@>#ZpHZssjx1BOKR1YSLaQW|9^9aZ+Ix9L&HW$a#=A?u9Mz9$< z6^SaWzkp{E=5@=ey*}czrT?|28#oXJ^?Mz^YuQGfA^1Wh{c`Aup2R%q)3h%S=phHw z7k9xJEOGv}qp!fQ!&bjo=;nw>M~>ck8rAPkh?--!(czR7p2W35aP$bgfMg@&F+!M1 zBs{SaBlsRSsoC1*wD}~6h8mF|w^4VoJE5oWn84Z`>j^>^{-Q~`I!fznajEm1$)M}XR|Wg0$q`M2(jOnX{$o?W+DIx>KaLR2Zd?C*g;pQ9xc9@z zaJzTJZ)jj!$_LSB{lFgfk=-qkB7WClN*t|-Etf7F{ zSBnuuVRI;rCoyWiv^t(MA7}I%14xp<$F0^2OGZ)L;~70XUVq2kQk2Z~#;`RVN3&7d z_9&n=E)dyRP@$$oJM}eA?%gKxTUbz1>nWIjKi0LB&};6at9s`W2>WC|Zm+O1XH363 zMZE2MMck#84x5GJQ{LQU!$w6>M;;NDE(Ws&oV_vf>#xTqjICn6GZqd}=VNc&n{m34 ztz9_IN&3h%Xm6J5hI-H)Zm#!9yDf;qEJ*=2RqqXcGJhk4NE7ptkn&2v)m64s?k>00P#x}kl(n}5jMz{88 z)$aKp{-VXSNu6V-DHB*@*%K=PMf35OEB()47##mPucL|ZdiLC$-)y4@8kjLSw_IkTGte!30a6epmuc7ck$DK+r|(kr-KMhVCRsh2~{ z?{wnk1QB|~=(JyBbHGGa7%nxB*HG7d%VYENdKYi2tUN?Di!bEM@vDqUX0w6ln{7n#$)lVD2$g%TDcdHwNv0@qeFj*Rhq;KW??PU+vf0Y2e!M1A6)@jjA7% z+E9QO*lPz^=$*P_8R?VkiD|k)Z5dtM*0J{7^hVA_=A4-7y^|d`aO9!QMgTa$M^y-Y5dZ84KY;H z30M++&AE)I+G#|F+Jj=^U8JA$)4m__Ibp=9vGPACh-~ zMbCOw9e7vHM#cjBpqC^oBwiW0e*f;q*|}D+kAikhR;a?C5-8`|lQ>34$#&6HQxph~ zYP`+hHb1Ht`e;vR`4xvr=mp*~4KmrJQ4!W$AI*?F)zrK}3^|ba)5PFc55QgsXnGyC zb959HX+79F%Wz(#wo!wK|AsF<_6HoQT@VlMWhdEn--bIPSye;W*lT%`{19aV`Ls78 z|IrhNrXvelW3`#ll~>WDA&Oqh_h2-#IDo%zqjb<~GSq(Dve)jz&0*ZgSB%grVVaU} ziG00r?1Lmrxw)d{%`E!O=RO-P*JF7t>G!IihG3C71Tz z7D~ok9bQASim@}YSA?5|qC|!rT3*ro`h#PxAKh>)v0V!icH9aS#t64s?{r1^?#tN* zNBfej_X{wM5-O?^Y~J5twGV%TRbqx#4*f7VA_;$2ji0$jd!K+Io+)05QLru`)(3(4 zI!*@EC#R$Q>U~8A&$XZ{Mbsre`z@9$v8WwJT9HCtnKp`eWmjLjLfr!K-vYL6=M@LE z$%lFBZ=Qlj9-JV^q>dbc*f9T65qN9t>lrQ(iTHRV;fs5|oR$6iIr6v)71V30INGD^ zUfzJ6?S3wrpzt5Pd@sSM(l$;oGTXFog`uWmK;(O+LGbwm`3~OBfXGPgbu~K{Bl+AN z^yJ)|?%YGSRzK@r=zZTQ?jOvuoTD1c;~H_#h4FrG>H8x|B!SKKasXNFaSpJ_1A^D( zkPj`bvhz*GU&O6zCYi5GEZhJaz3;z5%5Buur@lyYF@rv05{C*k_pdeA*SA3|Y^r>iL;XLr&vFWpe5 zuEs>Y@`Sg=N$&I8xEg#*#D5J>d9MZQc5eMZemBPxBhA^TVBC1kw!&t;mpM<yi077-U4`g-pSb4evffNy^cB6nB^Z6lB+URd_jt zzSV|TV9D?e0uKv7$*P}tU@EUQSI&Hu=S^K1UYOf{oAAZ(k7#B++G!T;<&6s;O9J6hzZt`L48klc-Mw6>u#Y zG+0fdI8?su{T)#)R?W(3@P^Dk{yYBM`k$oQvaK{b(W@9w|NpI9uRq>&{rwlw8DZ_C zVEsp^MB#@}KwBErmzFl*1+Il6ci=DBw9v;`VQchu%ja>HzW5)N@X!Ipy{zehfD0P+ z7<(ecz7fa>fQ_MITfMicb?b8QPS)niI=#9yv_0Ex)Q(WyAO%2ggK)IZ6}K2o9YJaJ zewwL~37%b#4gbr>6?zXCM(y$bK8!njZO#eW$*6t1y6d4&wn}BqpLm)EyeL2n@j_pf zAbYbF>kb-fmiZ(s4-_KW6+*!$`9O^&h%<;I+XwoK`^dUCl{~CvgL;2iuYl7}vR|`> z#2)%m_v)}*5R9WO$Tu#8>W$5&CBUJRI`}C!+5jL`fp^?=IQ#fHrDHjT?+JDYAl^w4f48XevAzpyxyLt53-?nNS7zYFKYR zYS7@~V~2v~^OtJfZ@L2=H9Jtp$o8K7`ua9%sbs}09}tMZ9SdAf$Hh|ybkede{|ga$ z*ghfjPi|6NH?&)(?KuW|G*sW|vuT&xH%qGWol-INaKofhRLx=)H%x@KwB-099#m+w z%$hY6e67Qz{)6e}wEnVVm7F1-dCBUN?kaIevM?xB@FzE#Gf&ipH>}ZWM|ag}y)%;4 zLb||tGq8uzhkzVuVRA^1_Ua(5kjQnHY!uQ<%U5m_&O(;UH4{Q{_zkE#zipi7R~i&+ z6J6V&OUiH?-YfK<%(T|2TL^#0+t@0z(4t`5eR7gnpN7?yTGg|dy9e{-?q4{oU!KRc z08S~|{Xe9_1tK;YddFeN&)o6HZ$sXVn42Wu0=0S*hz6vL(Ol&XrL9hZRU1$IFu?#K z>(9z&j@lT5_!1Ye7f zhG)yk0K4F^w5Jwx-o2W#er4Ptv6uuO+*OQ2oadDzTJzVjz^?YnGw*rM-|;t2hAzQk zJj+RpAEYM;pkqF^{|8m)9nRMO_kUZ}Dq5m;V}8uodsMC3)mGJPt!j1H?JY`Ul*Zn~-o%I%>vw$b`@XL4??3)a84MON4=!IvoVNeY#OLZvA471o;^jz z_SnLHcZZl;j<)t&xO_@-yR*9(3j4>ntlgEat3hL*glq_mb)2a|jZLV`>06R{{UfEi zfASaKYZh`?6ik9#E08`Eoy_sK0OU8p?=GWMlh045+_hhwftMd0Z|G+ydkw~Ri?yzf zJS^V*ZsaPmZ|Gn8pM_Pw^W(X1|Bj!p8*8ZFN<<{KKRdi zct8@(t~9cxx)QT!$z;u>tId;S#T4pqF26W+KYHM$4SCD1$7;_0M0IJ9U;7#M*T|C1 z1gJ=ae@hx;-NpRQFGeZiJx#%CxfWkP#jYcx`@V;#-5w^L{I=Db@74A{o$e!>s?`6S zQw1eR-5PlL(KXC2cDh2h`s{4qdDrhO9=qqAY#IQO9tr`R*?2Sv@f7r zAI}M)*Bm(8<}mT4*vtM%)!_qXySiVIdylT%!7&Oq!WaMfR(cl$x{ck9kULUtAyPv` z?I1nn%t>rf_qdrE$DUHwB zKZu`{Ze~)wnLr%XVn!-DILj6^F~X!G!v%IGm;l%)K^k~st^N05mX@_IUL+C29H&{E z-R3HZAL=PQWKQ=aiFJu0TwCTLzrJ-f0!A%4<u4^CzB)B=R8{$47qEck|VNVjI8>108lhq5z)7}GK0RLyt5%$cZ;Uk@uEjYhB2 ztQ7iu3VSxe-uWFl0)gvu2vLJ2GluLd?4Oxm;gj=hVQ_;Dlt;xe$Hg#n@Aev~nds@D zke9Lg9S;ZcSGPNli5SlR5m(o388p@QG7V8QTrDX6d#K;@6cElRsKy;cUrWAZ z)_tv-qy8Z%Sxa?8v5j~3b;xMP9BB0k>)^2PKs8+97(|KG zw5JGV+-KHlU6NDRZQzsFvORPrVzQ^6cq4F)n}+AcsS=)5(Jpf{Wsw);{pEzt;o892 z{q*|}daAl>50_kt#{k%Sl-FkXaDBxq^U+qSAQy82*!TS^Sqpx119B)d^VR1cM*}gRm zOr28dWtUb~ZrP;>6`v%7nDrAWBjVqa`E+sHZrZzaCL2>V!8u$Nk8BG&)2`O5{q&7w zz1KV_a_p~1`Ul=$QP&NSN&7T?e#mg1U4)Pr@zO7il-dL*qk9m5^5}jpk+G0s2 z7+M%33SQ%JFn(>k^c1>id5r9OC zZ-?zg$IEz6-iIXMp1;s9(=>cb|2(6i>Y_7Lk~Qt_Jii%4wdCZ9D+5(5-{~~Tz=2{x zsv`-FgCa|euDue2>La?v=GPiDiN_Mm6Y_2H($cvDB(_&|>-K%r{w?2O^JV^jLHwJU zr#(YxOb~E_i?;by`Hx>Wf3{mbCjvW4(LFq-wmo^d)c&mcx;op%Kpop@Rb^*3p0%S_1MUXZKzPK8JlK{G9vNI1j$;_dl=v8GURy>v1q_Cd&{k6ASV2!2!xL z7F@yav#i>vrf!{y?(5v+oI|tT%?rj03XHE{THCa7zNh++|#a> zvsh|yeud<%#Yi5;AMKi9FA_8$2a;+p)5(W-d-ydY4EBO@B7)Fo*Rou1wx?@Zw5BaQ zwSEYzSddatJz3O%+bME=ZZncTkjrlq!?_RBJ;SiC0FDMfljo zowIs{nb1_vPCQtSQpF9E&-~8X@4tiIds>eCbWRkDr>v87pC+Cs|6*OsDHR%XM5WsW zrY1*ChEa0fZX$lu+qf%YxR*TioM=4%a|^Kn$pcI437lhw8q0RrF=Z|>W&<2YGK_?2 z%%_PxlY$yMgD}f!vu_oQ`PFhst+~N&YQ8+Q`|4P)?XcCSEJySz3^}o1h25T8X$Sg7 z>o^X@eWYCa@srk;@XeJ42(^`YAS-iNxs86|=A?%vmp~p@FsoiOW6|AN@ zix?TzO5oVfv)w4^9SaB#+RHcWr*&)-$0^Je8!`x`)zHP^I7q32a)FA8Go(vf0se9W zBS#eQChaK(9cgvA+C$uAQcN%opf%CMf#KxA1EIvFVUj)f#J6+5%QfZPZekOLj&z$_ zRDbwZe(Al`UOB=~q4+Oym^x%KU$Q*fXI<`+5l``s$g;{4#@BD)zH#3Q(|vO{N*w#V zHL;}TI}T+o=SF0`k=jVzol=%)%XuIaipuc$ogYXL_ID`p-hKkBdh&*OebB{GKz~j| zrQ^>>0lEi)qZlA_&@dsV=8Mfb8B9=9iOx7mR`Uv<-y&eUZ5DKK<5og%mCQwDNL<+~ zV`#h@=X$nBau^L%T3DcdldVZWVDUk0(sx%&mHsr6&XlCswZ+2UOiXsmY_Pq8VYNA0 zHDt8&XE5nS{{A4)Gc@bjDj*;W8|_5?CkJE7bAS;^b%ma)V!*kfJ3qnZYdM~qynmS? zYBHU0pc2xUpCZQMQT_pyWW_e&hId2)4I1<_(fHO1@i8%oy31oF+ErGy&av~S?2>itZV}7#F z)dG3Rhsx`q4rUysZ`Pic6Y1xoZtuS~mC7gv#f`7_zA6vAyJKQP?w0`?pI|E6> z2_Y`d)+@Dx_ac|-5ksPy7OP00v;pha7jrr(V;Y;1*Bz}lJc_I1YMQGng8w0sp7l`R zOu5*p+Q;K^_Qa8`D3!$yNGCfR?(C1>^E094ff359Bo4d2m2|ICd~BI6HR`xi&4yJm zF<3vf{*SHNjF4kW>zN7$zcf|Obv=uHEf^jGEbjj^TJTmwDDI}TJ&=fzo$;@SgSM=X zC+s{|NBTu`wBXsLewwK|GEPl-{C%1!N-W|RNDKy**8e?xN2x&}ecJNB#ge@u4qW5G zv*LiH)MwNTXbmUYi@OYoqHIa6HwK;Mo}LB{yq_qd1$nXgyOp*i_}(^1oCY zm%EfiZvSXE|FtO`6M=AVLTm>ehi3S<;-!2RlQCx_))vUbV)rJ=&@fLAKtvgB&(B)Z zr@8gg-{To)_?Th7t%Uo2nJ%WJZGS5y4R_YOdE9gs%4%$PD+7|*Y2RAM+Ilu)atbXIgVi}S;E#Y*?3oVsORMteRI`bv{8w|}|zER^DOf10SFSfi1xJZMzBId9?kto6+G zM_v0zweiM~^}R;}ox77lv_~`0nW{z{l^( z&=g^mZ~J-YYBB|E2AfRDl8$(2|6_ILsxc2M7ycn{;V?<7bu~PA=t6C%e+4cN|0ne9 zH>*gDyK{%XKp)%tes8o;l-Qg#P;T!52h>K19PT=;rsYS zaJ!u&GMoE5AcY>02>xMT+8EFN-7Y>G8&lrE#O#LWPNlFU~gE zT#`vK3O)=uW95&$S^uW>EXbYWG>wy~{4jo&5fDA!ZCJ1I%7&>5g*GH3RcGWcewj2# z2^=a2U|S~d*Z$R3MEDU79_bpF>>Qjz5I#AIi4`1l9-4=u|GAX%$LZ$qazA(Hp`j{g zg28OZOx<-2E6fHrCqy3=B!?a%Nn@%e0QI^`>=3hu6RrYVe-$F2rHmy_Js%NQ-s@1) zZuUsL)EAfWyw|RBJk1tt+T94}I&ZXR$(fOM5@;%$K78E%MoK~(1TeN{I2L4jm8|v=QqHjSO4=?NUxR z%V)PsK|VU3Ij(iOIFx%edw~3q3Ct;#ccOfQHCp|5mQqsvcX+J@Wq^Fqyt=M|2LJY- z3A$brS=G@Xb*7K!GYIVP0U$>PNm1-s{ALywF1~1 zuB91U<^~p{~z1m|6SkzUTEf%Ocr=K zAdjgzUF5ow=h*!%9|8nFb$HC$MRNE(^9F=PazOU}11Z>kvV?Nu@z50(m;+`l#-7=L z1`0h#Eu6{I`7Z%4fm-J*>-BvqJ@?0YQuzzi#W2Imze+*p&q0JDuIVa9R(J<6kMy6P zbUJ_VAL>*++N+CH!UBo?`+deCUVh$yB&vZt zcp$m!3!ow*UJ!tMbPut*xpxxj2R+vk^TnMUO*(~w`u!d?t-A|Iz+=0&{ho>`3m3Gi z)OT@=`tq!F+<3g_UFh zO;iXw-x}z*%$Sw1x+I*5Ug9*?=_=V#5s+PiJ>c1mU3j?@@^{++MyrpMUU|Iq$FEQ2 zs&mn7Q^`VR3yddX^#-2T-qdw_Ju!>Wr39U#zzD0?)ZIbV@lwH4VRI8Z-_~_AuH)NR zes*$hsssYg!~1^MC^PZ8WaOZqkT?rY=Rj#!%LeGju$q3>Uy^_jbn`AhiGNY%*Z2D! z{9tt7&~a!jj4*qVS&UQ4H6Cw;ZOoEpv0NQIIR3-QdLRyQRhWMMmS z7KPu+8WfrR!G`EmKuTo{LB{d3E?xo%AAhbmd4)4e7-w+R4C~Ruq`j%75Sx46bO$k? zYnyj}A-roQcmjd}e%9}Wj_K2phjKGMW@{#i3j4Uw&=}&3GTG+$Uy%)h$Ohc*Y57Gynr}E+p;eV1t^KJ~)4gf0Tyu0;w;zxVv2B9g zeu?3C?pAPFM7k*TSSNZMf&p(E9~PLZ**9no0DIr*X#jfBe3b&gr*=S|_1(q8Q+a}p zijN-5PU^PGZ0ZgICnnQ{=;@{_Hz3lVm)r3p542m}=QeUGvpV>kSrWOL>jv-+`@vXVz^ zE(tSs(uuGqcR#O?&qe!>USpL>cYiNNhn*G!%qMTa7$SSE+d`Bp*8xJNGAE46v1P-!zJQBg+4|2Ms=R#ztSkahY5sh>)qs(g^yEHp zFFf!b&`Nr_b-5yU{Mrv29JeAKT3>YSBFF^7QYsZLPjEU-VqD-m`(L$#Da1`rL(Zls ze}7&AvLA2s$Lie0`~Xfqp;(XJme+paAs&Dsf&A@2<^d~We5mu@KMP2~0Bu+=jpA{ifqqoaZ(HmkHIx zAS7)q+h?%5nVhmdbb;xOG_`2e+k=+l=GN2S;vo~K4Tb+X?bfSx;f$S+BsPwy{mw1e zePiSRSWC`IiRFKr4{)x?`cvz^P$XMWLZ6BK%OgPL=WA=I1H|$F9sV$8+ZgA}Eq1;w z51n=mGqAnb7|RSko+&?r#!}UFJ_(hNm{8!+A#tr5(4Of88zD4+hvT_7@Fe=#_DMpL z>m3^3oaEf8_~Wzry|q91{L|ny*T5;mvG?rhC%v<4753=_CS*27R-X*-iEcdYxfk8! zzY(+oe(@PZmmx*+ewBQ%T>cGmo*Yu8(0ZwFOW2R+uH*Q`tXobtA00c-ghFhkKc%k} z9=mox@I>p2)jMoVAFvEHOL<(h(T7M_UW}LCTHMm03cP2m3Y)yktvBTrG_*w`?bij|`9d?Dnu7p8XcBN1;?bj#oB>}Ic2OR2C-N_7 z)DC*3uKy{)yQVrE9CK5;34cb`@3$I8g7pMGzrF7b7&mkRO)0jWoqP`Zc)jO{J8@^%T}N-Y#8(*krJT(Xc+-OUdo=-1$UW*2PXP z?D@K}xvy;>V|MUS{R`a5VL2vF>791g_y>G;z9EfAB)iwEUb6^&f=ckl+YNX5r5g&e zoNU&;!Yl9b3QPhBKqdFjS}w-!Nv91zLtl$H)E$TdPE*rZ zvEa)COF00#q}d_m5_EN@b}L#ZTEf}$kylw1&??|w=4~g|^0F8Jke*M((|`%2&gCPs zv>K_cST9hb%-pBoU%nRI(TieTEBo#dJ)#73{EW>2hw?`f;MN4{CHy2Wn$@{F6wrQr znhAE??MCk7ukU1TqKZJ+O!o8 zlgwg~FgeZc(0!$2c2}ZLyavd?2k97ozSXXIH}*n?-#O@N>e-{a&KF`acNWHfTkUYx zf|6M@>wd!IwIA-AQLM|;x8@YbN~YO#?mxBhCPKj>`T7n|+(}+mjYAh>+S9kQKEDyH zY${yN5{V12>%iR&QxV?m;2k*;Qd$2e5ZET^xq$ALMTSWNLk2C##PTUztw}!zQ7|Aw z_h}JkVGP{)ui*TCgSFURb`cY1*jKd*WBTvOAC5~r7UsIIHp>5)sl62W@#15w6t5JV zq;&0b(?e_NR>L?p&A23*mn zb(!U8tA(8Yv6U$XWYj)5w_62L6QT2QM)-q!s4H)bVc={)v<~ZXn+i#6*jTMjDz-aewgWWC<{bxHo6ACO z!Eoq|nMBA*I~STDNo71)ZTJ=t5FAv5cG{jI5t0`}##b-A+sIo#9z*pf(vZld2pz|y zRGfYWU{Sk6+{4%1T3RlW)rw5p?@<(AP8y~^*%)Pg9ac*Uq}SU@CW`@{h@ccnwpg*Q z@^9399)p|3)ZIC)qaNngBk$xUkjtXk$$GU-YG)Ieg|B6!@%!aB7EZa0m#y;7WzSZF z_{DSSiMpr3t(8La)855y23=kUB!D+P*>plpkLyMcY4cWiSM6uYd9&x&X&Wbr$Ggy+ znl|--LhA)!de|yxIFU4 zt|NG_5@S@(X0ckrmeH!*cu*D+{CfW$tuNm;<4F&U^iisNa~_*}U5f%B5f8lbpYq%X z9D0BA=y5#=kyw?Zx*s0~vVh8_SdR0|hV@~yXo+ngSr9^+v z({fV}^m8`P=iDdpL@mP{r1huNl72sbKDplaYOB7gy!I^2@w<+%Lw>KkurECP1pDe+ zB&#y4~Kq2n{EvnS<8OlnL*g%b4TPpk-`6phG*ONwNxTcG&cS~H*Lam5nv zuc}>Xfh0v9WNWy@s{p>4Uq23C^DO`44~INV&{BZuOIjF!JAqR9*TLq|l=HN2?U0d@ zp|wM3aDy}OtK~9*5X%x)Id>~~Wb zH(alO=j1vQ8`qGG7rIkoZjL+C_U7PuT z^Xj(z&A7_Bf(0Lx`8O;1fitML8aCzbaiW%h70Z1Pr;+Mu@`|BHfOjQ|gc_ax6|?G? zqSU%=H4Ws!+P`owt^P@|wz5AAfEZL4hzPN>e3HubDBjgvYx=oGl6Yw^`S>RiFC| zXxXZ!5o&7QhR0_o^qgj3T<>N*4+6SAmQJO`egIxNvdcT))^7K5T2euVY8udWEV2Rf z!U4b4RzMSwUy5K{F}3e>zmw#2nl+cXD+cHmJxg5g zda43`-AGrr0?&ev9(yEfge3tr*2ccxKf6xH5SV*KH-7?BTI5rjrF(r>=g*t7q}G0OI%f$RS~gj0}ED^6Cs zs6(U~F`GPPtA)Crs5QHYG$u+BHd_}s~bKb+HVv;pwGUxGi`n=4nqA6?-epAk86Q#4=OefsPGU(_G5 zwj3Cjgnn>;2|!NfZ?fy|KlW33Ud!vEd2Le(xDsZ``1{X5<;AfYH$iCDM(7*w8%pJG z!oFqJRl)mhFLLh_=+J^}EFkA@+6=5@#6i%LkO1_Q$_{`Q$o(#OrYK{E^>UlM4Q{ifHudtxMHGu^LwyUsMMn z0vF^7zfU}YhE@A{k8aLVTwJtR=4~wL!wPYYrSywB)vqdEhXf*ZGMCLwFD7CYbAkQE zWdX7B)&gebH@L_1%LcW{Pb>=smbkfsixpYC4Ep5~4LMZ`XGa+R2@}_L%?(q`r!Ud} zNmG8}%6v zf{90tfx2rgY4D_!{eI0IMWC&<^#E{YSf{}PJ)RubKwYfNo+CY1ld$c6Sui+QH()rh zX#0&Ew5pzR-4bp|;8{d?@k2|RlfJ{!A){96fzqSMx4%BB^mp1ivZO^iAusLEJ)id- zw~Q5iH}3A!CvsU&i1B!ITt!R)Q7qp=%kN8IO;sJA3iyOE7{ksnT@k>f013*UR9%>A z27iJ5h)+0bcS=H~wHRI9U)?p7TYKMvKE^-3ykQq_2ZzCN>rE{NO0Qtbym)5%$(laX zg?Sxpg8H?Uiqlssr`lXGjIzPdeM2K>@aSXbG1~T=j)sQJV<|g0J*WUI2K$;*s)POX z&6PkBV8r5z9&MeI(`!u&uCIqEDB1iu=R502Ecj@y0dq~}@y4!E4_}o+N17+sQz%-N zsHX=*`xWdz%D%MO;#jN*z@GzPMrw{B77!`mO-@}CKj3(a;FQ0NNB-Zd%KWoUsa};9 zD$OoD*Q{?`lgut5@6}iTK}`a%3qFd&E?@zV$qV5hoV7cHWsO0I2d~a@R)MnZZ)J!- z22d@K1n8|`atOm{!_8&cRrTqwq_z|oz!1*M|Ad)?qI0Zy$5)Cy!TwiA{L7c&2Cv5~ zoM)bmXG+|P0NaDC42E^~1tSLdqd6jYklHign`a4&sn;8IRzwW59)h(#N|QO5`H~rZ z=j85ZgU775Sn&pB3BPH=i>MqE(J~Se{by<=B(~SdP2Yhu^!!BfS5!e3pXpcXrIb)C z#&?wbL9(oZq6^oalMW9wnA$6XLc5ln#Y%!km4+NecTu6_f$%*sD9@n}Ueo+Zhg9%%w`fv6p|Qu=I`xhG}9n_Yrbdm%;X~cIuF}$`W3IFd}kPm@;T@D_Qc0L=1i- zmeTIV3%v6Ku1`eZa==;L{+8^<%Pz>#+(0EjWE1G=<9=gLf$a61#TV6fJV*&@IbU|m z?2|pSD(FOM^Ens&4)4~fuJ71!-JqxL{Xsg&+E-|?{IYXG>Z`m zAk8SMA5}#6Yi9#24*=#UADkc8)fco4yzpT_1eqmzVs1rvW19m9?HmqhJX+SkpAj|1{#$)niStUGl`Oa7G*THbvT?4&(maXP;#P~0^!_6K zuX|-*jY__F_%J}8PHE z_$51070>y^+5;S!wy30YlXDPBX@Mu>l|`-X$uE{HU}7V)fjK_~{yJxn2x|oJqS}r! zV{EtGr;?{g{3p7B=<>v9rr*@Yma67d%r|?Ub@u@mz+)#6#djlIw|7UJyl2;SMZ9!k zSxEnun4c;<4cA}{1}19=zlZ`y@2Br>PBPn3lu%d9!{YE->NdFbyY1}O$KR#*g95nO zxb*|S977AljouSUCW!`j$&iEl(pvC^Co%BMlQgk@cZ^CatFO4Ro0-CH5tp=hFUNY* zN~s=W$51M9SdWZw2e(*g0S7Hb5D)i_d~op7BITW%>ZlU}AlVRR{(yhXiZ8s*D8hr8 z1Mrmae_QXd%o*OzKRiPP6&Svj!>Cfea`r8;*27DF`)IIdtesB{rNIw3Qki48=;&6i22GX$+T3zA{tZ#)snB zbPFz7JyNnwLAFboSXR^pq*u~VZ&kv?P6NR!NQ;{C~L2~ zOC)r;nqR7H9&>zBy`$yO)&B>jAG&+vhPs0td!kN{m8~e(BFExA()uA`y!`@^P_jlw z49jTj_COp)M5P{=JwFlmr;PNzf;nr@B?Wq>5Ck-4nDE+T(#!s+N4 z6iXNg!JlqCB{)>#TF$gjhwrF|HI0Z`Y$$rLnG|GWpQ3XtnSoF%GeEE<6t)ZwQO1%Xo4EDTZWj}YgDHgu@;l!l_%xZHkSvkl%`!7zH=1@*lHm&RE;G)_SMSOYo)bB5zO9fh zm9uYrY`LcnnK9+u^w@sbyoPCT8B*B8v-st|sa!A={^OFTza%71(AFL_S?wvNTT1~l zoZkMk$^o~n8blcM z)4AQ;lgn|boJ7JX=*t+{|G}#4qQa$!bkMaG^o(3KsLFr0sI|deENTQ6`&`LG(!R44 zm|wUuFqx5Lkjf=x-{StjJIQB_G;{qj_f@Si`A=ISPe;Mv((hlhe>*-h3CIGqoSzh+ z^PEt`5K_2&g5qYdKAi*uAwg< z=Slx{jj#Rj67-K_9~>e9==*_DeV=WIN$0JE+%)WkpF4ja-L50$_e(iF%)$R?uo&LK5YzqRhHhIg*&h^~qgb0ocCMspHmVmZc?Ln?Y8cj8boE6aa zabriB4w{D3tgtxpl!{b!jyg9v!aZUFh%fuXQqEP)Y0LG|6SRI+h>3^zZ9VmT3~6VB zQ{IC12r-dB7s3)LUTdo&)mI+-*+Uk1AqF?DGr-cBcr)mUKEMn2rjVa5`14=s-}4(OI@Nm4rHhU+31)X^n6Q98^&yaiw87Df97fwu$N$gv}h ztt8swAzv}fW%6>a#RXt#L9a94gcWil4?BR_~ zo@Py#@@#O!?p0 zp>rB4%^Eblgs1y*evrqzh0D8~&3GR&)}ls-4vK_O?~cq}>eRz+Mca+M?e zP*5v(ZQ|GsP1cxhZsO;MdZQb^p0(+IH(C$#`^Fjegyvup;SZO)5SI8#W(<-gelc6k zW=t2`@WdRZLt`z}sc)Cxh%3VKaXfo=w3B0!CR7|8AJ7Ww zHzmB{NGsra-0G$fOLv-KUchxW3lNv$Tf5Mo2OAl>cs-h4=lgVze-|f1v2E@ej8$Oon5*o$4x0CWWT$qMh^H8= z*+cpkbLj;ac!=VPwa=Gkju(jM;R$-8Z>vHkdXlA7WkQHu zE_r8Cg?m*}(-lNcZmEd+V3F)^f~xxLh7H?4a(ZXl634pSoC$&MBK-nO=?=;}HO-&X zs&Ju9!Dcmb#qvDMXw6uN-elrs3MEO;B@y(iTK~?f!1czMh6;YT_`5O>c^r7HVMW-i zjjbq4scrky%=OM529ApFydsCuj$S!{HfBmAOetFoo*6tcB%$50au8k855IwOijf&z zV^wW%@OrT-vq0>`039~PX1m`)vUH`i-o#;74CkYl2>Ns@Z9cV zEcC)3gcAXNg67!;jpTtakV~~Mn9Qrq(fGw6h`6GDvFm*%4G`1##b&zTf0sZ8G@{HE zhzvv?&HKW(JpD@2Ad6?KD#)m|zv<4gDA2FDzWC0$)n|2NEv~v<*`+iFXa()t+T3si z7~T(PXxO!%7KEm0-+e#0D9o~ih*THP)tFTRM68W~Q9(pvHq{Vn%KqE;Td)M3sV5svogQubAQ0f*Z(mT*JBlk#F%V~O;9E(O!xB{s`DD}WF{ z>2n&v&L`{&LUvQ~4mR`8_P}t|MJ?$AODHip5Tt)`jLRp0Ak; zl$A`M0OscX=>K#~vsj-7USjTNb)c(naiWhb(WLnc8|I>~#^d*FfTJ=~O@9$cadee} zX0VfIG)<_ZE4e@Bebl-8(t-VNLJ<6hZ&rkv#N=sMzB|k0Y$-T7eg-bKI zurIP(NTsrrVQnVK*vOK8E-dUjc?%sac=1cL7;}XBQg*%|lL=(Zsh2E03s-nN4}^=& z=np}@p>7`Hbla8h|G|1Vb5^FPY+NWO8QNiYtx}4W zA3h4$rwAhU>yf!-FNbVErK$3&uvuann72rVUmZscG)dPSZ0PN}0frr#&67KDqd{%L zha;n!p}_470e#M}be;EnLc;ckKtif-+FZt=B!&(}f-!ew0x0COclA&Pxmp1pj-jcC zB^**O(@-qg?!GJH1GQhhFTUZrzaI|1){x>gR6ugq8xzl$w%WJ$%OJDQs&d-2-laH|qIt*Y!ljqDCS{ z-Si2Lqdf@;oRZda-|$btZ-Nc?-N$NM#wbV>w-m}na={set4v=sH)7dc z-~Ho8ZP6N~x?~y#?{#l{z7&-C zC||DVl+cCDUuf|b;MD#`YmVm)E3YU^R*54^cBg8x>?Fki1E!u0@#+t0zd0I;=?Y4W z@y$n;`!^8(oP`F(vzXkDgFli6WGm}LQufA2tX&{lT}f4vmWN7~S;$7S={=e6A(N^l zy-^j%YS^A zD`~*^IxTE0;Xc0`g{qREs2A-ois0z4z{o%_))+!8>msCaGwFNE)4M}b+RwV4%a3no zH*T*yk^JZ|EHxmNAj66eZDx{wMjld9*wI7rda= zA&Bjh6Xl5{|K$0LHrLPgOSX1loijGP6Z(h(eEDM0fRkzGGG_fn2E zOVm1bxd{NlV*9VZqNcHAa6~r59^LWzJ;W@d&#~`tx|&~Y$$d&WR&RpHVQH)OtiPAM z+E95=pp&uaMXzT%kaGhCv2~9GNyS|5o7XC&t&k@Yh1Vg9CuzcY;qqbP=Ok%EQi8ZK zO+-xLPLkynH@a%AWXVQ|tiGPxl%Ne#_FE+1X9^kR& z0o0(4vsdjr4Mjmzk}k)w5yQr>0;y7$J`B;clbBd25u40)s;I%qu<6*>SCfL_^6de4 z&kKvM;>9f__V+{j(+b-Z+T;n*;uVV}1sJWBzb-_7102*xkRAR8smCjrOH4jp7=<#? zFw;b$^Sd#^%R?8pBnrp96zeHDPDpC8|F;ku!t(FR0Hkrp67MQ zFh4-w#9(asvVi>WauF4Z$b+N8>oZeELEBRWe3Ei(SJL$Jb`R=X|7zObx!j%LRFH+t zR?pt~p1_&{9FIML-%Hbm;M-j?oX(T+1Rn>!T*JvG@f6}X{G@3loB`(w(v1VdigbAF zKKfHld=pmG4DAw%S#v0)U(gT|&23cdRt?*;YH2cj!~%7T=gELLks9E=9kO&d5~=d* z0uoz)l47IG6AT45AjG0skSa$%vd(#3Y`}9Qw6O6cpA;Q(K#WUP3KLh%*hZbm0ZB5z zF0FP3|8=`ki}#ks&g2agnJwNZ^V7d^s~Wt0*HiFijfFw)ZDWE@tTkx@CCnvlTg4Q+ zO?+|kBc!-|sb}i>XXa^u}CBN@JQRYs80vl?5_lhSv4n-GE+7BDGnbS2_LK7#}OCm+9q_4e=#%4xK*)Bt2&g`9l>t@-cO6g3(WLo5pBQkwkiyW z`n*J%OS>$yI@$VZ&`Oddr$cmieq2eN&eEf znYHraEu#zJoQhXEAxS?CEl&nJ2aJ&c$f0$S@;&{T#KK%m@tY>(L(bp!LxYi@oQKFK zUZ?Hr-|B7_ll_q{buyZcVv4C9VKP1kqbi%EOahB72ZvS%6x?D(1}$0FlX^9Gk;@Mp zzUAoMGlyku)+MpL&9rN%V!|H<7v;OJXW0K+V7xLB2Z%1!G>Z=zd~~@ok!}a1{~7!O zk?K1X7u&$FHtGfH{zRD#^|=)k)XVA$N`_93FL(FHqrx?Y=tiew-Wg19Y5SII*B?09 z@I@nlacIryc&92e1;R&2PS5Uk&rBkMk_Ni83uGj#G4ItYW&MnpCJ;;Vad>vjH|xDl zTQQ%bw&;g^olN;-_mIo`m)@n{&8Zp8iK&g1_5X5M)NZv~+uT;YP27TPTDI>C-3i(p z-u=tkZjF0|Y;kE@QNtLj8L=@P{)U5&MJGyM4D8U|3H}M`k08s{xGMr;g6cy4wKO4h z1O4Q<7z=n=9UY8*ugpo4p`>dZ5_{8cx`JFjAJGIFkNH#Ip5`g8K}j8~?T#fa@j8}O zr_fF4EQg>V@^o4|172i2Zz({;(hrU88#7pM_r@WN5!z4(0|-WMQ9j&r)^uqCi0|cU zqJkwSWG^qPXu}imME6$lvd&w&;sjJ8T#3doYQCGnhwB9ONF1Bz{fg*;@)+WeLn<0X0Y27L z;2b&^isO7ao#?@spPStqHTERuxnFeMJ9C-b(rgXHFFRpEENUCo#va zQRLuMWGO6*C-(!)x(G8)ZPKIUvsPiR*!|36MCl7P)zTnD=V;l9U;CaShV&0VhoqoN z43Y81NB1{ev40KVsV{~(tkzj6(}|LKw)GV{9Meh5QgpnO0*nG$C{-7zARB277-(7r zr;^{dS8-7E5$~6^#V!nIJtKB=XV+?uGIa9w)qJx>c6nm^45{u)q`p~oXkPea zAHd1!qnn+Wbu9a7U2b>jMmL!_1F0hG;36=(c5i^?)zClIeOb!&mm!gF1`zn*@RKmx3Hi6Kp*?5y`?NlCxUS)%QqF~{vCM5cI2 z!B?_ny6(20KYLDdp)8nKWshNcpiLlcxLMloK6FyXh4PryYDdw?A2zoLoH2BOMR4Mp zmy(1}^tVZKi|9z*_H_L5jUiIw!qlXV$LhJt&uItvwCzwIFIAeMw@mVuFXAOkHn|G4 z+=-Z#p%^K_uqra^3S6GKV}tC-gmNP1y$uE0v7Vkvp>9wv#-;hV2xh1o%`kO5NIae( z8cbBR-$XoEf=RA#ctq#Bp=761mXIRV>{Y!vOn&&n@LvPbU0Fj+Oz32Ve2-Z`+_Tm> zaC1<+k7WxK!8LhMVJd*4Zu+AW;xa)P{LA5Es(Zw=h@=(zo_oQE^OlNv*7D7$sTZv^ymTA`^vg5eqH~iQg{p>^1TCUgKSNvS=j!db0vl{*1#?=dcUckUXPcrP z*2!pU^i2AU;WSO>??(QlA>WSkg*)EZEp+eE+Ov(smYgOpDecV+m&#-BJ=%EsX8hGe zBfWp)rwX^ZOY)qS@|Jt|1Rvr!OanabHERk!e4?)YDVgX|0remwciu7o6UC^<>wIK4 zsG8H_O$tjruAEM{44xh-DCqrFcp!!G?W;bvd9wio#+)HDE=JNy4565is`>O)L+VFS z#r>cJK${}Iz4hq>J0zX0%aHEZDMxsdo`#657vx9hmlZXQrI96wUp9l? zhbwf{M`JU3X2hORot206$8KA?g(XQ*CfoO7BN_%M?>sC680mp?!ts7M`R7qVR~0v* zojrZ)c}uBk$ZF3e6 zxk?BrZs6Y%ftTz~Rpl$3)P9bg!5M>K$c##wxoT^pa8w)g(d!UdNzz<(A zI#9v%)gFX^%iC|^PHC-AVjR+e!NL@`fXS#br>?RPi8|t{qg(5=Amdn%XV6(Vn?QHz zZE2EPgp5)|)TL05$0obq47CkWcyVIn$#{7Xq}Q5ZyuKX#B+5%>J8KtP<#Esb@b1N< z_bE2kk&DF~4eBEI@-h#%=qth5PNy$LN0W84h(;LR?yRIq}n@n!{SjSuOATuJWUGYimAGZ5FfI z>Pp#WyK0Mp-H;fiMeSNOf|iO=v-Ymqdp1Um+KEkKM8tKx zug~Xu{l0(x{)?N8laq5^mqd#Rrl zBkW_lb}soeckN{uB7k;&{4SLkxG{ssr)oNnl%nB#*OtVf?3>52qir!c3MOB_M%r0K(9>06+6h`K9|j?_J2pzM?`G&WZbwRv3PQL zE+nn?HxGndl6qvQ8N->{nUQgAz2G@dy{4NfS+<2l>baJkTjcRlU@vLrGG=AEg|CDU zE{If2f3C1yAe(v}#ipR`HeGJ$a}qesf-yTWjVKL7yg(=u7O{ObhegeBf2)5_!uXEq z*#zqjt@auE62g?;qQ%1dx$Of?<-s?xZwydFfqzGaNAY|Q3C5tB1d2J90$;0hFs7t# zqv^!oF~N?M*5ZHHP@Ss-6YlVEy-}=pCJKF&Iqo($bbo0bQvb05h4Eq1)_0ve*GT;b zp&x_uCK@K_>i9@X05UD_m-Sl%IDus2__?Iglw-$&Zf2ygcR!z{8+mu^s4P}u?}}Sb%LmLQ%-85W{Uw|K9U&J#p1Qp;y8rjb zqQLf}gQ;=sgS4SEQl0?1A9~<>@r5t6k|yT9+fLx-xj$8fmd6H0O|n}5ITej+r< z04n_0<8)AFar^Hg$sf5PF&A(;>$u#_I(D>rx>)a9MVc$njP@%q!J%jN{`-#q@4bOX zGI+V#AsVqp^Z$(7ne~>u&9x8UdK4wVIbHQj&&O^ZUkvE~Ip+WOD4p=fYal%iOMsZzI0Y(r<~HZo2)a8SXI$+17W;5@BNf1_^0b5V!Vt!)=@$9@JdZMOLBh8;** zZAC5Y{A^?bh#HyT2GP>(w44Vxy?JZhNH%x`08=?Pl>BhF=#!yghQ9%;%LbtCv1S_b zM}av}iIYokr_B0SVTz?6;4(olm_tJYl`!JHgY!UC2haj(XP)R2YxyG)5J0n+w6W7v z&HXhX(+^vK1$;p^Gy|Kop5cAq|6Zert*HB*jtE=u+HB~OaZ%vu8)y6+jh4|)?4O#I z!;J;flO~z{AnY%RQX?LO;@_9m+hX%2fa&0GDNP@=eZby(Y6D4n=FTIa_F*hD5BCJz zp-5FX6nva(+@+Cn-ALcL7l4`$F)Qi;o`^szVgRluzcY$mQX7``4;abFulWpMV`8c2 z@YOkMfFdjBJ__dWG5G4=GqTfV>_dxGF!1WLCQ~;$*kAJzi+%cy*A!j{lZcRb)y*=B(#S%Yi?5F*3@K%tZ{zLgUI_vBFV z+m9&p1J=|$o+c=kvh!PS`jMowo4jAu2L!;^f$OeW$iV`3e^QQ_=9O+fv&3ry0Wi7* zNUV|rE3VhAIeBLrrf(5yKcqxGMBe}dIHFE7F?t?l#o_}$CM|vQ+ykIFs5hSMh;07k zH4T@!y$zgDZJVxktepoE&XJM{Tb)Y$AhCi$Wo^gPVbYO@?QXu1x1RlWiu>_hJBCWQ zcH^jcB%v*jx$DP}ve~TXkW#`Q){&Iz^W&vh{in5msO_Vo-H8B+mX%+u7m<)zj-*T9 zA+-H$6zI@&m^T#gF$u_AAHd(K_(6BIWxA7vtGc)~fQx za29?T<93>He6Nm8@$H-Y=R}%k0EDSkg&Wl|0mPkd=~lZAd&IFUQ3IETtx~2puNKr? z0U!0%2__xlclnh>!bgq4WEyL4@ z)%?xROWE(qd`-d4>pP2)VkLH&CtVYOb9o#N#T`WE>>MMv`klr6%@-_S^1b!of8P+o z#NYxzTWz7`J8&QE1%jxQD07~w;eVEO6qRZY4tq%It!oZEM;zn&Cq5r#6)6+m!g<@+ zN=vI9>_#54>hQkY{ETCt|Fr`okEgVjtp8FtIp`X_2w%Y`Wc5EiI~fA!K77p#>RDqM zkcDqI9t=(ZF`74{h3c~GWLxT18z7KOK$ZI2v2G>V&Fz!J2TTS8IS<*{7dOy^0fm+y zMOD8EulAiXus5*VBWF&2VK*f;*olb_|6zDs!l{BKEhp$}FQ_d` zpxq>6?AW`3fP~iod`2;P1hJN{gJ6~kj>!d>8g}UR`f^^n>*Ir8w^PjT4lquf1BUX6 zIJAXn9o>FqC`I%4W&+m1QWnTrGrnNCoLt{pH&}RG|LyO>ev`9$wnuvj2tCyH%mc=u z95XW)?9I`^jl3f9eVf6?(Z@nyGpwH3z%NRJS|GN}2>69uv7Tx5JH0EIHS0y+Ok?hn z2BKHR4~V-I@`x%D_~_i}*E!Ln^1VNRg^PS~IfDQh?;1v^^kh#gpxij!%Z-2$-5F?< z#xr;`4W-HFoU4UgF_9d={TdHrMgYvfkl7jO$ltqP-50_R#7!PzE5{%@?HGWEN(wKz z1Kho2a;eNG&Yjm^^^C>7l~0m8LYdoV!t9EaEM_f_?vF*())Tl$OLYT0T3p^<_+fSW zDs(t6Tt3&jT@sP@( zH+~#cEZ6_Vy<)X|YwZDJ_Xv#FG->fX&vYsR0IrvKz=prc1!}RPG^BH2F&0QNZ*~J@ zm*dKami4cZg$F||*<5)=9VTi8-Uc&8XU;IT>yMe}@S#pVD&<>0vQd%fj7slMO+S^9{d zH^Yb$c%}FnMUf3Q2f-r%cG|}?LI0h%#5*6{u3Hr={GvYeI3Nw9r#Hy^ zx}v$AN#_|VOIcQ=P;ZpQ%z2O6$1`0QkhHE&00p@$;?2_4RQow8>)6YAWOyKe3AS;f zSgh?sTRXA?+&gJ38>9E?$UP<}*du8mrepW^9tNsP(gYF|c3UaN+HTwdy{Wik31$!l(!a~D4ILEyWiK;0k4N{-;*J>}@Eb}luP5@U@kdBCcr<&nF0gXk z2n{suZIbfGk4SnN+05~b2sGm&7hx+&CS#b(s?FtwD%>=-*{?QpQ&bu>2SjjdWKZ61 z-wKhCpb~AR;+=f!1mRPnnP5vnOka0(?^RLh4CVCtu_xeD;MW-|6-uMh8Uak-LId6) zRv+Hj()2wG2B^^YvX@TIeo7to=CR`Ct;U&(Vmxs1JjB(x66ceT;$))3Ntc_}!d2SQ zKtN8y{w3WnrMp6n`&3H}b;Nm|})FvA+}#-(0+ zSLVI058xJHi(c;tFzvclN%e_Gz!fKhS)a1+nkao^0N&4bow`Q$*trQWy+wG6jyG;% z&-C5Fe-$S=XCeP|xwUZ)1Lh<8$?F0dAjw^PJRw<^nf zt{{hNYWYOmg-Umvop9?%V}teq>L}1;2kW^*)u8-F?r|bC{dn#uuN1H!4$Q6W*!xMD zGp69pW{r6Rkaop@AM1~2zA#JArnh-2s`(E&z2au>qDm+peX`#h<7(>m1ZmVE#@reH zj6Q%DSM(Nc`Q?R_O_cdd=a()(iC!u`&c&y17NaW&TrkEtPkim1-~GJ~Mfd$L|@h5rMllp0m}oKLr2Pk|pt3)PHQ;&q1zX zPNS5keI00+w;r?Yq=7AgZB4NcPmjvXRgltR3~@x}-sQa&_Vi>h3H~vxh1XaKrG+4G zZA^_h4w_Jd!@!wk?Cu(M{4JR}Df3}GB}+!i0gcSb+Ye&rw85iMhg`4`EkF>}x~#?B zZo?T}F4il+UC&(oVr@ioBEsu^UqtC^u(f2J2(>?PFAxHtrCUA~@7E<|@t) z7a8g+72lBedff|Me$j*x>x?RxDO-~c&X0day)My*{q z@SmAsv9+NQ5}o$S&F`1ifD|XZMSKdI7@XVRSr5+M5IrT4(z_vo>l^tAfW{efD_gRg z*+nF|-W`j{x{B$3d*$yj0E84*`5}HN9Nf9Bm{HLkDL$g#9SP^HhTm-vhbdLuc7RF0 zY|k%b_`l=t*#;r;_+H%20%7w)Y*A$y4CsCqVNmA*@^!DQZFtLQ+Ol2UH`slF6_JUV zOO=u7cfRIpjz08q*X;}(865It?#of+^aOd_5RM=lDw%t}yc~e}*`5JX%^-N>&H_!G zhV6Q%D!>N77zk&8Ix*Im?As4lE&YCw5^n$HJ&X1&8^|?3WuU_7RSs}vMt~EiThT={ z31e70`K32DUvFfi$!UZ?Q5bce&kZ}oMyy9?6?;rv=`kN~uJgQ&UE_@^9Z(pQt83Bua{n1ppU|*2qkP=`x za>6_r8B|_p_wzEV?lKxkU&vgw>@6f213yURm(IyM02v2BtCeimY5Qs@} zLjMGIrjAap? zf`P4-_G8xWa=^AF&k)}R1?Dq{%+}R&fB>r6w5ij0RH$?KH&IDkBLgBoU7Kr}q=(eu zfUW!{KfDY&8E^|oZ?)YMGMpR&T87CueXvH_=7gv}N*Gv%p8Z4FG93!buD^VVw8+}> z$om|CwC_yaV|W+<@cqfy4p!(RQv2GN=DkDy%nO5rg6$-ovoivE<~EnC!(r17qLe^7 zvVXmDBvVjANzeVKqAJIbbJGz>P}1XNVlY#lx_WAA8~?BfSe4`&HH(dM`VJ`tW;us* z$iA`a)oLQcpeBOI%xc>Dvqq(luFJI22o^N`Fgp07uW}|l)nbn|@tZCrpzW$BU-l&j zNI$CXjG4qf7Aj7}8k>_<0%?IQkS%S$3(mVmyO{N9Ev@t_h*mPny!u2aZ(0Reu!}j3_z|$s)bX>9#NATz$rqjI17uxk(&q&{ zAlKPJ9=#1pciHQgt(aM#a!1fp8P`BGWe3ie*fk`z~9Z^_PZY$I`Br@&?H(z_ki!N0#YiiqQK2yDXl z7wW#93M4wk-Rw%uZxq(?;^55V6dZ&HoP{Hg!vmowDsjK5qtk|uc{{#{?5-_GRaVJ) zbMZC}WCxzkL<=Hhp6nLQADTCH&-8$^K23wpra61JJ7}^^S{_-QjJWkua>R@MJ*lB) zre0=mNICp0VPl5(v8*`xMW^u9h&9j^pB`fP_HcDt`$P$ooA8tX;Ph{>Avo%+0Mo2% zpXLvSR8&?r_PVkj`IHa=pGNHhPJP9_w;Qu-7~9K;z7Rv0*Igd6YMn8oHsHU|r*k+V z$YTs8{;#VBW3r^(V>64fUx1@p0zqi2{pk5Td+fK{${Zj%`SrNK6#TA6{%#I&zt`sw zfP!t)_kRi2)P=~S?Iu6S_eO}d#1|9u>9=@;e7A;w@RlR6h)SoJatX}>(W{9i=CWw~Xy*+Cbu9<=R4&Q&Z{UD3@BLYC@WmkSR z0M!EUUs^B6?@CKik_j=(YlTImG-o_u#Q(_Kmej@K{1l*<2{(7nqTe@`fwy!B>S1&) z%t6>We+}71DoC4jqTva~$ZSlkOwOmYbXc=pOvI)_+*wa)?+7>c%g}N%hVYcdr-=5^ z%-g!tZU0=n@>!w2pLmU-rHfX}70CsYMqsmR_c3HH*NLvx4)4!-Dz3s3>;C+tlHTQ= z<%vCXZbmgv4<;%?kkOAfIn33abHB)t>kDvmNH`9;LJJ{HrplMj?0oiOik+K`a8Kg$ zjyWAk46NrtC)0fgmVu+Wk`4!`@2=8MG;^Ef-o0IK6X3(#0sDL0g*5G}cWs*aO*5KZ zC67Q9x}7EL8D?DuGei7<+2U0%h1o}`M3qf+v;koDWbO~Q$|4HdI9I=P;#3%BC$pGW zLD`pYGjM--CE?ik<83v=mGuFsF?wb-UZv0pK~%=4eKK2mD<;rCCR3*36tBoQi7#>+ zu)h~)-nx`5wntTvtHwz#9?m&%&d0N{;M$)@KUp{@Y`8r-;A*9+=n~|hd7I7p1O|+} z+SY0HD^X`G%bmO+aVwf9*Ep}-m}GZ?*jm#stJ8rf5n9V+Ml05U370mgS>l>D*(H+z zt1RaAUmui^pco#aRc9p?<(~=gN(_YX8K6LzCe_72c)-*`36mPbD9o>^m=%6om7+}R z1>o|NcPSb45!l()W1}PoAHfW+w5N>S>h1#^h$Mq=1a-38eZIMp#gm;hrrk|-Nzc3o z5T8EJXFbRU>KC#Ts0uPR2C4mTaMx>--sss++yJ4o<;C?)5rhp|+oWRXEm_XuB0G<_ z=#Azi8-7G{+^tC-Rr!J@?vlA55bVYruj&>m8vlNu=91dGVOEPWAy12XjcqcD4*FW_ zz}}k(Tkg}B{vZ~4`xXd@PbD#AxI>e}C8G^6Ne{x&O_CFl)*pCsnGH5=ff?AL3hj{myk%dEPLy zg}r(36clB#S8NGNbAJ9@-lk|Zf0GUKV>Qhw4|ap02cvpm&an)qlo|>4xdpG}KahnP z1>iMRfGU+fmS)`|T~(3%1g}FN^Sig^SqPoEMAaARkse!Mc58CsXNZ}13*uDcj=xLw z#OqSXonpWTSS6P_)hax%Dzth*|we|b~CoKy`sB;#gHvvy}E=^YmK6#bUR>ZMINxhxB%|4FK8{kwzD7{jtvy`+0 zlrDVI#|iG9<}<+1qXeEP659QSNr7fRYR$c8t6 z$Z_+|h2m!mME_3Q{`ABT`&i|RZuyt6?Wt5D@gy}f6Js`7=Xb>w?6;O^Wr!k-g`M=W z;fjTx2OKsmNTDL1y+5A(rhbUK(bS8>kjjTUQ@6M_THc$u`gkry2kOAI-n1HzH%JZm zX9S?s-7igb=kZ&#UwhD7Ai2{&hNJWflWoEl3MHrvRS9Bbt+`zE*(W^5Q>3qNO)7T6 zgSS_}RPr`790_95xw^l?R1!M1eFlsj$v23$2h)|!SL;HAA_>M z7iT5)lR&qGN#fzqi0{ng-v(HjJT3-$DWjq3*8hl@4niX5 zn?TFLrL`h6JTo)sZnXnSTzrJC`hRcgcm`!{+%dhsh?OZ1z3r_$fLSThtRVo&G^|1% z9n|~iDk$75=swGEH+634Ji(bmH)wk?|7WAZXaq&NcE)q^P6irND>?~HHi}K|>vV4} z;i{fopIfB@Bcgt`vMot5xGl5>JGeWk(_~u`a3Nk~_tKVRe_CGyd%c&6>kRd~ao(PS z>qJ$bJ&VYhFDVh19s!yc{(#Pv<2> zO>0j3d{DtDuAj~bRK1H^ZjZM3yK1j$=pDrSgc1Z@;C_;|-M-SxE5B=r_zmpETW2D2 z&4H|WX4t}bn(PGoQ4bhjdnYiXcDw1Q*$XSyOKq!mDU=s|p%Ay_hm?=da6OI{O$?ru z?@@1@@#rI?mc@yV_KwlMyc++HHx4LZ-!{7>%D97mZWEoTbs$OL?G1^uid}T*jQ#vl zGAJFBcWTM^alDGz#OEDzYjT2}8l3FMRNV2Oa8Ulu%{Uhj&%pfw*n^Z&{#NdT*C!qX zMz-sG+~?E}<3I{ql{SRTjju0wK#m%$;xU$!VRb@Q)COCZZh3r&=H>WCdCa8A#fUcP z6$*+Pke_%k{(?bfTAE3hoqAzFaazICh5!aBp}u`>MqX2ws*qFVsCW=4anP@#E%O*u zy?j|5r8sJ{aj|bp5Itit`GNGn-BrO_K(Bv=8u-Y6QhupwaWj@dwzpt@I3^==JUKpR z^&5d2{7;p)&rQymFRWY1w0tj``ZjK1>#lqg`)-$p(Q-JFPuG0>L$H1gnt4$zxYQVy z*|BAK8lNmXRqVs~PIVG+_e_8stBqq#LKf7r9n#g{H`z1t62a(RO{fs!Rzn5vLfn{S z|8B@L(XVAhdgy+8FGd{s}Wv>;5xEt%=T`=kK6puV$ zac-Bs1u*ugm-bq5ksWKiE8}s{RwrS?Mel!hdJ)Aw3ytp! zf${2C6K>}PE1^sdeX`jvA-qbbba)b;2i$n$I56Chy7xLY0b_D2j z!hZ7I+CCEpZU4h@F$=&<^JBWX?AZ%*^@0}4-$iDK;v&;2@>Yx43Vm<6*TV~mS}Wck z6O~tzO1^!GA{g6d-lP1|I&%H1wk5K0Ebya=+s- ze$1_P+9xYFgtF=kZIU?@X0Asf_-;(5H}*=-h8vz5(=E}rRB>^C8a37@mI)CiGF>2i z)7%ear+2&Kb4H&tp}42M_^IbRL{@R3&r4I7p;*~)engOu>z}@ zFZx4x?a3iOQaYMd@!Le68|U_*c)Rbb#RvuB=E?GiuggctIl&^?gW3VNs>^3Z73-dg zD2gFCUu?(qrH=t4m<}rbz)PuxT*E*1Q*9>WMBJvYzKqJO@|7-Dwy_DLm7Tt;aG!4 z7y{7tWtW)5tdsHku$Vqe3+ntj3L3f9EK9*#p3@QI`0|U>C;Q#|RMa!NzK(CMsE1@* z068pze=voP`UdZN1tabzUbPe~;l{=P@TAcZ29w ze;=MN+9Ce<g$nU)pzVwoU_)QC_YRyR>}5Wjn9Ri?)#0iXx&9c-Y}k_N{`8r86C*_5DLN-vJb$`fvYgclf7o^;J9fi!j8Q4s@*HP*fq@aApa)nLcymzr~KT-t(7i50WqtMQpo!Q-z;M>jV z{xEIPM8MtCN1#DI9@(y~RnNQXgOzrsvbqj#&e)`2U;=%v*OX7~mLrEHm4P<}!V&4teW%GXpgI6K`Db4~i?-9!bAzOuAw{w%Sv^eUWQ93i04rtu(0r z#Oi6A1u}(=4CYumL)6Db%Yv98DDNrbZ#Ug~!^Nq}A!;A{ILIm2wJXO6QW=Kqr>Mqq zp@meZKAap#0by<)c9#xzk-W+Dx+Gw#ywc@kWEE53$kB5XOd#lIz>HKlJZfVxiE$+V zoOhJ$>qo5{1pPe)Z-?mXe9Gr%opd-MRR`4F$yB*&MFnlJsKK307w*n*7D;HWw zcJ9gix?ha<`d*Ce6VbD5lI=n(p(WMt9@N-XzKOUm^fUnfkRVgA3NvrMUU`j?1G|;z8){lzbT}O z@}K>~5tpp!=u34IHWVS|PPX$NW%w5})N|j-12fEn?7Bj=Gu2vV4VWG2m1flf?YE}@ zAM912NELlyVV-0(TZRd*5lF=v-rm2i+PH^ar6$llQ3w}X`bxZ0BcW@F1Lme{dZEQb z8OK-mW9hzsn~12k*$k%@CGyEe?Ww`pdEyMcO6c0`KG2tzj!7BcErN4+zWc}Id>WK~ zG~3wW22nVOBK^vI|5wX$LJI=ls#~+{xzfgxp&MUaB$hPAhBcM{JK5}0oE0tqRMVTc zi5K(@MZcv1Pvw#IM(J+q`CBs+PAJR{_}x1rZeMzabCO=W+fo4&v$$0Pf4wGspZ>-( zs03K~u)u<_Ov(7w6$4-HUNe+yXO=E*DJwyj=5w5AH4X#1_!#Ap(wGe-k`UiVs_e!=;yU! zIPY6F6nY3+`#5`irC2e9X6gWN0V-f1!`xq8k-aZz zzEgZls_kk{w3A7T=oj9&2xFs*z5f?L7Le!G9(io{AZKkjNA6rhopA>E8^ z=U411$;eCkpnNy{nKTn3aesq?$ns%s`GpFX$<}Z0Q`BLj!RauXN}p2gnv*@jH8*oj zVrXSuemY2Ex^`VAkYMO}+2y~j9B3@iN*59+R=pKc6HKviY4~1#2VbYERcrNeYzL=( zS=o~}^?j-@v#ZhpJ5i-BAAXvD$M#;=crwm=s%c)I^U1F)Vy`jZrKr6(X|R&U9O=75 zTY`JVd%veLl4f!pXpTftiy(MCg}pFcmoAn6ZuBS=FwIk1jHEN?xBkS`uwb4;{$2db zy`R-olyBg>Y6v%F&`>Vo!5NP9R7$Gz62eU>`N{Hm?hp2vcm!|J?&REw_Viymbu#wTsgg z@}Iar9HCR#{2WBq9t4lGiIEdUlHz0td93a9Z_v~ZHg}#Z&&-n`rys>&JrQE(I?G&7 zCX$?jSPwYRm;!r0^+UNTUS-r=Fw17^XWH>6HPV;rG7Zf4sX^{fAJ?tV6!4<^cN>8H z*f)7=)T&-djtw&v_fXOcQh(NDxwy~pe1QL1phT~m<${04@lzISB8ss!OQfekh%^2+ zk^J>yJL@bVP&BB5kQ8(7zd)~QV|m9SS6G3u1x_| z#@-#1f8yH?RZHuL5DVQA3w*&WGo4Y*mkF4hiN=Fh7o0nc5Nhxq9R?___#E^>J~gK$ zB3&@eCCQMIz~@de52}Mm%Qvkc4_DG!0L7n3aKiaf;JjWoXW2lY6WQ}`dh@E~|0)i| zNHg!x@`cU~^LNNMPqw&_?W-cvRJ$hJ4^UKUco5uhTW&QUec7vI3#&r5zxiHoq|VUl zH`6o=@2KF@R^;Kw@vl&aldM+_gsCw3E4}3REPM-_DrE2#bAB7&a96Ie#SxzHojV6- z;`=rYp^pRGx6grCLHYMIalZxIMshZ)W&R%DYc>?a7X;aS+6w>WCcW@aT7dSso^Pvx zHSSyZJ-@5G=`I_FxE8O2?FK?!(dZTzLCrh(_@gqA0=D}rHupHAf+@?3K@sS%8Q-n( zL)$x0kacsUOn$645ZlyNp5)v$C3xkyP9Yc^Ro&|oq-W=RX0|X5(s@n$zaW+B8a~AN zC&(j;gfl>oHWTrNVzq+bHiXD`?R#*0R z=jMHW<|j{U^Cy@7R#dqW@sa3Wfj@d;B+N$y;tq_e%SaXcdd&`j(=S4vS*GGK&*^pp z-l5hHFvBaBCpZ-;)9c_1x%gZ&UfY3j{4v24lzD!;x+<$RxXIXB27z8r;$GKnCIiBL z(RZAq@L1_kOsDs!>A3QV2{~Y_|6)e4KLwOrYS^$!IiH&%_2Vg@i`M(1 z`<%5coJzZ${3fKAdOHa%-HwZn-7VV2CnM+l#>+ZsSkZ1l4bh4xFaB!o>mF_XELf=} zH*ZKb@y?@l=p%?ry9WHM7KmyfL51h1P=~@%0g&{h56!{^E&S~!Vms?8io<)Y-DEsu z*9&MniVQ{BV`U)lo9*w8nX<}t;H#Qmf{l$8Ca^DNXLf(?w6>JtoR>o1t}qw0m#lXX zQ!Q6?ZiL%;ujY{=Ddf43{%7%0643Q|qAs zfwTLspxvyB{A9ASWtJT4ux0;>&I2v7px)i=*_%-n2_t+x@A^W=vN-~-$;YJ3G;wGc z3tJ*R?!_OOvr*jT?HJTZXw4M<&@5^{N-n<3bCa|L`utalQQ|p;zb^J#Xu5NSHeWNy z+eee)wyu?)Pg^{}uYiBd_m!_$^=zjPFNK(}B}Q_H(gG6M&e}!tI3wfL{T)HDHrpJ< z>#`7hczu8Wf14SB@(A!S-D{p8BYP8OXik>tcLKcq$jr5qdiFpizf&2X?w6rZ-jA0HD~U462|6GdPevN6hV72aWoiN4^!q}Vmsw*gbrJ4 z1u-4Gs}87C#mnPRG=9|~=H?)M)N?FuUI$%6%W>$HR>PYRG#bv9o;(?1NVQL1OZDVA z7l@;sa@Y(ognDXeG4jn7lVq8}_OzyR>CO+u>E&Cot0ANz?H9Qe*VJ5PXIC|blFjJY z+_Y*F9DAM3wQ$; zTeVs1sHs>eXGf6)&>Zihpr|>rr9?doz~Z*5C#FNpbs=3Yr|#=~5Ob)IM}LF>nw&b! zD#B3PYbkA`VY@KcYk|?@XN2&Htz@f^ypd~YlytDy14mE-^44A^Z5q8CEsE&#Sz(Kx zOJQ+J)(rHNFSo5ZQA~9L^bx2Vhw)gkF0h_lW&1Srn!kjP8AW&kT|Ag?zTXYgsVL{Y z5`WKGYp#;H)pk+N)rPmhFf?P_M#^mOln|%tbzAT(vQ?KvGKNWJmF%}qZHp{f7pV3>I0W$C#3-G&V z!V*Y_ySl{A@S~Zq6*NwXIn_Gz4@s{jo0(>BP2>_p`L-I@EAO@jQ!l)d-2CSl8(7~i zLQ4vzWHH<6rQqeP6@%WtDI*YH*xOfnc=k68uMmKfbDyd~)@non%w&_>( zcP&i&F-tmAJ4!4?j36D8Z-)4wI2J9T#Xb3fWSaT@df3|LGesPoRqUZapsKs~*I04m zh%wo4XX?cwb?`cF$SA#sQ&IxH{m)WkK^$9%)(c2Ts8eM=Yj*6GYe}K22D~(XL#c$&h zu+L%91di!1HA2+NNQ{E9id52Nx;>>6KR-SWdyOvsijOlqlrC9v_3zQ7UtC(VpkJM8!0y z*jn2$H(658k{ok|o;w2@royH_$-woN$ANcv>e+Zs?KsjW*j8^(`yd{FJ$P?LL-E*^^2R)k~qTEW=T4Z zSJ}fjE$3v0{XRD+P~7A%S$!5eJq4sD3}Y$Hl`Q`YpL!r5aQ7<^ieRnmEf7*d-{|qQ|EDMA*ZIVMQx7Q6^(wW=ZncbI0_}`O2J;*JpgL&syr*RInA@{~v?{Kn0Q3!`= z(|PaIM3{|5-F;EfgG)=<9l3HrIP(69Fp3*Qx@=>%ozRFPUBw?k(Pbk?GF`!s`Y=&< zIL;+ZfNHS~y!%%dn`qVvF;5Ql7n-5^1^?SjCHUlToK(0l4GGj0bhAwZo_YB#U-5Hd zq+xwLB(vmbzbnw(;Z`OD7CFDjWPbo7mAs%NwOVEjG-`allR)eaTid6nqF%hY3WL3M z(3rhCM5Y%+>0aOKThFAt7%tCP<2SG&BBoD!3k{1T?EOLcBE7D|YG`Cz@o(n^Nq789 zOorM5()PlN)9J6Gghvgw6^rF}mYii{Rh+N1QaR^^fDj$_>ZumP)-_+mtY1Rd@ri5q zai0QHpniicD$YqtYxtSU*u3Ho2%0s2@Y$L?-I?^#Rm58X7fgjwOIJ+sY1A$=32^_6 ztptUa^bXDledXxM(~r|X`=B3cmvMYx##J$pFLRRbeNdVUt?${AE>$Oua|Df37nbEf zUM0ooJCN4`LlF%?)n1BI zGE^zC^?KTNLwkx0)&1gc&gQHfOudzYM?E%vwTlOhK6l>}?gc>BIM#t`RZ7_B`ck$O z(LiiWGx5rIKprlI1VuE1N8>fWLl5qa1ZJ-7y1B4zEbL|-8ClOu_#AV&ht4Y^cS{?U z)BWO-D?;@&<`(@keHv*#QNtMbPH+%;Mw3kYyl zW*-5iQ1Q`Bi<{v6gv7Sjtc&4|urN-w+%EsDFU%k~f>~!)X3cMK>vT+igOmdLK<$AK6F%cedqCV`0#-6aw3sv+cdNCN zOW&gg>^2+k_*$}Tu{m%ffOgB*ZLPGkgX6IOR@%~8Gn&t~6su zS8`2IQv1#KGJWrJcm5~=&|sWYaY>u+{#Xx>O3;ht6pQu0=3(|)E_BP@AI0>GPQC+? zH;Xq~6qf>gcS?U)rVTY*ohK@lS9pC)mvkd{1tef+^J)XK`?cWY0MyRF{D3cvcyekU zJMd=U;;-6+`MS)mZxaZ%mI0@<16Ox4ugx1;;$Ghx6pP>JT_bah1Gg4R zIojtC0;&bCu6JMmQF3MDpS+@ducMNpH9c06tap$&VzZoZJ}rOZ^kJtLKOSg8l2=Nf zJSds-_8_pLgfgBuyqji(9=uT8Ay~NZ0PwLUx!08k3QFU*Hq!UV`z(Ah<8&Sq4)wfu z>mNgv9Ka=Y(R$>6=>->TpP2ug5uCN-Zi|I)2!RgI9B2mY6HXh8e32OyeC-&8NBKZL zbNlRsg12iz@G&D@T~?d_%eIZSnWpfRFv@OAvv>cbG?&s`uvNX3Sw?7;xNdNo? zEs6dUVPQUGF=8>qwcyxxXc{Pec35q>^|r>iTmOn~|K+ljQVHVrRwKSTVIIVFmTYq3 z_USUSCnNBMT4u+;Z_N3eM8wQ_As4qIESHuaf(DeR4Z}93ee&8%{L)B)AvrnC!fwzm zmHTyq<#p%M=o$PR^X#Fq-$3QnJ)?iIJAAEMd(8Vq0y;IWOY~E-&!|KzUo$~y;9S6D zV`o<58U6#wPvO5@a(c3P;NTb1jQNP!eC=Fyk6=V8dkKJ0dm?+Mi5BMWN>vemp!&c5 zK`WG$;%Cu&tTNv9q>`iN$NG)`y)U8mOM5b-N)iuA8yyzI0FUtaz&)P(qsmPL=9@`+ zEZFSN&TRVLb-|~(7r-`vumAT!Dq(pg1uFC(Rhvo!KqB)4h`V3{aQ6S_N&oX8^_k#* z^>6<_o*BU3OdQz+@){FKljVY>o%Oo4Oda3()kXc7c_371FEf55N6Un?d#v)1Sn#dl zG~@Fzv&+f>``7O~jRz1s;580D0(wDtD!=I%cfuL@M|rG$Z@)h-Tmgav^^b&nKW)!l zUo85UM1RsusJL<3D={&da_Ce?Azv>c#`O$wa?BbN)NU+_2^N)g?g|&mjNpIurrWe5aNA8CfFm6WCx%(iHs9#)Fz(n*hoC;V0`k zvC7kMV%5ino(To6RC>ZJN81O0`u^ev5iJ zq+$E(6D2KtT~(Rp=Ya{-PHEi6iqOfp{>~<*%jHbf3-(JpFe}UuCuk zu?GNB=1S+pgMqH5yZc20edmC}^~nV?taM}Pd(Xrx!+O6t!_U$=fr6CPWsBNx&3 zy4o!x|14#~1_34d6#Txy$dTgVq>aT(N~P;QPUiMbh*>SCH9&GV<PmVvq;%oKY)cWs@9&!7KUEc(9%1th5&8eR${spS%YY&J`g%Hg(& zQ5`WPi^8QkN!#u?$KcB>-vNc%wj-ePF5vcC|AHI5iGr8^b7#F9%NCl_O<|idQv<`^`Jk=7Cv=Y03mW~PO~*Q0xTjj`ChK;1O0%i0qGfO zp1|UlB{e`APOcmdh~cpiX(3*n0Pg2GoU}H%xhVpFzc7nv``ZzS0V}jWtbGjrBE#oTt_Ij#Efa%^RuJURRI6SUWj7PjO|rUdW0v~?K7r!US5=Qo7tVgnlev0+J{6c|~8o7xjNInSnTzEB4;h8|ZQ<_6I^&fp7f$LJvKT()|cH zEZ0p$%fy%&nl0%$WKC*A6kMKo1L=*@Jo>z)%>+~L^)F!p_i}66JJTfr7^-ogtVtw% z2oQw}{}p}o5g>aCwbFSnpfW&(TjHpkDSWb~!lRq}Ks3eN!$FAD5_1%lZ{t0Syptf| z`SR=RMLOlbyfM|9_H_<}btex(8AB*00!+F+pglDWDC{P2qeWkLCgx7l%)!2MCr8FJ z%0(x&X2BGN33Cki(>0Z>I6`eazt-XFxp`!M7BB$(IFZ%Pz3)6(wpv7;``?EA4$zRV zi0pfL1`IKf1hw`yfL%$uG6&9r7hYhIW|0XTV-BUj=jEFIAF9qetf{b%`y(VoN)(VT zL8PRc(H$ZPA}Jva(ozGJ6e%eY29lGOZiY&?qZuVVIyPd!c#qHfJlA`@|L`B%IXm~c z?_YhsU$5@8yEdnHEj{U554Seade@47oU;u`7 z5C?V%-|QjXoh~}4Ky}w>DS+j}v8$|`&yH}p zz2drCJ7_W)SKBbpMAMS-x=I1^dVl-GfM-|IJ>nnhh#)0d_MlJ(i5^@2^&hVUELolZ z<{p&Sf1ux@A*5S&M7oDP->VxRLCG$YKDh^i8Necb!vo2dxxw{Z)`1LHnkr3HCwNE5 zb(cmb1N|t@4il@u^?QxbUr@!z4kITVaIcqEU*srNFSZVcC3p11*M5-+l}}lReMseb zg+I1x`gT2UDW+_3;qsV}&9atayDcPXLtq8dJnuc&7%LPwWN^t?fU0=lX*3iUUITzb zruZ_2oCg~j8+I#BIYUpQ06@{XZP@r9nv2=7_MD~|r+J#?F;qJ=nEIUl^zP~3&>7{m z*u_+MCQSeMpOnJCuFL0myf=UaBHS2^J2Lp}@tal!yuu>7@c|8WALB9adnbP}VIl#% zg<_7z0iE!JP*3bBFZk7J;R7T|au{~jdr%;_jdTy7qdbdlv}F@lM%;hkALr1V91JQ^iK z4kN>68m8Stg?q@Xh-p zO{$9>-)ADbvSF3kkT_Z2&W;FJmIalY^xpyqpv}K<~f_V#LV@#qm9hRRuOPS1SEy)s20Ms!pNx^_(g=uDW-BsJCT0lS?X zMkkP!ht-$M+6~M~d?5<}CWzN-KOS5FXj!$peAL_AO%2G&dFuN4>P>dv_@n6zYsepl z)$MaFD~}gIb3E2OB)Pav{$ed<5&*@Q2<-^M42f67D#@9op2U}2`3(en@SwjSqYbcw z7z4_Birz*{r#t$xIfnA+QO>_o59SX7<$J7Sbw0Yok=QC-#knm#t)d^I#n~uyq>+37 z+IQisT{g!`6J62_$&de-5tb$Dy`lrA!J$s~8haeQpfNB#G2lE#`Y*ygt zvgtr0291Z+xT~Ku*2abL{Pym20lcs^Wu^7nPR^OC8dc7KOBGnAv-CZxBdg>ERF*Kv zrTu4n9hWT5F#fw&D_?zd-8z zNq~JPsx}G;u;xG% zS_aI{%N4PQ2B}#|vo`|b6vc;%1du{WVCUwR55fWD4j<2qyZzlMxqsY;ZbVKH&@S*p zjF>^()&Du-rf>Vz4oetdCBSn4-G#77W(t`1BDKI10U_X+iW@DS0#Kd*v#Oeb$GjDQ z4u%klUsD_=J|u*C-?3ZH1Gv6fJU#=gj{Rjw>rus7MZW9AUx>bO{&O+$`^T{z0D3pU z+Qs=i)A%J1X~LM)$~H?cALAE=>Mo-j0s#n3ql7|LfV}83P?->(XHR>?Jp1 zykC&v>&a*t;@33JcUVG#Wd8K41Gp<@LOmwC2IN&DNw>H)h<-M37z&gui780^!CxkPi$fV`6yr3_c@U>^h*Kah(r8$b<)Alk z0>^+q{6iZPiDojc)#SsEU_rmPF*vo7)S+&>jJwyC4vBry3!96 zGE%cysuv{igCQ-{ue#Evc;a??SAflb@UDouEIP&@*`Bu<$2NF< zp=X?omKuhwL^1i5fFG_rqj)<2uA;DFKD)G#8ns_Js1Jg( z??;^mNr+y}GA>l(txsxna{m(qSW9$L@e&y=zP+y6;u0Ja3usH#&jECAJ>p!&Z@~`} zuQ-<-!sjE;Dl0!phO{^8QF*_N*qBL_iYL8tEyLRTb+gp_x#xnfOK4u_hMqc?GU8@Z zPk9bDbHgCA2iPR3*zZEd>U-N0Mg}?4+q@w$?sE2BYs$(Oc4NS`s@_UaWJ z-46lgM>OD!nDDo@vKlObz6?TX#3(`VoVc4X)_fJ}O#$>lYhKy;51rUJ%%xnB{e#l$ zN!9%Co!T-zaeO%U@#+ujwFO|h?r!Ot=p`GW@IoGu7+Bgg92e7Z5X%!)KZo4H(D!aX9r{K21HlDv!FopZ11Bn7XK*LkGTuzW z7=AkFdpMyY6)cJB1?|rsZB@|IUjg4@gvVjGnvX!=ZSjCH54hIyhodRwAYiv~5qPmv z_ACVdvw-b8TELh0GwC;w0@hu)4HyC}x+OmQ=68IlrT^^1qE{?7`vgwaI!v*kD`AQX zK)a*Ddv{ULjtB1<+X4R;-i_khuJhH~&ne7eJxsezDeFv=BNd*Rpf@neMaF9+m$DQRqIVa#L z>wQa-kphwobFZN0w;Am8RHcK~}!2g!hteH$}F&ef*JZFfIR>fXZZn zLuKm%(CX1t75Vj0!W(a3rAscHs(+K&=I-Ajih6cJzHjkPtiV+4_fw#r1km7EzaSz) zm8h}E9|%X$?U&G|4(&^ID*TI?L&%BJx^Mfbj8GbfYfROS9L@S93x-!S7i93RX7>}@-DIV?hnKH!_$unW2N-2l+6FUD z`#X}g36t-Ha4qgc|2my)#wyF*4JDAr+g{JKs>oZ@2`gernkI2!q@HaJtoCPT{fF^y z!$`6mR(FxXdnsc}z~fJu%lDfP00JTVTwlS^(C-~)3v8_KQvI6#@E)8k-6(!cDPU}v zaTZk#SWz{#$Vz;QQVmgq#1klKj*%^B8Cbt=*9zG#Be6Bl&8I9#0cuDOyZ_2AR38A@ zy2$LK&iR+ROBG9ZCFMco70xK;H`oay491j%@8Z-#)~2dMhv1D44`SMYrc>}(8G`A@ z5}Z=LC9E1ko$0p3Rku$16lZK`tn*)`=w)@p_g#eqgMObMQLz=hHS~q3jdDe-PcJZy zXn{uJ7GXw$j6E339%M&`up`^P8$QZFTaJPCh+iM2*tbM7A zr!cP-Lo);UdTv3e)v3irWe4`F{c4ZoYJ?ZD)TqSTXuyS01_>V4B%_)_-59WshdA?B z#D4!h=is;*LzbU-IMT%L>3d)Gak8V&<8@QPYhIusZNoVCqeBXdcS`L;j|iEbC2ad6z4fXDzK>w2>X11#LtT1_ouVtn*Xc*=4Esg~|-BO`nvT8{s=-r`EKse@| zGkscL>q``(78*f!DfqG`j|4QZ>(?T~#4nLINnz3a)@dQF`QXAn%3?X;M zFqK`_#;z6hCgQzJK6L@rq^|FslwZB`=uPTB0%}P)pxY^3H-!J+5`P@_oMDU}Kcl_~@+VrlF;s+q3=MgBCfg@YVpGg23 zs{hfE^{GCr6BP&V-4BQGWHPM`Dv$FPQh{QGsSfSiRF&EMvMH1WyHU67s-Yzj;P6F2 zP%cH^4Ag?RM%W~{)MZQmggl8|A$~HPCoYJ`Hanp|;3EM!NSSt2U*-<0RW2L2Dx^2=A%EZoc~3%qu<$+0JAg5>7uw(?xdXl>zoZ z#SvZbHQJelX5|{tn1XHuYbMP2A)#!M+5WL*u0z6rN9oRKFbH{?CjqL!SW_QZj0+_f0AD;TJMe%`-^Wv8fI5tKyU8jJ0nW2aj;9Np+z z)z_38>oCK65=Hp&qK=UhO*wZfOBL4@@*`zN4gQmeu`X;0Frbc|V4tfW*Jby0h%Z=# zWqDvHHn+gp-^PjRJ(rJjf^i+pJu;dhzu1Jkl!z^3>#0`m;L$BFR6#Ihs_0wiM16zq z8HYO4F+$KYUH0^un1`S+Qt2WZG2WGY4-*@ z?TC`*$eJ*fQAqFLRA1#%$TJUm0-H>jd4wD{;H;l9MC+&`5$W!y&#YAS1n0n(PC8x= zB}deMtZ5D<8#g(6?nFHMBv9`@xjvpVq%~1+@fi{|as8|5iZ4#$y}jcO`*AU(?pBx8 zL`t((w$+q|Iy}yaZ1)rShWn%x6iH#I?^Oe3`?)5W>*%ZloIx6wl0dG*4u7R2-1E85 z+8x#iI}o1@4RJBRlKR08FqYRFjA&wVhWXp3u;>3B2&(GENFO03k`s}39suToW~)zuQUKo`Tf=$7H^(r4?yt*J2NLuL&{z*6p!xc* zy(v#;*M7u0w!WS5^8<0BWP-SZcZ^V+@iZh-?&^>e6+!Zpa;wNdV*u2hWTLbWnzsA9 zn|Tva^YqrP%;a6!@~kepFkwjR*O;ZIhCy{^P@0~czL43x%zc74_2DC0qP7K;R6l{i zcYgtDukK-B)Y9p4hpY3+Nhw?P5Wz~s-Cw^;*yG-ECCY$)yhzHjzST+aNtvsUaapF4 zo&ycTg){WEQMJNR5mL|ZXC;Cpl|pzMy?odD3`yAtlaS{GZF%58jgZVuLg|N;Is)~^ zh*&-zf~RFm*MOa(;a{}A+ox@9P)(j46$0W8XPVxNWJ?TE z4w((VXG01F+9mVOESeK)*@Xk7sw=5_6l=bBCEN-UBqEkoSt0~ON|8?1;Z;&ckzBE5 zl0c!MPmYF!C*cY{L6W5?MSa52Is)m#9>LF~W{~RtAQN zj+sse!T36I`YuslZfe5({t55ZPm^!><&iBH`cu36UhXv-xLeO3=aa9w*YIx4dcBMr z^C@=>hm!oEI;DK7W-C4G#L~Z!_KqNmz1@2yZss0|g*^ct+W+BpqP6M04gd8Bv+#KT zRt`!R!a;f6|Y5VSqn?>e6EC zRng)S&ds0VfG}d0;abxav zrVWu!>^+m9n5d_`Iw?Wh+BpV>olLhN#D$gfX=heytq&28!~82)eOCM6a}+}m^Kg2pn}`cvB=)eR0Q4^%dlmzUSYDggK^p5(=cO@~= z5;;THv=n-U)niS=R!kTeW%F@G zH{EoY`}x%BVFdt)4K19Ij37kahuDr&?rK`!1Ad+^+psx9i<0k_EcjncyrK28FDsge{poOrtTLG~OkHO8ui8Vi5TjFtu zsP`c`-90P!XkUS`3b5O=`W;rJ2t8|%C+Ff$>rm??E3~glWQtL=C*FBr-&iOQ4pa`x z|JUQ&{RiEmeD~s+MTw+8G^v&1(LDi4)foNmjF>;RzH?z?_Y{ACzB5LxS?%4eI7=3z z+Ak9?4v~2FBF>xfH=hp|@W%kYOP1ZA9HGU3Td$7)o~57die28{k6bj$LHH40X1-i~ zy)yf>5vI%{QgPSX_cflCHLsTzelb;aWr3~HD%@Q39f$sw_Hw_ypanRvg2PSc)>4V6TN_?ZKvG>n}Qjc~ut>1;PpTNf|u^iB7qQaG>a40T*k&;x02WBiOE!|aW`49~Dj zSyDFNhe>UOY=5^KAmEKwp4yw>Rzy zDbEy@zLm(RE>jcc$>gbcmDeG(ovk9zpINi(f4UVY_V!SUZARR|-zp2%9b3iLF#-2} zD+J*Qqay6O5(gix-!y|ns#T;xG%!BfXz}*%1u~1o^IHZi1UQP>PO{Qq(Xb*+AP5R9f(O!=5eb7*{){u47$MT0JPPt^z5C@%a4z{JK`$6YiA+}>2EKH6ES8skNbbc|nDiPRSB zIX{?$TgKE%k^R>@snBisYahHQnV*2hC7^q$B=^%{{yr%id*$W3{ddpg@hcy%sG|ysKygGs^ZSbQynmJY1p@m|;3vFRZF-@vf_ClX}b5 zKAzkO*cy7UZ=`8%j43anktjXc0885iD2{ieljxgPln4bf3Fi~-#8xBEA6&Pc>L=xR z#S0?e%ta^+DJ5aUU1gGboAgItj@{fGaT;hgG*u?Ib5gG<2JTW=`6f424d zeSSh5N_r_)StGynm$aTmP+R8qicTxfG0cMf_}%&4j*gXDkKSLKJRNl^un=_S_Sgo^ zp04~6qp2r{)=JplI!dk-89P_($rjJVhrr}&A}!4TO?n9f&kc6p@425++%|}U_v%d_ zH~OORVjn~Hkn~Jl8UFMq9EEw(i)2DS;HF0M49Ez(tejBvpI+yEwJ@yK-*vOatr1jE zpaUP4JilD&9%o0L8TOC*Z=zTQ(s%im{jKPDmF?7PSNOm{!%C1Uq5daIudh1uUhBmQ z;N3APUC)bHp*`*xGl20`*&VWY6eARxic_=LmLy?qV32UhDQu7W;f&$1Uimo8jLz44 zAUR~dr425*622MBcwF#f;}gK7S#JkS+9mm@-neymx~;5fzzYmUB{?6pDoAWIWO8TA zOfEUmpczg%tglMLLiLz8Cd4whuoGWDg(+MI0Ew^}msUGq zzV+X{-?Txcb)`&sz+843twrT*wUIC_UDv+kjnZG{r)=jidv=Ur6E=#7E+N;}mG)AD$0hRZv393_c%eqXB zbYHK)6~C^0{Gvat`uV>F<-#~MtL(R1`c&4ecg)MNSIwxhQH>#~ykH#hK1Bk-2-o*xV2 z%{b0WSSx*^FB>)rcUX~x3xn~#Q-8DMZ+k}^ZWocfv%QuzbMz@hn3vN(N*}QA2pOuYTJLrc-DUfp9rW20q_$=Vv5S^sb_3LlQLT=+(NC30;nh0yTg&iH-^NhpZW&W<6xZ+`rF zFyYn!7|)}ms%hZv+bZ`Tp;-J7jyqI=;SX^ZUZhN=2HN|{+aInGc2q8}@Lcb^@YF8b!aOZaM?Mgf4Pm5l_foio$8n?&|O-A3s`}6K!Wt zg5HVqr*xWNfo!^Nj=D!Fz#l&yISU1JXcQGixy4^}pXmhbK4) zd%JG9#F-7mq>`9&%dy{4j2<&qacoKT0+>c%LaJfzg`P*5X9UX)mbtLxlpuTFcU%wY zuau;){odMC4gXDS$CP^HOrPeAsYT$lkzofYG&YE+ob%~c#$c7gMAa5?cJX8*JB}pD z+Wa+5_^#BjD>OaD3+=dIa}9b@eA(2HM4?>1)HWSv23y~ex=60tIwAQ*V)zs8#l51u zL5TV_l~k_D{WqCU$&E0anNj8!T#tN!%xT2n>V-Amhhe5G`mC912-{TJHU~~N{*aER z@%YC5*;1c0qeA!sN#;xln?7Ol6(U)#>)f;a4@LGpcFi~-{cBvk^_id$g*=b@Hogf+ zw_Ha*kAaYTImv;XqODSUgAzuPcn3-ZlP2^t?l8;L$xB3!#P@D1n$1u%77yNxE?V~P zJhuoYIAA?nJPYz{fBLPvqOR`gw=w*pG22@oX#)ZRGM4x6$`RrV&o3~4PGbAX%R-{B zxv{WRKPopnyD8nTrTOeOU}47>IFg<$?m9J8zEnONbLA6w!3-kb<{lN<=GLf)Uzk_P z_>_@DaJ=qC=hZjJ>B*qj^Vm?)_vd+vlH4^PYi`ERjB>J`2#ZZ7@~;KORGAOPx8D0d zg6o+!5XdTS)<;Ub2G1yevu+T_l7Hi*0htkw0tZ|-eLCkRI*o}mYK^RrfGeg3pM;)u zeVBqT@vJPAYcOIlrJ&>M;8#h3F>%o6Gw%q>h2xxtvDbliMAv9Z>12mM^l4t+jUo*r z%~qPfVaL3}z(|Mjpnmc8K+jqYL3CX-PS^4HlH`P$k@p3(3{1L@+#i-{!NI zP)ZQKJD%NkH!NjcakM@~!YJx$K~csqzBH}DG5Laa_7SDx3¥+*fynaN$9Mv9ASZ zp9Xgwri`$!=by4Lj$sU1@X-T?*l@(QqvnLP#_b(h--JqntGdXVe)HVt5;cP|?AAb3 zByCHZ>?Qu#nDr^kwKCiUGQDOVn!uvTGrBbM!22n&pK$RS&UgS@n3UD8Nz}#h&lIL#;{%-w)BE3vp%;Z8?z?iGSzz z*!FjAL1&YhVE&+|$@5U<)3bPqRiAX`zQHeddYNTo8FrB zWPOjBLQ&st$#~!nQw=gxg(JhzY&(!`)OdYSFc)j_`{aSWBF3>4-o6KEGlNmy);3^SjaR~u$y@mTC>1Be1rz9**U-rkx#n;RwBlXmo#wP=Yw=~;Bg99q+drn2+ zKy$7loyKf#z;;ufYWFu~Z+e{mb3Znk2j4v{4a(Np2sBFZHjb;UGE4VfPD%$#G29>f zf+W=35&Q6%gmbSYd)r-TAqcLVV>ejpFg)$UI^f_kJ*A18#HkyMNW&aKk+YE57`Bg9 zw^#IH*2wr>0#u8>&$bMSHm6^zLsj2Q_*HutB%S%pO0Wn=8_SJU5S? zTNfI3x#<%NLN|pxI9Fs-XmN0GC{<*6imi9T`&)tpF){HVJOBH&ZbKgM- zn?P6fDRzQnK?m*Be^oc{5c8WReo+}nMapgxUwxBBys4U_3zrG}px=7>9XV907kG`4 z{N-geEp)cy*JqsZyki6D{=Bq7kK1|5UAz>W5xZFD8tvbbR8^$9E92W&tgn)}J0|w%~0HlJ4Vegof0HASWBOEz>N6{I0*l588vQD|V9L z?ZkZ|M8;K|4i&I>HTw;&8I`!o2A^X8(QiRnvf9E%?a+R?o#!1^28}*J)}J`1w=JgB zA5$CqJzFZp)dWt8i8^QTi(Y{0!cU(cTkXq+KU&)3zjO{ZOF^F7?|5SHUT((Q?B%F5 z?~U18O1M%guJg@$dvF!&lMqe40cLojr`R|m(QBn zk8Mu;xb-bU^KsXELOO`agVJQ`d~t8p?(@D4!`_5H1d6)MQXU$|)CS0G}-n)~9h%aBQ4EFrGRSmZc-HETb z25PJh!bn2z4?Gcf)}CxMYJp-3nd1uxpa^M&{Jrq_Sg@$X33AbGO z!TKM^c?@07ebP!%U+61SCqJA7u251Ab79UJmh*>P39Dt5b*l|mp6YnVGq*@+1bwH6~Dr4l-=%d-y%jO!>nSLFqh8q{(HhYat>1ii$#T011PfXDq7T2JQB_x~n6 zr#oX6OV^>?2f_x_@aVLHe&b2R6CKZNwj5%-MdPZhds%Yzj^j3_ZL)Z^h|C65RCR z{!T6DIgLHZ$-xb8TqnA+Qw`?GW23$7xvjI?C)H6Z33+ z|6<|B;FD?(8!B~l5EdzouKZo1q8^;{wwut;&s&6f-|MG4Y9)9Hi%2LE`n~O1SruPFEEw!$ zFS9nE_3VZRoXcgF#5+4x)o6ze{0j{;xPPC0Vvfj;idx#?*i`n-slodV*K*Lw7-U9y3$=L7H5!-((N3O3X%7|ciA9|{Ej(7b$ z*~hb@0$L(Uo{o~sBc-@G5Nsl4qs{zo^N(_mzUztm38rfuj=(Ykz{ zor;RUsK_<-YN6*Hse-MuP?n2t<0sGeB|41?y+($G3JZkVtDJUXYt`l=gtqF+qJ3MY zEz_-Ucr=QP;>57>7iN56H`Yfpy^;Imncs#tTu}*-AJIINAtF$*!&v<@?yiB~*2ZU{ zdUt{o;j!c;OM~SsSSmO&;}2gTw6rZiHzaU9cyX`lo}U2tw;>#?U;Sy@XVgCZ*D>|I{`m!7UFH`Az zNq^(Y`e8hjEco0@20foOPCg2oxO^9Y;0Uu4AC{Z0LeO63+#Ly;2_r!-H|BvZJl=bYZWp##o=Y=~VC`4m|u?59-%szblq zGdHn*fu8BrXKgwijuw()*Vq6ZSo5ZJT@o@zpX}IE%UqC;{PS?X-Gg>YO&bg#FDg!FS2ATB+=3*Saoq18uN+g-Wq7TCCf<8@%mVWs3t zS%q2x*g6&O2AwRkmy%5NQh`I4o`#v-wD)>5qt)U31*#RC-SDL>o1UV;tHyTv?}b0>5f|)OfsP%KYBItIega8|Rb1ij4f^F#Udq9v(tN?=C+$ z7gIWVc%%f$ngISal}BEEFJCshF*`UdRP}Kmm!wu%%B;#E4U7H54&KZ(qbB%>VT{(g zsF^_)&05@*B=9;fy0u$R|J=U3SUhJEn)F_mSwlZba!$`3%?+}68_XBv6cVUkX8~NX z1S^k#!vhn1;w8R~*F~!*CAKV*8ZGFbXD{6mX+!3I+iw#Ra=j;)>q6O)7NbGUr3Y5W zovkRx+8Yi5bnpGRKul|Qy)eq4%;WRxY@bEntNWDo`8GX&P`~td!+IuBn{c!8TO;cd z&gMOW_Fuy`8)BF2<;yuMLMpg5;EmwjT{L{@0y1}Wcrvah1oO48YE^#CSKb&pk!KZk zB%H}SyR%<6i3cl+JAm+2z>N|?E#B=|1)SNOZ`G>NsJXREeOPMsRAqCySNrEM9R=nf zcLDOD8{egIYSsAF1RT|D5}dww<@_>ilLarQ&bOiZ`Xr`>Gvu$>HW%;t#Ose5@63C( z;}lF39A5bBTo)Yl`ux^LDmR)gxQ+gpHLLWS#nYHYknLXu>#!tyXjs-JxH@KiGg)v; z^zBj$s2do6Y`1q1BwxuO!|m>~`~bD=<3V3#S=tbG%@WA`cBwbn{-;T4(&qcdBmZPy6i;H)eCTxhV*==%|_Q?oy&s&$2_~$(>r9T?|rW3gx#d%-h*do%d!R<^1N9o>mh0!WW8Y1lU&_( zZdDl(B7(Bh1qd`3?S(vEq`Zi z-=7->R=s%B;#hN4fP&Db7*aIa3APLAU5?NGInKUXKP?zVsZ7hLh#WX@(& z?9HJ@xBX!Y`r5fT(NbYQv)tlMZ<%wCG~~g^JGgtZr_`-7XR-gD9I3DD{`Tg=XhUTl z7ZF?Y?%5+(DgSM(Ol@zqg@(T(;(UBJGF}_7Qb1)bCATK4R~N<*Q$FX=fE_aGTf%n2 zR^I#@YND{d=Ee@=bd+=Q`a!Yu#_neX)1hubAhTVE4W{?%vtFGc@{! z*6W-g)>}-`bmaZeF|F4~o?QmyAYXq`xM$CIEfOUeCqQ?SSnC-g$Ye9H8A$CFn7kh} z#P_kK?1SYm#h5pr$@!pl+jrxxV1v*Vc`~!t#D*REECX+TGFPd+1Ze`TN4S;!`%Y7{ zXv6N-Frm4Fb(635hrgX2NtXHiYg}{9t!~fqmW8BhT=aKh-eCRv^e#$!w=zv%6GuCy z>X=*OwtAC0B%W6Fg}u0tWFBx%R+-AnrilLd{XuI=F!<0)XX$E$?Y-t5hqakvS&5Ic z5kB2^2Wsuo8*(W-0wV7^Oq5JS@+EWjctwggzBu>#N1lbQj3>ov-fJ=6M)NpN1wv># z=`Q+Kuksh_>9!OWB|Yo0Vx_s}Luxpe&@6N2 z;;3sFUd~bYpVztO+}?zX&1~{B?#j2k5HL@8HgHU{@LYh7nbfgS#D6Pv<|79V3bM@es13*9us3#)-5_I$+qy(;4epaI>&W*hGBz0`g}Oy;+Lqr&D@eK$yt9LmgD6k) zy=lbey!Dz!1}Hh_p?OW2x&dF9>PNr6$cmTFzoCfC?kl^_Z-|82#xOqYo2gfOr7QzP z_Tt{veu7Y#C_*~hF&Bw5&5j`Qp)&Wx_R0fN=wua_SuHC z&S_pegyqqBp9#2oAZxQ~rWLJhw#Elf5|E~NzIav;>Wv)xQ?AmiZ`RnR<0&smd%ius zZeeomeG&YpUHf8En7|rg%3ZT-o~(w>Tw%U$ecnsa{I%)<{MbTf$35L}rfkb6H@eax z^fbT>UhQ6d=Q0=K;O0Sslq;7r<46p|B`8=QcE0*=T7Q+e%u5#1-FbWlvZoND9rLf+ z5B0u@vD!ZA>!WAF($RcqhO}$Woi5kfWvu0NgGkQjR?iijkJhIXZtV%tVeRgS?YM=# zk;?E0$+MW5I=Ou8rAzctX|$4&;B;6BS>*4GMU|g)y_Yr6B~MR4{`ewkV=l!wwx=!` zp5s3}=Cwb&x3TCSot@$l)Zd!elPrc<=8sLDq4m<@nZ_RyW^%q;!9o4tui)1x%hk#nSfY zmmGO!Snv)%8Jw^F(!QG)Fn*A3CEpY2944dl(tlVWPB_T;Wk9qF0)Dd@R&;Rvwxtf2 z5PgqqdK8g6JyZVH;}pI6;I%&7{B^voA@0>2Hhdfrc0GF2-2=Tpy`;bd;y^MZ?S~o| zgBBw*KB-{>8{5BZSi3dH;5Ljm*_t8eU+T8GTVdq>(i|LOOw8wlOM(wpak@U&kKOBU zoP{@Ix4s94F&^C5e0_l59KD?EY5vq7#N$PJ~)? zv488paWO}62cewVA9c!`tXxCZRIRe*3+qIGhE9Hr8S3CIOWF0N#uAXSQn~gn2XNgG{%SN zMV^GotgN&m5M`dU?meW(z3@s_A3+D%(rNj~UD}TU8(7T2$H1=NUUz@)!Y#gk$+e<& zuj$g>>7_V!&1I%x%;<}JcStJuYl%}ppwHrs^JILS^kSs& z(lUt%Wig*V_&n_9DECiK)}Eh5cp?0SQ|wr7Sya6Kj$6Q+S)$00`W>}yrmS)I>U@a3 zfx7RX5VHtM9s|qD*`wMOtZd;01n!$UN+iDbw%d-iCxrBHoqnJ!I+Qw`SXx)~H$QN$ z3(*DrVpkKyj*Py(RKr647&Its4b3DKv4mai5dB5}y~2*WWEB?#FVRuQ^U$4vRMw8h zXE$OkuY5N@UoOSU3_TgNsDAyzVRhlNou0IZEP?d_-8utv5W03&CsU}lhqmN^CO4Iu zUF~w{_~Zir``2qavSJ{_YIK_Zsg_e)6n2w%^RYf^I6z*oNj_gdwCTJ2JBNY5wTA&P zYyR`-H~fyUujrt~V`a0&e2+N6vqnVmJ#5Dcku0t2S0MzRqZK1&wQv9Iy!VyC&}`Zt zi<6JaLQ+4c7XB_jYBeH3qjdJeM4?t&ixwx>m3^-ETan{HcB^Q6VkDEl_l_=#y!Upmy~jc~Z|*b;jqRwHs^^a#YD2+>XR7+l=PzB6 z1o%v}C2a2_A=@-a)+y11dz`Wwc|rT0<_C<^7)fW(=B0k;6!`@5-b>W9-2FeMyOj!T zw{JC-QRFkv=Dl$NUpUX@^#G9A=!FHc2xyGTUnR z*d6Jv_X_&L%B~G!EwR`V%bOkAKy4r3M9`vJI8g^F-a@su zpmxQmQEG3sLTg0L+FR_1En*~u-_`rRKi~WJ`~8!LKjKPqo$H+Q91 zcB3j2EUaawa|&A<$+&!P9mz_&A4~mkn$&KMp#oOA*Q2n50QG3!3e8H}nhF8L{zmO; zU;%f!K=6oysAVDsSgsL%v;ix}WmY&(I5K z%zVywYvCO#V`qQ{>kZ4ig!J8j z_jwwON7AGs-h?A^1HUj<&XHF~GUBEB-JPlR%ZnHl78}Mu%44wpno!Q)+6bw?_oXz@ z$%1Q-R*EvFuByt}vHN7Wt}f(cu}yxn<4GdiI<&AOSm>Z#cWa1iex0(*9s4QrO!0YL z@foqrckSDrsF<3X7YC53-PH7X#?drV&irN)ZXD&}cUnV0@=!kdrk<15TLm+U_CTR1 zgL~OYk2qgWdHSa1%EUIG3@DZh(liX;w#;VPxH+WxdEl?MNM6>wWjW$R@wi2G7x6$@ zb;|Y-kHnCy=mq(^C~QXzefvENQ;015qs!q@yS!^GS)T z&yMjam2L4scqhn0vANM7qm7GF^WIj%JkW4=^|4%tLY(|ce3m}NOY`VR&NMCX8|>Vp zJ%dZg;|sbnfK4rZ$H(Z*Y9gog*0zGvpW)`0^Eiuw{2#D*{jw~d9ATQ75(}3)E*Xh3bGOf;GUsrtUye_iWsW!V2*LpQ><*|nvoc|& zMY60*@ptW6v=ozB0SQ}T8`eI+e||0oxzB*vbcCrr99TAqb=kE<#Xl4ENjo!`1e!P2 zYvIDb0DFt#Ja!EUE#V=k9Jd_enY-*Nbpe}OtcPAdPKQ_^6~e45$< zKe+<>bg>2-BwGHAl*yEqY;0GIhoMj3ZB?wT&zoVme(RfQp2}Yww@AJr6n)E$B%rc> zL!Gy)vrx0`srIV8H$GN3A$p^q!weh}YPuLsyW9|w@~KA!*gP*+<`Ec- zxdrOS$A!-)EF;?&W%vHz%P)>Dde(z~v45_{hRGB3CDE+Ve}%6neXKyKJ4S56zm>Lt zl4>WmAd8PPO|mSQ9s*gjYNKQEts+0^;V}K=uMF4UDn^mP^pL6b&GaI-jNQ6C29*=9 z6@XA&pbq~yrSR%+(rptm)DM%bE>=Hwo~_k7Pl9=&FSpt#{4dF!tR{|2ZFHo(d8N%Jzy|{ucxwwixpwrmbBj_24cJcT;GrH#s}~ zEjGyq!tqdiLE5`e^5rtPL zQ3Ws!h~J&{Xh(HaZd$yU$B$hBMG4gFNUmUOM4~8L0#6t#nFqgGnyFgwsB*R3_9*OM zkH?v~Ua)`_df6p?_n;D8eh5dyEjL$?2?i|~xt%20m?0SMcv5~r@RVv^*F_YuYQvH7vv zaN%lpm)Sp!__SpBU9x!>7&v<0D~$BU3;l5BeJifpFB?>_lTQo3T#-1h$5^?HNDjIz z9=U9sR>}(PCevk?jkg<|;_Jm1oT)xtXF7W(8J3@tD=s6% zdZdIlM|G>qf`3>WU)aB^M$?4(U6Eq&JXsKJ|30ESpl!x8Rr&7JGl;4!324Gd;0V2q zGoN#3*0hd3EgQlpiUe8*#5qpEA-&-eRl;m~qAp8p&Rvfy%Wl*41YftGUrjXuXIrRB z5=JOQUbBgCvb&PmPbMlztS8pdYbsH?W1@3}Y+ewpl|sa&@y$5V%Q@rG)_C9Dgi{J< zvy8rRu$8}3WZ9qAXtJ}1?r+(;*l6srM?NPlpqn%gE%-|zD%>#F>uoD(H^s=yRs)Ml z*U8#$XWzrAu?*QZD)306!5(3z-l{A8+*4xUw{N_*tNc(dFxvPKs4e-NM zs-X{S*{A+sfNVX%lfO%r=a=)m+q$&6P+P95Je(MAHHOxmHbbQrRJ3x6gCpC1kBQH4 z89S#Gt_lx@x|EmbZ9I`&ZSZ_wO5>_N}K{^Rficr5)Ah#QuGSECJ@S z+={pHe!5^;71j~F!zW!qk(x8_{)?`xi<8t}S*XBi{(~?d^eZb2$!1VPF(!D5qQgd#WU|N>>%MSgQg-=s&3>9l4{Y zr#4Z|@w02Lbwk{@JtG`OdAdzVcs_C4T`kH_WkXK5>C?QxZ>Ff*=21lKCQ*0Y`5S*u z#fx3*!8nSv@HJ4u^ZG{#U zr%kl{mEkt*L!~oG>8oWX!Th6LTruWU`#rrNee&nzGdtq;8-5h0HH2qgYqc!dIFbTt zJ-_y!a1siuRog#~sw4+a z2YJu$KYhTkE&WL}cSp_s|1|db+Ly_Z^A}?IrzS-$qVa7hgbsDt=c>*S>m&b$-1Lx! zvx?(&XC}}M<-`jV?81pK7xF&-!5ynpxQe(cohth3rbEhkO;6YUt8xb#IsPu2gY?Js za(CxW-e9j$syicmc00haVeL(W?d;#Vgc|rEqX$BD_CNG^?=gB;K2pMHq z$6>Ce!iFqI)Wb|~z}_fi^Y zR%hP39*-H5phgjzqaNN?pUU+`vkciGUMo!Nu)iIOYqy0N z1TIIl=fn+}j?dNEpkl}$t-P(LHX3CMOUAioN4$;9ZO9bF{O~DoQNDc3b?J;ES)g)> zXgl>-B5Jq){J6fA{(v9HaQ~>(DS#gVt0&2-BvM0#>c<&u8)_wJ6`ta6wGGd{N({m! z4P*!@3JwF^%W8wR2@NX*7w2%2(kI5S*YFLh8nAWHji~YpPorbD8$s%l23xu+P0rsx zyzrnr4^RCuNEI?x`&00JL`W#j@}ZZM3_Mh)OCO`3Ewkb5?X$(67Nz$_15{z-JLCsa z-{v$hBkHe3H!GF3ckRp`8O{m)JU9gPy57@B4&!A`pAEV?txGy@92&62j`i5cpj}v~ z{rhiME!?;joNT!xl>w58aE;DIX9^z1Ic`|HXWC<53F=Z(9-xUOydh373>7$$kj@pA zIVJ3<+KgY%YiPhFHaHi)-&HHbq! zl_?{cG?VOLpzI^M}n|x>w^a{_o_Zz9jg^2prR(*7$Vg((v@YibBe$zbe@`Pyobq;WPL6E<9D&=qyvY#&NUDXpZg%HJ*5Q3_VgTTKkMaCkR0 z&3JGKYyBiw4tk)0KyXgfiRCnMh-w*D`5X{OiE^?pV=&jPuJh*d8sy z=$!YndDZYjpR!BH=Vq8rP_wKZV%%vx-m{Y95^q6Y&sVOCARa~r-^%Ayd#N5@V;+b8 z85Hf=oXIAJ2KK8*8F(5Kdo0NRnD7|WNxkFZbJl19-O6!#Z^We2bJ}fkToWX;N})!; zgB}^?&Hcg{F*fcplhiIEoceALW(Lkx@TT~gd)Qb6i4p13qAKWa-*5HL%iX6`%bgq3 zy0cO&zi!%fJs?Ws=_#0FZh}1sLJaqs2P+ueMM1Mm#s_s(Epk`lv{};m!+SbFR(m}0 zj%O7dZW%S%@`2QMWj)+Krq+Gz>@(?oy68fw45%teVv5UnvdNTawi5PVZh6}$lFXD+ zD-=EOD&YnV8Trp$UH#YTkc(5;xtAn}fpG9)QLjR2Qh6No?scV^*q+-%eWQyiy&8ji z1{Igt2Nwo)<6k9SFN6=Bt~y=lU2A0j1i{IY|G1L&r_@~Q6M@~aO6%GJ!;atW1dy~e zs=+J8kGd01zxyJp1W=2KqJB50@(w>1S$ni_tExE7gl%kd9xrr3baLs7UiOtNl%#x5 zrSsDMh2#Hhj$7`mCwFAI^R3Z>U25<=q8F0;=E>46P-D2wmlWYPE0*|ChB}5Nco(^H zL?U5G!zy}irfXs=x?UIse-&@E#&()HEd;-_(#Jw|;#=v??efJfu4BIusFiV1#w-ux z=d`4wF^29_VwiFUct&1tX|1;Medoe7c9$TxY-h^waIc5oI>o{o@lb^1f%0%P`-v|n z($;o9j`4ih%ZMf{(VRwhlTDRtp_luH>P$FgRl=R7ELQ6-^?2@)bNlg8Og!CUpKC{^ zabw#}O_>!lllLQEc7vR=)3WSglhFMZ-6798DrDEEYm)Wd2GgF8gj&bP7xOsnSoD=u z&h~iZuRFiXdcTtx8zuN88`~a{%LZ{4Z;qqDXeDPgpRL8+cDaii+?j3B*kOU&qcESz zH`0V{Ba+6W)T9(fw#%dDB_H+cj0UuhP<6~k+pAnSivUDpZ~rpH$Pw})!?`d@u&O#8 z&KspB6I9}AV-43l22xGN`;*n%!5n_uvC!ZEiSg4M`qfuxsMU(}lLfn}$4hEY2xdH2 zKw3!}hEnt4Z2U7)GwffEpsP60TTlSv&Dv2;(~7M`eW3*IJHN#bLEO~_xn38%&N^2? zeuFfLn^xV}>=6pzuhLSn{7|L-(w=tIiAa{)>=dF?#-~-ZtLJPbI98=2*u1rxUKpvO zvo1g&C-3WoF1-&tcmHv^Typ_vD}1>Hnn!zvgvtZDj;)n*x9A#sg0GBzbsEagZJvG- z043Sm=Z1l+Xax>G9Z2+gwXpi-p#={oc%#l;_n;GD=7-YqsoZUzL^3^#6bM0(^O34Q z&>jnnXaCzhl($lS!A$Dq+KMDf-1F^H5DxX&Uv&=~~IY zaDEzzr(^AX3R1wQ7ap{8EvJ^AWTHj$M=m-7WAj+4(|aNYT#8#=E?0#hYB{+FuqQf* z@Zu9k-7xqA-NE!M+DD(2QXi_++5g$To& z05-&;y4uC3>@=Wqrl?X_%%2_bm7u2^g~sY^CpJM;R0t1;EB>s3PoiTs-abv-W)_k2 zS>XCcYhQNsgWMEsKz7fBH&*Hv$yPnv2ua=*I%Hh6b=-ma5yr*7-2GEiqrJ?XY*N@i8;h;YBz}Ub>X+S{w>wD{00(N67KNu|(N1s&vQch3H z>KdxlNU3X&|JiWn0&(XcH@`u8B(!t-v!9I9#xFmCQVw#2dZyI_I(bj^M@4_q+4s9p zdP>*R40r=in_ZR`QN=Gpl8w3>d6O`-3%HB2;=M+NkO!;bgPY41+yvf)nv5f}S?TW; zj6uSrqhU^TQdEO-xtwl3?bE=&|Mw&l@1EsfVIRVg7mSH|#N-%xlf2yvhELG5$2VJg zOWt}JeI$wWdRP{$45Z_V*OURzf(TX=1B#nYAu>Rn`3AifN}S1JgAk}aSx>k1wX#sQ zkxAKaUPgvHp5Yecw&zRedA5`Ak5iw@@W&C@~MW~n00@|Pb|4T}^Rko&(6)^w# zO;#8>a&7@Ayq?PPGD*_|7ri0jN8QuEYKZAZzMK0B_YR(4!@B$_^x5n zbR6yr=%g}E82Ve!He0`E)*}-EHK!_w5A4tEX6Lb>2QoStDBe}e@H|Z5IAwn+tu(z` zQ_Q)v;@9bqJDV}NV%Asb$~Z^zl_U@F>UVU9Uw3}nRq=9D>UQb za|?{OpZ5pT-fJ#OWf5#W65qf6gL=oG1Bu-iy1+SMfr5G%QVJf+g}lh7Ta2dUO%sij z)AaI?JpFE+1A#10Pk*nF)%cl1;U91uHj>yjA)2x3yIL_EQQHx@$$(22WcBI{S+uAl zpw6_iF1?LBnZhIng9VS837yVM9mnlbLFKlAQZER`2+WH(k48mn1J!ps-g=UpSbm;L zt|5pA@5d{JMe`u)_!n?TvX6aYm}YGBW~ed?KecLQV8RS=r$R3+Z20j(qTJKCh8%at z#OJsa?x-%1yoqV%x}?uuEjOwdzZGPyqd`yZvO*I|#p%FZmE|OC&ACb#gq`pAT|^}< zjL9z1?P|dIpbBzQ*`upAWSRH1s!}8<`$ko#zlHmh&=LpYzdq#cwH1 z8X8A5VS24iw0*HxaQkhum&v<=bya++A`+(Ol2Kz27PGKN(LwqUpFd3D)qIO&1QcB7 zsJO$$b9W)`JH68I;o?%-X_vkX>NHR&4(@GMm~pbT(0PnxR^6^Fn4XF+n4bM!Y0_yR z&=%Bx6yAOJ?zMtP3Z3x=(+fbsN40fcVKia+S0TA;mj;4!0o`0d>C;+}ofk^* z?0+@f)?IJ!eP<{ciUpmzowLGXW4^#+4%w*W7A8+2v5fPF29lG#=ErZQD{PchS*1*F zWv}p7B4cA868Gm8@%hUe16Q^#*Geu4DSXygnuHGB1Vp5sK@K3gwmzVb__k(*O?Ztr zx0b4+%MZQ~nM*@WDBEo^OX}t$F|Bh=|CQu0RlQEuEm;f5&$iv$^d~wbzAE3T)J5Qk zdAS48`tEh^OK^C2@@-=Cd~^rcdOWcTLI(Z0&+mX{^>(8*HMG4PVOTHYNVS?7nu0j1sBq}?$a z!-<+EwOa~ZV;Zp!S|@ZS{jlKH^@_)pzLRb5m1zG)Ihgt^8!J+smCn)1G+jbMREy8l z_l4)rEL*lCMVjxRgYT)D-8;4HBMZEvlw>Scyc{GHu$KX9OPu4ApJtAu>;pNL*1Yqv zFpvE`FFi2QglAg!`a^P};QcWd0DG2BP7vAaM1DQ6k?@R?=i5@|pg$04f@lzALjuZ})@ulQjgVs48@U~&4yS7p+pd$!PmOEv9C#(>ytF>u5u5+XP`ikONHy$;CZFv7F>X4@H&Hj~pM==gH8?R; zBcV%1BEXtW&X{P^Jvi(U(gpYGQbPr$g>=N**x56n4VECaY941(fV{_tI45G)QIC-1 zdsqSumYmq0#|!YNpE5@t!>UhQZG%68wYsPk!ut8T=zXBc`WCcor$Vaqw-a1ZP*9wV1^3h8fT7B>Ie2l^iV6g zNbu5;XzICk?}mz^T&6J$#kSZTm)Xi1!o%6G$0s{LHpG)-8wAba?lbn3|Lz3| z#xSErJP?m~nr!Y`_R**w-oB4vQrw-HZ1*8=j$-W}^e9ux#KdK-*G;YIKSIuRF1jC9 z3~`J!M2Zm0B52EnDmt!enl5~SE}8JSEyYA%_LVLki)w%R$qVa!M{m^neDX)1t*ZRi zfVPl&wW$CO^L~eTAm*lNO=F7=1&=x}eJEZXpM6jYoCDNUy<6z>RowJv;fT-YW+Tj` zmj(CUb3ZH0<$_GtAytnNaZhu8t`-ren&Phs3?TlTa2d`l8d-*KUQ_M6JSahd%HNHZ zP1106zl9bXX>|?D!d`9Vpox;)t?(6lm82O< zK@A-JvSC!UbS3^7u@YB^uDcW+`DWQ!he4{nvOpt3WGFV#RQ5=w(rL8U3!|gwb^4R$ z@-Vw(YqMz`nuQKaRw*>Yzwm+0S{JZ%%mDxW&fv{H#dHCfpBA z+ieFL`zbP2ZC3*t$eE9UN5WjB>mX|#$i@R3vKzX+phthGG6Ac{7cE-odaA3TQ9YYn zs5F7a$>-61b$bZ{933L+HC&$;729w%S#IrGT5l2g)wU-tXlWMy^yMlk9$&-Iu zZM)dx`M2FWm)XPG=~J^|Wc-pBf8cT;1c(<=I=uMM{gd|m6(QQess2IeA3`6Wzy}rO zCy=zqHSTg%y|{NN$I{D1E0SX%_ozS+?+xv*6HmsoIXCn8A1HhRXWy>i5&?xPBM~z} za@pjdM7b9s*QOj6u7@=c%|M@WyZi@x8#&IRTpJKxzlfa|bRMR)MobIbDRZo06DcfSw|>%Xy?7}GB+H?>7%g@p4OpO9RRKCTp0*t8{+&% zt#azRGy>ihybj`Q%N>n*BM+%xGt|M2Uj#?(kUKyprfBPe4$=idwJYf!PfUBzyH|{v zbjbLKrB%c(&2iht4+x4na8Ix-=&CDaPF2J_PwW|q3nabpN!>eBsO?~@sJ6IL5Mx(X z5vVD*s;PnM0Pm=Hkfn7io7Mz_oJ$8I6qXYUl!$heZuz$6WyI$iX_Iu>c`+jH8nX6A z-|RSf{zS@NdVj*rssCYfOad)RymZ=}h4YGTBC<)}6wx|TbQ+(0kV5*&By{p<{ioQ3 zfV-h-wi8}|dZh;*0Vn(DyOTP9?19gvqx*dC_ZWJ5Ncbc1Ho;ESfr&V_S>~N}osMs% zP(%DFv;FAbf*d11R>rn`@yiv~BXj20BzC^g>JMp%fWI@%{L%ISdLuo;V72}-TgKxt zCHJJGUtL7|s30d!cwTCyM z)0ct@vwX_X3AqMGaa-Mrt(?FSejok<+%WyY3(Fxdd)Jij!pWr^j1CDB>T$tme?SX@ zLWB+SHnG7`6vuJ!%rtij`iuwO3Nh(!6lo^q&C7+!tzI8fTuu2!nN_Uhg-Dm?# zAHk(kxy$%0WdBX*jl!^dZybs(W&Eco{wmWMTrR_Fs5UcYqnp(VoK;H9&>GMcNMF_pefCw6w#Yi`- z@<7=g*~5Mcq;ux$!w1|JF3m&LtDH^eh|d+BCM4hgWKzsW;B5?AzT4ALRT-Y)JwMxw(p|6AUJO(_M7E-x`A(vpsI{+G1l#x$T<$#cEXq5 ze8G)DXc}thgVatjm$jW-CeO;gJM++@u}6Z2{{=r{%Cw`*0nJ5p@TaZ~i9*B4FTn|d z4|!JGII=p=G+1#=f^#+~xKECZYtzbw?8q0xSMXA_5-LEjJ_(O&)^0F#8~)}KGG4H6 z1RW%aL6R5mVZG%Glo*?{W<~;FFXU5di@b)D*r2s*o@*oevC@!fi_A^U40%(i#oIn+ zSnJlE<(^ulI`Ow8m;A%u@B9u&OcO~toD&EW&yzw%Xd(@!($!(r7HJr7dX}7w;l~a5 zD)aP*Rm0VE&!=^jV|;aHerAUGaYQMqx3F);`Gh^pQ_8f+ynuU2?YP;jy6W~Oj~q2~ zogiplOO2?iq~Y(Y4kA_1W&J9CrQPOP`Ng? z_n0%4<`%8qDHybz^UfQMIrFOU)E<8>w?q4o_?@VO9u$dBkr}JCj3x8uJt&@-Ymrl# zZ%YPEN)7X0uqOz@O3v!O9v534Ws1aVg(B7#ZZ=1DTVdxl14mw*x^zT6*f{Rsd1q>{ z`&@o=>aXyk8C9b}>qvltSDh`e`0d?xpgV-Tar`pLiDY#*-k(hkAI2F4jtj#T+JT}> z)MFP9R?{)3L0ykf!O8Bp*2UGd5^4)$4gL+&c6 z)&pG!joo5Z?Fr77Z~1ff{Q*)v>6^;mp{55RGCDPY@%-_0V!-5Nb5UUC#;JZ8(FFdo z4-2mjjjf8M+0$Dw#*fiJy?N{vhs4L^3>;2BG#KpOI=s42%Qd*6H%x3D&-~{08L47& zJFPI?dP;h*eq*{zaLj|hNBc){D_Eh+V<8zc`4ZJd7Owc6b$ePJbAVoL-iiXPf?uuO z&?fE4``vVP(wd(QbiX^3Wu)twBe+}Szgcny52Y{*Gx5+oFQK)6HQv5|SQ?%uV4wDC z$j9-9`SX3p8R4Qg-+f8%N0i?U_HZf5JIV6Miu7PD=OTy!f?DbB!eCyueNIoKag1Vvw@j7B!u7n(K=FR^w{zErI8(% zxzDtv!1G)Ks;#fNR4QapgPz$RnU2QX+ZKiwO+@uWlLE|i-UK#nx<&mM>v?&mHA};I){owtx!sN09TRrUuK4lB zV60n9VEK{1ZY!(k{T!4_2z)cw4|yBkOOav|Kwi;%##0wP*E|m|9&}wI%tLPIE2}~@ zec!Z(_buP(S4UnEemy{Q65>1m_b6jO$w~2eV`ADxMxw?&GBWultG*EG1ZUej=Z};g zbjJ{k!ia}udN50>dZ|79owb)^wey)Lj);HCuYZQ~U=n5EnU0%CuBAXl}I2&o6bZQaAfuTupTt8&Cbc zGO+^(E-g`h+j=ZOI7AeZ9@rd=KJaHeZYr{lzD^q*e!1#e=qS3lRTR1Dk(o;LOtSJC zUf-B|FmijL)TUQ`(2FB$|NGo4{jrAPx^>4pb3RXD^zZbZK1uSJpmno&=QX^CILE5J zK|!q+ihAar70ih)Cp?kX35@s2nQ9b;ZL1TB$%G2EM=XC04uoc7jt#NyG6m5fG1UxD zU7c~>|5~6PP`Fg|u8o+?Si{BLrq@!YSvR*P3k{`${$E}v%J)WhH6Fg$9j95abIqIh zU*0Ku5((vk4D;fDM+wuIo>NbS2{bc1Ez`rs?AatksjLE--U5^Jz8<+64?B;migour=l@OGevN5k(K*jJg$eq3HE5#>q|oVKA*&};DepeG z6bq@Z5$iUWx65kHiS0gy=Q}2bYE8wpR$d1VsJZ@&%{Gp9K0P#F#<^}pYtNCdt6KoF zfL+}_OEgrH_^2xmRuPz3XR*{H&yhLXC|^nJ+#UI^HlEe$aZvrgJY&+YB2Tfj!{$n+ z|Hm^1)w4V1ZvMX=V=&nG1c8?S?PtT{*gII;AkLAQ=yk{OWV8Da`TF8CEBJ!B_dh2Y zQ&GS~X8p&1)BNih0h3kW6vLeGrri`42wS1Tb~2h`-CGuS_WgEYaR9z%OnrhU3GD}4 zv3OPf&pzZo@0|bntDLR%3}=VkuK&+O<^!(&jS~9uohh*YV=-HNAl2UEhIJARV5k&& z=R@KD|D{~$@qfM$7|Ii3=xcKS0O&s(m4EO3qhW3jv;Y785AlxHf6-4s=2F|l>27P5 z9Docat}+CiEQVoYBuq5QTTO5PplqfQ)1qs-eT~L@4e$akHa(vn21r;2-Rm*Q(E#kB z6!6Mg9}jT7Bjv6~JVl$9H$N79+%}Pt3*4T6pZWC+58REs;CFr%v-G?4K8tF@S>V+^sZs z^TPh4EYr%LIg@Y`ktTtJRYf`7(311C_3e7JI*E9gZr_O0&I5pOLb8XWS`L^Q(`31m zg^7G&jEsStZGI-iI_ z{~+8dI~lS{2CA+M0VK`T&}+UrV#@gd8)ro>=}m_zs|nOU4HOaZ*7LZsiGMFXKjldA*TRAPJ_|0GKH{ zKEPv{RRetfSmwr8;LE^kI&Q+QIlDf;8Fo;KVq>(6{D;t{zaj6ukffy&XpehB#VVt} zeJOj$*ZqyXn^#1O^gnV|`YnJw*e&*6c_Lj%1pHVCu-PWAuBS}DdxHh=;$&7+nQwnA^!XAV`&u4=_`N2Lzs>jCZIKA_&Wqis-xlX| zPr;Lx;nIO059`{Fw=e!P@NW!<9O?}G%XLuG!o5I2fP4D3p8 zQ%|p^T)fy)=m<3RoSKXvNCf{wcmaevs(i0`9|?xflDl6`yyiScj%Idmgg9KBliGdu z0lLlD8=>^a7YFfV1OcF+zkW4xsG43S;xNkpf17?9#ITcF(gbCq%Q!CB7C(yI=pu zIe|7^I)+$N9q`z5da943`mbWE1K1P+EyGYe65&~NmIK(Ik!W+)RG}8U>%-^+rZeE% z6aYqLj~oC>(IO(5fk$n=bMpdVa};Q^Yl4C#28LdWIoZGIl86+rBOZ|-8ox>wQVJy9!c;`_b6QYS4FWqBZ<6e+HfjySZid0Rbnf6VBggu}Sa;29W+yl&;B$aM@Rtn^eSoXma;p%J$PE`ystNl~`Cxpodj>Bpt*hwPK&`*jZro9nyPM z0^7r4YlWtTkLj@0@|-@S@g=Tkt`{O1Z{rq9>m^^)!W$8u(^xD(mSx7A-%r8Qp!~TV z@>w^lPJv05BdEPM>U4OwX)7DRd~1HI_XrbET>)(I=RUxW${fu%q;Y@p9|#TV_O2S# zwgA72Uc6z$Ioay*ow3u6)Mtj;r{>(NTyDGUo_ev#F})XLv6m)@4D~prZ|Mjim9>&iS@o{*udc+=~-M9bEkXlu}j8>C3R7! zV>th_ebpgGH`x~I=t^7hi5EnktcN6&!S0W+Q-JX5C3QUOW;l_b>(A_eBpCD_*tViK z$B=pxSEP#AK?8r{%v-O%f6-*%RF!O%4CuC75L9oxu;U&RqSIB52Q>u^7J|Bh1^x~l zWn`vYsz`bDbBMV=UqObaLEm;2E9Y+G%XJKZ$)cYiWniFbuFRr*y0s-6T2v^KF{{>U z*2uo2u@{y)PAK6lhT{;K7wFN%3$yi8oz_k_Op&O%GKam?-O@-(=9#-!{{dDsafL4m zDv!R^v)YSG2-Ge=Gwv@g7Fl!0XS{3}w9L_H*Z~lh7WO?j=}nWzuZa4C1x0>`uTF@I z-@@3&@8tGqmCmcJg=+t2TK8j6>(Ysc_oTkX!VG|2H8-F8owP_17>R@c z0p35O$iFK*G~{h58Pz#RAnu}T?dO;VvnNpcC((47p1aPAq5TgE9*>_GDmMW7#l-UJ zfku~DrMag!qo#A=QV#-l{m>es{bFwWr|TAr4>{+UdgC2NB2>2j!BqqCx$Hax1YqNr zU3(lQ*r*qD*`_Tq^7weam%5ploKA+*=<;x*oZCPyqb6+*&14 zrr*vh0CedgDJ-c_OA*F;Q+Tlh)O@l8Qv$n_kE9j+}a<7}F)<;4p@ZFBRJiCVq3JwF;o z-@H({$7t-5$Z?ZUCs!4lK)8Nb{|hX8bW$JaQ;+tHvgA_2 zN&;~S*ni>FEnDXWfyTnd{;YF+fjm`QsADVXNHRjvVA|BQ$2E^f;hE)eYc2InfhTM* zLa%A0Cs76QdkL(a{TQ5ytg;Z0)(aZd(?w7J;8-isQo6eMn`0rg7A_N3@4VO~t;jP8AQo z22rxw_F4SQ*mW?C`k9!qLJZ#2`-;~@|FHy0l;C;LvILBJQ?n;pxg~_U^Kf#@oil<+N#BBtmUp=Pqn6xI-cbsucOiq7FtfM zN^`Bc9iFXT`b?L$un{o*&jwP2zyaqMIc$T|WJ3PYx z&NGD-%DjDO%>`m!ZH_L0K97sJPkT;BD3)lkqypCe<8XuW@8#RIrGeiCT8cBe!SStf zUVop2DYH`1vJI16?u%J>I$t51(=_#^xT~!iU<>#raL5388?%Tss`6t8qU_< z<1qmAJ|eKOSFovTHY#&n%R(`*=be(c&jX;uB%&$k;0o{(pH5FlzS=7h8#QDZ5+y39 zOp4-k9T)IGo^r=G#P6{2UfGc5p&Ss!a_zPpO*h3EUP%7$0`;Zc1b($uY;v8va0JTIPzHsZixPyvzgkh3VZ8a!JKA&tXkfhrV-W2|OdN zz>PUBA@)eAtJA*zPrFQvf6cr3pm+2NnbtBumRc&Z(L0s-gGZLk_c{N}v!kbPx!#~e z2P8$iB7s7Iu)D`@%P}AR_&e_m03c`B&gqYSvB4=&)oo70vH%5SCguigt&n|N*^tS% zo0lyu$#E_@Yhn`f-YMi$lVcclYfQE3)>*#`IADEp;0?S&;Volwdzn5Ak5LsR&yCWgA6^0Aiu1_YWpqazJC_d4dFJx074C|28eLK5W#f(4Mbdn zogZW^L){wX&lYJHA1+;6=)iO@y#}v1<9^7ef#?!rmKCAK6FN8^dRy`%N$wj|5|{*B zv7g)yb#^_#jqGp|nKzG>U8*O#6c$F+4XQ&2BpeDIdvv8_g~G+4AG#TD{2_ciKf~m6 z{Xc}AWmr^k*Y1Y~k(Q33LrOsDl$H{ak{D7%K%{#ZKW%8U`4K zI@{+u&wI}KbiT}|xtN*F-s`{awSM# z_~>JqbcG7t_IJFqDIUmYWJPDXZ3Qd~+1<@nK`HxnS>&fw=$#&QDkCN+U1tOUiD@ow znNYv`^>jLK|2wC(>nJQpRE#S%bVtQai(SrFLAEQyZETlzPMLlJVq1ha6bMWtFUYyS zq2`x=xRdDdbw5d=)g@e8YzbMx4Op(z-llV$Zu<`tyW1jT(UZ>hQ~a0bVvp(^M9f2~gEb*d5%+c zcW8oWJBO-mqO6^JrN~RF9bTtfa#+6)e5Fv3VM7O=@N~1}gF8Md z<#$tSj{@*1v*oGGHu1gL!_nZg()WAp*7eY_#An0Swbb22Udj|ffKMf~bd$UW1yKA4lF*KZX= z)f6Oq#%$z?j)`9;0_o_mh9Li$%}TUALS38I!n$ggk-q8%*hL;7{%sIsQ~<@#Egs)I z%AOSTVex22Bgp^E2pAuML>W$OXZpVaP6itfR)@~Pq(nM=nD2#j6HZd?b>?EWE^*_a z^MOvbs3tebO9Gw{=_EjP&0fZb7a^l1@O?Tjm&2b|;)S*ac_2kpgMf9s-3g7`;sXt3 z^^PfDG|9k#gC2;HQDt$W4_9gX z>~eTs)Z!&nb$Q6VC{VQku08MUs)R_4%gLXXNFR`nc-Zh6namSHu>Xj0Zf<_#p*eT! zU7nWkB$gx((bd){@7*QwRZYKptP?Zd-0r6FW*)J$xoC6C(9 z)&q9mQOmMlIaEoffI4q5rt)O0iCDzq-01MjjZN9p%BKyyWCoO|E!6`PTC$! z?VxyR0sHjHTW;#s&1|*aS7E+*Y`5m<#U!UWxs7q{jpCP1Wq;gdhYhnfIGi>IK=mhV z!}L#boLH-eig4zf=&YR=e0OX0nxhy0h@9HgozCZF+gJ*`$LcQD&B24IZ-qaUzMRoh z%oo^zcV20wjtGu4BV}6PB&nb)91L%k47>pY@LS$_wDg=FlQk};@8%U3qi`zl94@&I zGb&5P{THAv>DfP!dxWZue}d7Fk57x*2JY#vn#@?G$~oKOat|vx$6(A-rDrFk5HDe& z@v(=APg~p1h%+>C9klVVJf(o({Y6unQKL8SYOb^LPsG9eCTY3koBvMi*MeF(>;bx? zp6W8TaPTas=3i8|6?&jK(4)(YIb@}O$KVO%>Sl}nXQNoAL+cenEK+y;wGtFcHz0cP zB8l0A@C*i*-boPq&=_0m2Z>7lQVDj6aYJCcQf&VIP*$nbezhWqn`$4ZF>i}&?s$pI=dDf{f3-!WuNy2Vx5bBr z^G5l)@skM*rK;0Won+o$(Q9~amhtv8F96dy!Gp#nL>qfj6~>a=&7rcQCts(OP*G?IPi8Agg+*8-@uy@p2ODuc;TA zB8&MvC|&v@(jv}Ai}-6nnHxLU#OV@SU-KJ&HZcy880BdqxyJ1ZIZk;(9wPb|8m6_S zlpm4GA53Xz*d>|WE>gKLlpLj)e4spkrbE>(D!X927LW0G!fVnXGoC?XT2n-;K1y0? z4+AlPF`)!yZ3Lw(zHXL~3v$->{l1xE!sc}5?PehSxB69>W~PsO`psZA8;y&01bJDo zplBzChLXXgM8GG>7oJi#*CG!$7I~zl#CX5$X z#oVriZVVad)AwZ+zkE}yUtY~>`0VIFNmfg9SX#33HIZTGzj&|fphcY}A`^?wuH#C? zzDp3Zq5ixy`r#6z~U790%7V$J_z=!nwi=rWW=yu5|+F|@Ouv^i&~fF+`p zc~KB4e_(6aZfTi+Rq2c|`{Tmsr{d4trRFNIDPA0_v`Z+7*gh%2_IzR2BFkRNrE2jJ zy3JOUG>HC^t)>Ct;9U)lA%WNDAFI?VU z{1%KANy3%N^9SWJG(^GNS#`;(d9k!WB@fw|YquDR))C$I4uV+6P zH_p8G;?V7|2FvkHtO4IPBOK?Y_6Hk&=S|OX59?GRDJ)M%n^;x_QB4q8GTR=fYq>FP zz`6fG#8M)s%y2IKl%VUi1KjaUSt7xWb8`zd-r@dBQTR*!4BJfW zB}YZ^^A!+NZsKBMg6VwoN8C9Ih;XO1RRs-v6403I_g>N#ww_3&}G;rWxQ zEv_lNWDdRNzpJY|kbU{sN55ogi~0K}Dc&%bczpJ9{K{(L!6mhR&*=4M>A!u(wEMiS zR5O0g=`_t(2R}xL!!$XQe7c8NEczoArfpXDv^nvCf{AlK@%Jw<(b>V=!SJQ+pP@2K zEe3F0)Z@WexkQFUX2L_sj6a*}x}`vO5_4YQ%^0l`uG)@SY`^z3jk&(*0cO7Y zF#JptcTgUqp_Y&lua{^NU&D~2MZK{v>}-sEu!bTXw9VU>7;_xse2v%PFUAwdkR>uy z=c7Q3zskJ&-IF0xa?xWrp-Y$T-d~9bbN*xS7rZBIAv&4dths%v2!0u2E!(a6zw^Vy zWMe)cb5C#HFm69>^>H_ZagW5Z$-M_!<#ae-{uIlX%%n0)KvrO^DlQ z!yB?U;XmnK{*~F?B$Cb~iOt(1_Qqn*m9rVAwfe9-0BlL6j#WNL&w4Ftnx?vyPgSRv ze3nMx_{-RiNR?gH9#;Tl_02)prMjX8@%}`>Y*6ZG)JgeA3<+wer)12S@9|?*t9N#r z%AQZKe8tmd6n)(4iSWc>?UxNf=9YKbiwx+(jg+Fl}YX^&TRh52C;owf;Xpd;x z5Sflnl8N?i786(y8M`RCRATy`Ne0#NsJ@x>@?cZ-z*72Ht)CU@F-3yBe^~zU%P6ps zIS&j>-#yi2DEr1S86kBg=T*DI5A|ObyfmKc7;mWRwpf>c${7*e_4Qrw-~f!HdJs1;A0xr-Y4{VE5;5yG zUxHTb17>;51d%UY?24UdWpBaf^<&sEv3<{H%*%d_>{eDTHaHmW+;^5PKJW%HllKXI zWH;CkLw$(x93&1F3U-3+qqUs0r6P}hVEbSoE6E}9kN`}O{!00Hq#bg2zs&jhJ91fb z9Ot0(fk;q=HB0cRc&(_Xzdo>Kl@dQ*t|Jexjf7P>d!N>75|jCBCmUw{UWugK!UQeG zH)rKc7Em&M4W&}Br~!wrbXlMcp^vAEzfaXeSEXwUjdd7Q)Kh!_YnR+9NBhv8zAtTF zK^6H~e)S8grC7nAMVN0Ry4I(2`+iY>KXiJ|Tl7@uL!Dr?lp2Z0L(rTT=xXV5Nrsge ztk^eS$SEr`A{8HeDjLc%YPF5qYOkRfM(n`%VnS|#kzCl{yP5D+0QjT4@dxrx1viME zR9T@uIw)y?EV;ljX@Eqvw9-`qwZi+7BSe5TALjE+aXO=wEp8QZ+n5&5rw+-v6ThyC z(I5n`h~H_L+}bPc4#9?wCVDP|qhqkJa6HZj(=$YtQu-bx7j|%I0-ZMO z_xtZ4(GL}d`#mWnMRrV9;iua&!t{gKUyWp4RFXX|*?o#qq_aGoF|t$Vz`{JQue>!l zQpZt7e>Nn}t)&$OR5IANl+edJtud_%Yh9V8grIu<^Gc?9qxQYurPiNh#XKh{&e^ls zDn?V!(+~kI^tX-DKOhGTqD+tV!hN@NJaOqv>h%dfWqYsfdS-Fi zuJML3xDMoz{M6*?C@;oQ`9vfAaIJ=w#?ty_Z6r?kD$e8vPtOp&U=;?P)Azr9_QLOb z)2NFU%pNY97c-v!P=V3+Phm@7tsY}Sva!sy5g9?B(@^O`gFS{Re8#o)=E11v-afR4 z#PtV6C$^5*9avE}&gSH;hHWE~wPXH0f7Y&7?vHR!Y{kX_1|V|_EKejQ4SFaq)A~3V zRr@paPRXYrOta>}j42Kaiy%8D44)>5y@q$d$fDm6#2gEEYon71${Aq00C{43$CAfW z>veJK!YNCLpmtyelaiAv*;&y1AR?tW<1BkehGD)~NSFjtiy(nvM{;9Tha7Ib2Z2GH z--bTW=Ek!Xw-4}^F)|HcfSxmK;(dR@l@}Nv2QrMxgwb^Ix30p9mZ2E=0%JDLE`L}at04DYPtWeyL3f_uyZHw8$EEuNp2B}YdYK^4|PWNJkz(Qbp zSGA)++cn8OnPfua3MAV_%l;bXi~qucIYD5poOcbdlgwZh4U!Phss3c?08*r?*GvVG z`BW>udj~costSE$#PX@zBSgE-)NC;A#qcCh>=iF;$p4l3KFD3qZl7qw+RY|J7^4~G z0`yc9bJR(UndLmjNJi$86UQ7k16!LxMo5+1v?k0ThQDwkjH-DZ1~>hcGfZ!CCt;ZD zU2u{<^?t}B>?8-j8(q4Z+U1ipVBI{#FB{Aur|OT9A#LYn3QO`l{Y{HAQww?^^jX?K zw1ONXmv8gu=5^kPSRrnbQ-gsPpoeWoO z23@Zj#RQ-e>3@a{+$g*WzQ1a_wt)-n5VDv++BfPo02w0VNB}jtYEK;FJd%UKcLX>Mrr`t9_I=UxyXcC{rOJaA+dH2 z8C0pEkIZ47pI3W>|6miJRqajRf)QWWAwEWDuh>O(g`{oO4)tHcDTUJa>!-0bssUIV zv%2wPYrh8zm+T4miT&YW(3jV#Iay5mKOpn!l55O!`{fV8qAT)G->aSyOk^J!48E{` zoYSGih?w42`$D{8VQqh$I-j-G@|kg;E{d2n;2`(kMmKv|Hvp(@CpQFBCZ2?%Gc*@re#okv^Z^l}de_Q%zA; z{>5)C4y@#1?-QVF!j9R0(x4_5fVlz>d0GfxvDO=>IAeAjFf>p5igpP|br=fWs3M0T zqG%GWYqZqGv*W2g4q&AXnD0ll**RQEZRsu|Vq&bRIfX*&LV9_JB4L|R?h|jVWb?;Y z_Z;&fLZZiVjg{o3ebP3Mq<9jaqC%P(R@e&#vl4Ffv-io@Lu+)>qB)Q@f_M9YlHodX zdH!+YYzfXo5xFYDtz(=wA@^*D{L(`A%>F;u#Lkozeou9*f7Lq30H0p&#O*LCG0-0y z-r~qu6I*B7M6cmF=C@B(l#j13_Vf3fZ8`gI#w!v_AoX_=^NF2|KR7rz+=$vY`U;Kd zH+~y=Ur@l4}Mx{S}JGd4_4V__vN;`SGj9Crs z4zG#A05bEF%Aa`A;Noa5yOzc zC5y=eGLR3k-W*XFQC%}3QWLX1XJoTg_6CuHsD~N=4CDnUayx+@Mk9FNvGP+koAAj? z^VmRZEKF2{nbTI}?RC{x5dH2oR1XG^#r{H^@d5CY#nj57R<=A@QsSftox-ONc>WF-pn94>Lw81%@t&H7w+0v5?ffbJ`y@{=Q zUOG5_2BOEDSN>Q`I5+=@r5Wf(7d1QYe|-KjU93(DqjuQPCn4>G%rCd|$bOPSJX|fp z>~%OuvX~H?{iBE{;LMp<^;N4jY9FN@UB|ibKxWk^IK_T=O`>LO5$~C=o$okc)tJhK z$?ZwIKtQR=b3)Qjq7v9OcV)IigUyK{uwX{L;-_F9nIAwx^CTAo8%rkuqSH!iDE|~x zfRA;nvG2=r^PCCvK^|<8$SnfFO2Kld=;K=cTMFYoW?JPYmVXu469|f|`3XbiwC{8^ zt-Uk)W`=;QwaO~p$(x^$f!E>z`m3~N{UKTC>I8f`Q726BD(ZU)lLYS9S-iR+JCr)9 zK$o6qZrIZp%bP#nSsBL{%@|sX!(G$@@dtjbC@tR*tvc{2V7}uzIe09|76vh?A2NmJ=z2(@xJfvyTr!VJnZh=;v$LWCHSQun9mj zo4iBdqYE^)sJwUcw)6fgtaQIruF5RG!->~+Q}xktQZU_ytC5S&l3@Fk#7rvR7Oy{cf#=k>3Ji3p zb+S`AtDRd#D;Oz3IgDM5d{YA7d40-B1NO0>82dCpe9j1D%gWb$IK$~g*XR0Ppd+Y1 zQG)+Y4^?Vz3RDPA-Ji4xbp@RN9JiqTBrw4JZ7h_SG35qkW(B7gqAGK~`zx69M4%Su z8=;Su;JQ*Y#$t8L#`fB7{z7ET0{au1tCNa%QJc?`r}gMnBzekO3Ugc{b68}owx5E} z+oZAG7%oW1zrCHS*YEME=Y<~8j%SFlo;{zD5pKPaJ)AM~S}eVQn-4E(bL5Top^uJL zziI9140*b}mn7D0QDC6BuvERJwu&4lb>cvzD5aso+hT+q3*$--(TZxyo%~uJmE+cO z>)^jtLaO|6V*LF$0otfYoQJj&S#&O@)^nAob;*(&CQ~tYU4L`}(zxhFW=-Z7Gxeq7 z<2)Zl5s+RAU6g_C>HJ2vtv{%s($Gnvr^-2nBrV(7*S~K$z#d z;3Qzd)l01R#=wt=us6%3UD-$P^Eq0cl3_XHo75^ld(9=p^8X~k(U!12W}}wWsz{xc z&Jxsi>(AdmDYHJ6MOJkp#0|(T6ktAE96|U>qZ$jV8o>qKe_&a=l>bVKN>1Jq4W9m6ai%i*d`pQf+FQ*{J zADird!?%CiiEy^XZUk096;>2A!b>|ma!M$kul*XHNUl(8cLKdl*eWo-W=2TdNK93w zG#~Ei-j&||F*I~!$a~Kgpt2;`3bgcmLjIIJXf0iTD;i(bQhZIT`|G{&HwKS9hk}ZG zy#2X@?PlMs&rdZH!y)Z+e1R^d0Uy<5%)`HrE=!fk>IaApLB^$^Sd9lyi!~M?+5wHg z*3K+46a6n_0}O;u5vqL#9(t)TsISZ&%G#dVx?TFOi3=llO!!G__5}dfnDkT@qT5@v z!o~j*-q3i137Jc$=pn_!a}b^1cKX+g@nci_Bh)XXG|xYKcSu3xX?lPKZ70unXwFxR zmtHF~T*iCopFUIawLE+8J6i*<4D*|HRbKjaUZOFArS?of>X*koqAb?O;J1<5k_zHA zS}hMD&=+4yVvtjYHf#S->Qo_;@szjhR4Mblscms;{#~0t!l{j^-e80d3JH;Y&jQ^S%+n|Uqi|8X0inKqP6JX!CDXT2Nk z`q1U+arqM&&Pe?;lqdrim2K90GB$fe)A*(Ug12F(I+-f*>iG5&4i&KWq>OsMFa7@4 z#T))bms^wEcFHJ~8QnT%)s?pqt{#o3Qh}-tvi|m_1NsSaP&dr@Ezw}lT(ml8 z2k=XPbm!ksnkf_n4Wx0@1s~Q069@2!l(n^MHK9$Nr{5*tw4NO~q%CY_E$oiJI5p#O zfTYyT`ZP$k8!H#VP1As>;eb-!xjqIBh(II@ zg$9~FE!dX;BR&0yD|iJ6B->_Hkc_O5S_3Ec*GF~z+c|--80=;1oA)M zqT?X1?xf5fh1|P%>CPy-+vYrb1Xu!6`S$|ZWb&X@T-_Ib*50@1{YN8ucjr~h3)?HS zFWT>Cg&}E_iXOxZ2-g=?lo$CXhG%~mBuUVz2J>3?zka*VEJY(-{9j8T=1eFH%(Ng9 zt@Lbf>orB_b__>X?w5A|=ofdmiD~4cp|T>1W!EhBHCnvWj#Q;IDmrI`OTNDC@I0~c ze({&>zYr~?KgJPr8J|nFOvvucIwlf@kws%gT<$4QyE#94*7odQE;9As#-z?_#wssG zfq_y?0SjFJeFDI5=}ro*#V`T^`ZW^3YMOT0e>MP6a4U@*)c*kh55GD=lvNe^;T8d4 zc@>S=;paz!*<#<@0HNL+28PZ*9bMq(UdIFbztsHt>HJnK{>bf^7G9K<9t`s!yEldm-Rl%pJ60Ajpk(ht`0C>Swo>dp>(=dgeI%f$J75$c{P?qW4HDg9k?{1&(z z=n(@E5oA5XH2utG`{lApG+^Keow7UHtLp5yHCvC^)mFU$0PbS%{hI!wYhwl4e|8V6 zje&&TesG}l=q)YL6nq6>*VEC?lCl6@-3#QD3%l24;`gY2xi2~wMVcbyJ7<%s;D)%2 zIVyn7(;R|)2cR@PTHopZzSi@vLe>EI(|HdKyPmR%Lc58ueI;zWL+|nLhRtJ`1D+ES z&o`+R`0k8So{qk9+xh;Aos0U=Ls~-=Y3JUdua2T~c3jQj-!QN`TH|LO;2Y6A0qRih z#<9Bu3FE*YFKRXh!i4ZlqQyF-2WiTwIsc}WpDcc zfWeO>5}mKMOrcM~|BDCQ2Xm6&N@sWfBTfBHus&`k>1p%C%A$9an1G1^-lNxjfmC3ftKzT! zOGpidsWNTZMYR8?x0fvsW^bg~zMf(5x1LH}&OZ_dLn(Zp@*37LGu0ZH zJPCZ{xqMZ{NR{G|Vs2VBr`sTXhliWdkM%PDL&id}$o18M+1gyEc>!v+ib~t^c(ps` z+R4eOH&I7+7Iam*8#dGXdhI~)eadR5AcJbR;5PDIUL#@i_@%maM zr_d!RhloL(`y4X+w9IzV^Y=j{K-g627WrnDn(LdW{3(UbuB<7)nMKX|;#eKk4p;U9 z?t+|Utij{Isc_d!fNs@VX>NV62Dm*odpA=d+4%Km zoXrG)XTKBA0kht#_Pm&&sJ}Bh%mVWSV^-$m-WnqG?gowMc5f9~z=UEbWHYcE<1m`- z#lQcKm`X`W#@r75RAUZO03?53?8)G0(`Tb<@N`v~mG{z4gI_;?x}Xm|UkxD<4bN{e z$MmD;1^jdIGkv8etmE&^XIIXV1)s&~gshap9MpOgiFu~u>~?IrhcWON=9x$KcCSX| zX6ai`s(d>v2@krY@0Ehfk|XY0%^=fN!m4BO$Feb>MZU!@3s zh49pKIc!#>zm;zQp6G=OhRk~Y zc7l|*ojOMqro4HqCHzqQ+#46Za`SfcBI4p0>3TB6OEDcOBB*a@=AqBpp}7+XK;VUx zJh&`fgUZxqf1b842yt%%ca|Q&DJcx9P;hmmgJ>uoUE)5RFQ2!EXHCmX`lBd&0V7%x zr}TMhx2FHOY|@1T1}U!@@vZx1V-oi;L!w6d(hF2bCdQ=ncYHdItjgx`HgPA3LG6N?5jeHrCEF!Nv`TM zuN=@La#u@h0zK{yW8B9X@k5lrU!?^*;&-Xh?lO1yYy+jQuSgXa?=Y$^TflNFr0UW=05t7_r26PP4HXo7gZk&eQL!z39diiRkF$68o(W&JNry$ zfkBbq__7O|d^-Gw1{v_xUO@A9mg}=%Z{J_YfLg>8xIOo54Zf+#k0_+tR14(tRto%>6s09(1{; z_A>z8b4cQ{zm{m&Ky+0$l0Dlet>O9SM6eeiJf=BLdPNaQ2L<$vdU&bkzV;_T6G@f~ za%#bK5Zb9k?{n1dm8gwO2&@O&r`}1=>g2}~oW5^8Xw*2$u3P~giGS4XZ&2<}rg3RK z#D>^OcIyiC8ZP`fXZHG_*ln}qu-HcRBDTf*_Po~l(u8RU9-{=IxA)_mQkkk~zmT*K z!etEqT=cKZnXkPk`hkiyyT-5;Xj_mmh7;Jvi;>$*6~s+BH~uAYZa;tfQw+H3vx>-6 zG{{yD_vosI_f5`VXD~A2?lLz7m#g!&_Eq@052y*b@JWkJ$K$7YcAQW_dM9#$|J0+M z;SAU*fvUJQ@M<0uLqE7Iq+a(G7-t>0v{&7CH5|7Y8R}N+z>c&nCpZOmE5menK z-|o5KLEn7S{T&{AhO)$bRE`quiwte2&b1a!GjRe(W!a&vY_!$fJHeqnG~k0tMot}W z9MLuk4%V%A)||n4oNdE9q=$;M=pAWucr!kB`(I0{@9ggJ z(P}rIn143(G|uy&`E5sTG+^=mFq_sr*qrPU=0 zD8}muf4tk7u-t9(9wNI!Y1v)gR3h}Yu3cNGZnN4*r1pzPOd9>OLmF;3%)qbJv)>f@P|8?rWQGsX{*OMIJw+|Py=mJk^RLqvhp}I+;nz!%NYyO=_KBGY7$%#!8O7*!p z;hK`}k2)t5jOP7ic6n*P-DtK5!gR|A(gd;OCGl8)BkP!Fk-<zBA`Mxc1Mqx%&rt z&1gR-b_`&w+q@}8-yo9YeP{o2p>8~I=9aSgA|BDAFvgFX2WFB}dLKZzuii&+q`V`2 zgx^tC)gd{`djXv7HuRpavU&74;XdJ63V@kgbJ0ifUi=lIJfDsvkZ@Yt=aDnpv;(Bv z-I?6IGb;^7iF|mB*z$J_)_?iTE_)78b9i2a{bLIZ-E`(nCpJwolarEy>3vjNM` zE&=y1N(G8w*l|$4xa_#Rb@u#_nY3REaD~rqv}G&P*zS++@3<49YET5QAzUP2Wcu~E zFPb)MXcG;ng5=W_y!Mc!G`BG@HE>LOlv-l~TUV}`G9}8m~8zV%_n+za=S< z4)?{7H!fsq#B>L=$aXK#R$E?9p;m95L*@FeD*`P3J(b&tkX;Sn@hEU-6r{7gK5#Zv zcY2O)TJ|-O^m%T^Hc`*f=t{!!Z3F=Y=uXYngZYE1z$K0a)!(^c!W9j_7I~a(JCA=5 zPM;yS*pPauY7`!|01ZcVe-2sUgH zFjWJ_Vy;!~j-l$cNZ!WOv!+3VMR9t^hS{7cx9qv?U9dnqvKA@8!rxJzQ~O+YS-Jch z7l6r`t#?Q^O>=-Q0hJ-W&f~3Me#SP?LMG3bJz8i4Hp#s z7@Z;;)lU89U7pgs+9eQ4weRWxaSvzw{*S#ZRrIAb{5QFP%kS9BqbQc(x__)<2Mnm< z+nLT2Eh%b)H@jj7XudCq*MW+`C|0zSXnto$GT8eUD){59-PByA7#i@n+7D8%XBocX z$_kY<=mmjbAax~$*Y2*hA#Dvf_De4PSrO$k;1isKYd~D9+v87taFCMPU2T#b_YN(7 z_v`X0p<`**yNnynYz0lFrDK8sx{Mt$V8F?;2+&T~z*3S0#tBSUQU>XK|G+>!PUC2* z{1McLaRx|mBi~MCqX|fk8{nenXx%9z73{J-x<6DO|GE`;vgY;y@6H3FlC`69|o8%U0zZeG% zm-#{E*f)o&I~fXQx=3094(g0P_}4k_!*0A`D5Ve!KQEL0*n>7d8Fz) zRT##Y=Qz~pE_{?A7jR|9=PtjjmKrl3mQ~L-&Ry0_C%171J);aD?xa+;P<5lTf1%2F z8b}jomU*LPPG$5paccbQ4;6%d`o7g6SD+hZ%fOu5))#jxUR!e2B@$X!Utfdvx;X!( z^}sFKSft)PVB*tOx4W41Dis@#C}XV2$}-quTRY)_TEXA(#ZPjg7?%3ua^i=mTQb%s z5B`mi@JTw`?w`Jfp-;rN+6Fi~uNvA$ayGkThxW4XLUbndjC*ud5yrAx6$r)UeW%!N z=b5sT7p%0FsdB&Ie~WEJ@Ikf}B{U4{KV zOHYcZ`cIuo_MymU6uCRvoVBf5jIBe{=HaU@PKqm_pupT-Ok>*fY{J{c6i85~3Pvvl zVX~DOG$13Z^SOP7hhNRT+ms~NYNVmo7SOj-G;)JaGim2Ukv$AB|MxPlo9=ePO^|K#8RKo+Ezq+0^ zyni8owb~t`C%rqbY#nPwjNOI-ZLRzlIvvyRnQ_|cdFLQ^r>V$OKG|v}W<)QYn$Wnk zoW?38h;}dtghf@+d54Z*V3rpaJlw(n&5w%ymJZ@F-w*0{85#T6N}+iU!`XTy4{5dB zfdWS${EFr{#0ho-3;+VEt3g)t)n~8FKyP5X`>;<~PbV3DU`UWBh;g359@GV5udE#^ zwI31T!Zsbo3ddRn1G8RywP}M4Pc5ebrN+N7+&2CPFCO)8j=_@l0O2mn@cb8UtYFN) znD0F6V{R@1EumC=g4b2+N&({gqHhcg#yE8Mw;<3YQTHfzM2c*$5$+G_pfH|f#!eYF z@;9QaBf}o8LST{yc+@KaqfwW$%9jx^i*){_!l)n!F5( zld^VrOs(;o6k~#R6N4IlW`M)s%Lg=ze9bo5 z+7SLHVzY$tAsnRjFiIuDOr}eCv-tXpJm?DF8`RN(TN19Bz~uT#2LroZ;}!`&E2w4p zNa{HipUO778}VpLWj<6OobtdiKSuOLZb8eh!-J2yRJLm*j(N*h%nI&K0PDIN7+~tv zLv6w=eAVhcOClV9#Ow@!hXMm!1DGwvf*p}hl~Yvi3&%M?+OQAm;XsWxf>GjzG+v9r zkif#Q>GYKWc8RRU?|Q|soGIhZ`=-uY{=!{+-`kp}3Bt%NH z&2-O)@&es^ClP!G#1_7&rVbJ#qMZx5oR*`G>uH&Q?GLR7*&R{qO`>6ra5+fbnX+F> zN{0pmnuRvY*Gij|J?|-K&fokt$E7cgXo_yWLT=i#car)l#-he=P@5x4Cz{RC>7hQ8 z=*Oj|+!mzQ0IT&mA>tK?G?Ryv2 z*7&&nvK1wCF&M3;C;OKg9|V&Lh+bJ_S=ofMsr;F}yYJ@-yamytDwBOa3uS#cwKcK7 zqHm4dU)+TBft@Cecw;XtrO*AP9o5)HJrXh>Cs-YGXVkeGh(5bdU^~+8+#M3IHDgNLa`9ILbAkzjXA)eKHh%5) zN^EB)p8hh376Z~`!xp=a4WBc5H=Xkmfe|Ux_tO%^b1Q!;BnOEM^5bK{R<`01h1rao z1+zu8vQzi{e&5*C^f-Opf{7IWw487uKNCc;n74^>Wih1<)huMzd^tp0WKWG0I=q4~PRigvcVgWtMd0fydEI`3PTvFNN7wYDNiFcJ#WEy|hbpyA zjjWQCVZZ;EI0V~*D*fN-zA*6yh3nL&X3nLg&wdx@x88lGkpb5IwH;yOXt1t* zUb^%w+&`vpY67~^yF6qkEf%K-E`*A)-y%aUSMDwTr$IMT@9eL9{A!B!gfdY!F|aGR zyGwqza6VW63#6)GFx|V0BsZB-$Q;@pj>;_nc)z3lM-RUmeCB3XUhSRd&+|1 zelISP8$DRXkfspq0oe!+oTjAC-eK%u@yEz{o{0~sE*eZ>mNEoUz)EgH3TjRnKH?U{ znmv%G!%mfsc2cP3&cBaHJp^1!b@Mru6tz;dz1qmc(q{PoENVSE`dp##jxmY5TYFiyuKiA^sgy%1Nu2F^zw zo!ojf`L~fF!c0p;`+@CY!TaR>&qgl|5&iSA%z7W#6Pt^&IJ&sxBTt#9_kYb>0uvb~ zEMp7r_8;gOSTwgihvoYB)C~~GvjRK+7_Uw9%@IK>SQSBkY@wG0-$4z)R2dPBj=x@8 z9R^fbLuBu3)_caX!~x4(d7Fu|SxRar-rph5Jk|LzNTZ(f4H2k+jua1=7@XB;L9Ejm zBf2O~=CKc|wvwBxC-G66uK6<{COQ{tDq@!f-1>=Hqz;FlK|^F89ECov=3>u~c@&IB zGe`*U!kIv?yDsz%j5$Vwt};)Lc~*H$coKaT>5TCE?vcDb&)DfPUa~}>qH0+rlB7Ic zXPhXRX))Pi_ajBSLc^6nOfES@@t+psC9PHEv%edRNg3$)9L!I@1;whp?xLxAa0i#W zwMQh@E3?zMjt*VFbyEozhvS(oM328)@ux2#$9`GwXYsA2K1$83PR5UP)xX*L!!7oH z6ub?eMrsQ7eTe4#P)3tBiGs^7z~hSqnYxzLfWLTl6|5P5u1IzO)b1m;4}U{9;yZDGIHEaPg%q1wy4=dhuh zC2fVf0-00#gFJT5Bkh3T6D3JRPcYG44|MdVkZB$bKy{j}dj&z7)n8e&Z3SWHtKFNG zr1x047&G#x?~32#9o)jT`|Kcy;|uLneQ!RM}nN*`H~4jfG}hwr@DdY;=Ro43YWU7iLRn1Ons`xBi>5}#i(*wLS<97Ee|xnQl0gScxfc(BzWMZ#dyP9t_wDf=Co-9_6%85Nn1lfJzYuz?-0f?M0F5~wa- zs)aa>s_z^ynTBTZyIRO?h+KCu#d}zS&);{4Ky&UEL$?+@&ogjmcrY!YlfMhz#>n zp>5@fV!unEd;dB@@bW4i=XDKX+|o+meMnN{l7=Jcxqz1JMw2N0r>ynpgHNfU=uZJg z(%qa}?_4%=E_&ffY#+t$See#Fp0j!s>IkPk39|t5ilq?1+cem%?uA|Ce=JUm%vyibdh9Qh9wF+8!| zld8ZDqr(a#h{u=+NTlD0aln1x60V*yD}v2zvD)zbw^F`#@u+t9sy(X}rp5Cv&K|sM zq7I=vkzH9a1fYM+^%5j}Ijbd(%}0&-Y$epfkDGA9UR1fn@u3GBtbNLzWxBrU%1{0{VbZIi(g zi{Yv4LPIxa524tE;niAUc1-XH^ANvGE+>feiH$4^u{0ce#*H=M2B8HB zX<^&eyHg&+gU)R8n}fzDTx>AzlX*&Sm^XedHH{_w)9E4(W-&ocFxAULiCrHRT2OU3 z@wzB;_cIUf9%yKg_OSq&z*YajY>^r>>li2s4Xh-1%WI#&ET>+I+Se*J@Z+eJFbw4raXarH zyWSL_Sx%U+On=3FT1o=PJ5&lH6~OmzQiopx0!2 zC)pnXoyT>mnDZ?M1R`%FvQl2yU&i8O%}>x6!hMK?Sey)YRBf5iZB_QSm+~i{=aZ8J z4J0!v#J<}u8&K&njK5wYgM?#BU4OgX(*P1>GjhmYq2%8)H2Lk?MBdL=zj-36+{g#1 zMN7zMhm;LB!zEL=s%dJ79&x!F0nI}S=0n=tEkQuG*R!cgNK+8v6o9+ReK)U_(6Ro7 zBpA7+iqK%@=}UyvOq6u^IsqBa5PTWbJ()_AQ_mB?4Y4I$j}SHj)tnCTim8d+&Q=B9 z?j<$V{yH!vnanwbY_vXIXqA3ZQBJ+Pc)LLt=o-_`FN^bvJ_=a|a?BNe9wyu61-0aR zNzzw2pd)#scE8Uk*Rkul8nk)H-|?v&)3r=YRcuirWF(jE71?0HxI8CMlx(+v8TtX5 zOEj^netzl%R*l8nkn8MocjZztcGJj>xf`*;vREbDOH*cw&uO1t1*#bq4Jnq8X!tw9 z5iVE|gC_51#ad6gK~4@i_m@FwjKAG&K6^j2Q@?|K^_MYlAbvX0Od|`-$Eo=>e(is; z_1^JpzHQupVymsSSFEaPrPQhryEaw3W+|$uTCpX%>`hU#W{JHwZSAV9R!K|kO%So3 z%lE#&&+mR-uiroZ36XJK=XoB-@p*qf+#&-4k>vdvkq2(dyMX86hyFpPzcJ$gFLNFr z6(xx%C5^k@nT)5}kA&m>wiNK5k1o<)Ha(c&e%vcA5lh0xJU0H|d?GiX^w2=uYth<#X#agiSebunK-iL@{7T2YXR7l$yG|&Sa z_}WkLwL*Imk z1(jta=UJWvjz2z*Q=f!XO_|Ik5g9aAWMe;hvmqYIEv#uy4VYM`CkfC;Sb8dS_mm+O zX763}_xOi2kim}hGH>rOEV5lbGrmwTme`xClitgR#|=0OL-C!_&`I2f3xyfUU#~n) z8$N0Ot0F1?3ZPzESgITmLD5xTI&l`!zS^1Ljz#2~IwsnL%#<^5T1(5v3f4~c9ou_u zkr81{o*O5FI$u!akM>4;AKv7*q@==cK3!NQp6_rrj3%d?H6mxY1eR_}5%u-kDb^EN zj{N^K{^bsZc5wct0a4+^NK@*N=|KzMTfRlWxJ+}rvxo0akD%ewKAf(bbohS}*guvkrMy~Y8i(~l5 zU_a^bE9Ykcl7JA$mALoXEs`4`XHfI9=z7N)Nvw4cAtnBYYfu8pPCi{!s2%i{_$x$c z^>s(K0LU~jQ5u>VpgA3EvYdJLqyD>1;iwMmTs+Qr*;p+VaNdM=9hX9W^T#??c70HD zn@HW~eg?AZ2d&!b67i$%biA;ZvDV5UnqvFXLfs&Gbw-Btv9W-a^tTUV{b7J;<0n>eJg^-|Y5!aG3(#AaVrW+-^Nw$*}q8Am~^m0Y_$%^(y;#HK$| z=73s0Z)4UONT1GJIU(h^`{X0wkIRaoc>;LQjt$m-=E}R@?Eh0(xQTu%I*a!%{H)@P zI?EqxqpQpyM?Bx<=DARngcul-u_czVa!9^1lB+$AH<5mj^sP*JX*pz_85L?idb5TG zMi|`WkTLP0jhjsDnbTXk-%+lF>b2x9@2OE$IAQgKsbK=!Hbb+X}$RJMAcfK4BKjLOsu;niRKrC8mLS zhPPHvRD9EgB6bXKwd0MKxd6O(e%4Ltv_9urScM%nUmnQGsa(x69%B%Z24rgY8(X8_Cae&#n@g$MDM01*rKCt--K8YB$a$@w7>9@TiSE`_n%SGaedM_hU{-Fy2ysw zI^R|gOV*cNZ9d#{Yss)^?pEhRdB0rbk{gS@BV`Q>teLxh_C9ll61E!yQLy2rz&tYE z?u+13S2a^LnkLWt#rWa5z|5`3I#!>u^mS}WXL;qvbN6}Tu$udmavNNWFtZNC1xp@5 zRpbyv3G3f)5iO+3&$A8KnP}V9DiN<80FyX@)(JW}cbd<4<)qgnabyJVE%I$QhYKRN z01o(cVM;5o%65od_4`Bo2WFZ`vl-XT4lu5Vt{J~DW!DviCzUXGd88tip8cwUtD|4}bh{xBMXx4!tmgu-w(Wr)^co^QQ%SZ(7ao3xv0sSsXC@ zotbr=4fcM-n*92$!XtbsCY(bX&wkkHdJ-u%K?nOB;~pt{_s80=`&svxDj>aqJ=xuo zZBqtv(3mN8KANLtfrXT;T@J7Al~|!o^d{a?iq*{T1IKE9)MwK3B&l9~xNGoR_vgto zChGg?nE~>F`@B85`~HXan~#O}Y>X2!Y}fl_l-c5w>q5-=q&p0xZs(Sv8~-GpP$XaKpgZ8xqmgyDQ~guQ|eY+ zQPsNYp$oo3#P(sKgajY}Pn2kV*|gRz<}J@&Etuvs)O}kbFlKlZc8e!VB$YR_>(`56%pjk3f;FKC9Ij~+5JAs*^#11;CtgBiBr8FnQBzx+zcE;I z^p(osz-Ik37l!`cAG-~;+<-~|19k;u%|Gc3|Ep;JGs~(0T1~7G9g8S{R{~!Cd~I;n zxpLfo7gucvmoA&-$LbUrd|}sL8Y{I<0CweQ5rauk1lxursfbw?l-?qJCfu}|LvBsv z?)L}E^uatM+LO@Tzt5{C+wJF>Euh@Ju&+IuBtHY&biJ^$R&EbEA06?Frm2n~!Py(v zVYf^a>cCG1Tv}3Qsd(=G4kuRX_VeJdCKZvK0mj{95v@KT#k`M~YYyJ6ZtZ7-KSjyU z3UwO~@{t-ekZY0mRppsMDS4#X(TnLQu}lY=o(}NOybYEfEneeIwQvrS4qpii_r4-K z;v=v|$&|gE_1$cn;cNwR2m^|%!{dMzQX@?oG@VK+mJNGN&YeS@La32R z&JkQ9g3BHMI#8Dc)pIB;tM*g)7%^h{z*x-~?M?beuAu8)_R-g7NcaqIV&p&F zp96n9W2ykB*i)Q@quJFSpAc7=P@S1FFcGt0T?AK6MXv`V-M^!cA+7W{<3L@E|ao-7=5}B7AF0gM#wtKAE{@@okbrD8KZ% z<^tr-)SDkQTy9@u;wl%CFj+A1wHLxWX0OOhkK^*4&GEa9VZ+t_!JvM2w%b&P)B%yi zZx7{B&NKW~oTi-uU$bI&LE4C2OFQGlqdT$)^XH%5U02hf@grEANX6HtsUeq}79}_Y z`8gnkZIMC!d1ZMkfw~-rJMA{Nc||+CV7#<_d1dXid~Y&7FyaBLKc2o7n4y1M26 zxz%}vRwkJt1u_G=WH(Ccu(> zJ9un+mY87J%fjjw64JT?8bMhuM*0mG3wt+BKBB-H9GWPz=8w{hFNvDG9+D7hQ0V@>TCNz4~lj9QvE(@B* z3-|%z&*r|P%{L7dp~f@)HeiAU{Y!giV@BVD{QE;Pyypc1qS{YhOOtj}I4jchaE&Qp zG)al_+JY|=y@2DpKbC7WG^5seF!|6TbMZip^BLG`DGiah?=_@?bPX62s7T&ZVs&M1ZK?5QJVdFha#}OHG6OjuG$Tb(w?*Dvnv2iK1zY@lrS12*SNXb_1m|y(o6gCZbMNr1YrEXGweFpg zC9eb80EL)f;Bh3ZA1xO;^a0R2--(+VK^D0-ZP>037I@0*q@KRpZc`QDa;qVB6y)M5 zP3O%@Kht1W^^r#0$3H(%!a-(7T4yGinE)rW0K-AAd=P%wS`x9+)<9PdABt;RA-GYr%;4BS11Xk+M$7Bk`bQpblhTe(yCOF{ z?lj>zmMG>r3pG0_vgLzNWgv&nFwe7E8|hJ?McMem6pe`eAYFIu0X8ZC6uM7U(ckKq zy55eqi`>XGE&!G0=qRa9qT9QUWp>d|J`9t8{_`ZUu&iE`_<_Mh^Aki53AYevjLo^? zDNhLx!~z_h_t9#qY1?~0RMg;H7@MnWyu`3xN70(3a1M%G{o`Fb_`LDpJJ{o=Y{pKjL^QrtCc_=8ugauY@gtiHEHR?fHkjehk1Oe)P0t_fDl zP2Jx2(q;Q#mB%6dHn7;Yu?Nw=;61L1d+*;y^`5T?PrtzU%CVj~zRv?2(N>Teq{p?d z_(Ztj_iK)}Ir^o??Vh0q)#~ba^j?QjDxKC#=Y>Dqde{Q~S}L=Aee#aMfy1{KXHBwm z#$~;^nMUHuXMU+?hb5kQJKhtRk4qK>#z7b%M3CXZ(;*1+V&kpJJoSY3#z|+%faxQ^ z#H6o#(7yddly*MQ@BQE><5do2y2px_Z-?bmlr2spOd@fa$Q{5&0O~Fu6#Sh`jjH*1 z7tN_w?ZG%g|2Xxte{(&p>d4QWOgJtZ`)+%^YXz}Um(?1kHHlI-GLDxUAwy#s{ zH4C);W;s%-QoBEj?xB$7#WZjsh8fXtXvVOD1>`PzP1Wzu*pE^4>{r)%IQ$|;Qh+}kRHR%z1a8RH{)bOv1~(U; z+-&pX(m1V+{`|tr?;r3Cv;14704VX^U$v-M#7eWw4`3vc?&zYBY(FIaowrPUtf;!-5Oe4>_ingy+T#$r z-rn3aKV55o>3yhGJ$xq)$0V9Bvo^8+ba|HD1us3^bvdgm@sDCMY?PD}CJMtb! zo?XHEal%#B)2JISuE2>P9cg?*Vy8F{3EwJF^z$;BoLd?hqaC`PtHOk^dJry((rBio z^+XIR2Ojbb4K8Ty9NKd)jFN(#nNl+RK&H*Sc&ZaFyL6 z3z!uaR2>wr);*stQ}mYDa7_1Gy#rurFpBkoXb;%0CjUAiSy;1W!R7N8i$9#Fr#&(( z%0t$|CQX0+^A%(laBL#(OrBN^X6_VOT6Cd9$`-m?ldXcp|2@VtUmfEg46uHIq=l8w zhl~c5^8VX5zd{f1IT0_y`N<1=G#&|q;F>hnFlE~AAAa)B?4zImg%r>Hzal%=->}e0(oS|MR}d%gB8>3=Ivlyad1rGO2N#XCt7BRb zZQt*AI{a?xcPrthwz`98y+*pwTtD{z<3#^2FgoD3zm9eVh}bI>^HYq{&9*k)|Mk%L z4=4Lyy7XD9B~bDF=avo83J6r?yKZ&=Ca}?7J^m9wiIEoViT?pn|N9TBlm30`oo`zF zZC4RXHshc5)|8qp`c%0B4IPO%uEgZyI|VIO-U$`PA}34d z;pJ_cQdytFMF7xb$9Y<2;XAbb4p1`IEdU}SC#Y`EUZa3JAh@^@BeSn|zVFOm-XdDr z01PM}2RfwQUbY8-Md9ukE{r?dy4oq*xB(?49ZiVdG?*HvOG;3wt$<>wF>lE8DE4|@3=HH*#R}>|C zG_@%E!m1m(RXK09I}0EJMc-=I=rEi=X|!WL`=1-H<_xwT*U!AX%5`E@YPx;`zm!SS zDZ;GRwg7P96|eOms@3c+v<+vo_l2}$7a9)@*jGPxaTv{0m(7G7K9ttH1eo!=K$rVb z`!A(}?CvmEoEqq~RJIQ;)FEv$fq0T@rhH{b;|;<}(gsb8Bm zF4_V|X97^|z;_S<#3a?NPL^9$Vn@GUiO&O@Ugn$tnmVI9K)zWQ!12w8UIOhK1^`uX zYr3?cM}7O~bWvtvmt-idWu#?!C0V_Kxr_&{_z^(BZSMP`_=+Y5uG@d`Z$GfX`S}hK zdd2rG0QRZs@|wo`fC~CXLXWUTfb7%gqkG3Sf0i1AA3bi@-SU zPNv^_5q`<>Z%%~pi>!^a94Ww@(~lUp1^8@t0rCUO_Ont()UjI+2L=RjZQeRMYW&?q zPL`TP+~GIlSjYejo>b0_V*Uaa{z8L*(I0-PU>^WFZ(Jo{>}^;&Hm&h}ym{gs=r z1_Sp2VzG9i?Q|QBS+4}056GTK${L$|Lh=OIE5D~=Gm7MZ)de;7h~^G?-wZGcgcLR< z0Od+Gu1|Mau&iOBAA1_7%Bj5r15jS&!a&r z3CvF{r>;1JfzmAZ%De$REkg_(|J)5A`*aezuP9hbt{{~Vr~67+!7JhI9v~Tw~KxuFVBl)-wVrcU)Pl>8j~0{yRwvt03xhvf_zy;kiRKPJIMge!5m!mfq3w* z`}*&J{p=}ww^kdiON|f<0HnDAGQ>&fu?F^VzdT$52u~X;D0iXZkS+;oeDfT}$Q+1w zJa!4);c)Au>jJ`u^xG#&pTmHB3XPAGKSF^Pk2uHdpftL4j+%Ep8Ac}B?K7VNSsU;U zp=23Ne72!OJ0_6+$|i^hAU}i&;8%Q~IMy7^Y~(RL{^Q+lhHh9dMz5>?_-Uge*a4<%0B#M> zlwlg?-H?9--)L=NAi`^5Meb5%%)+GX%YdLS3*UzdwyePF9UStxm13b#X^?ht^GjOV_LCp|UBI=SZ1gq6WXbsp zIp~c^EA093hc0T^W{Q!MKPQL{lG?sc4xzqR$ZtFZeTgguzdqq$+5MI*O) z^1=dLodos#V9da0MB=76!2NMcgDz(4b-IBO`UCIW3)9lrEZgV@*VVu&1g|TRQQ(B2dmM9{lp~Ba zg26Q0M*z}?^bDQVs{7^soyg~1#diSX&E2l-Tt_d~j$9>zDfx%b+4SFdcJFjALAvM@=ha^GQ10)8 zy>Wp(b~A%89%*GRZRj5QL=)Y{n=`MgnLAVW*O)l(d(dI4BiU`3Z;hcJj{$))Lsr>- zH2nU8Au-)kho^%102k1lb0MJIHn(=i#W&0!zvhrPN72t6T~(nul@z*RiOab}ABJ^= zdSlDmjjVP&AqOW}EB(^Q<~Ob5s(IzV^pJmnk3;Z^edumdV04Yx8rU~=JzWZ#8e51@L5WwyHl`$WxRZCvky8uuh zFL9Byw!44(5?GfKbtZm}jxj2!O4QqOaP#i?Y^QtlW5b+A?I{-hT3yD^RL&BMZ%>6zFi+)F!I?4ti*m2*qc68N4SaW89iI%$wz<=Ux zeE0fMcwG<4ZVBy>@j>!VZyav~5SJu9aWLL!c|T3-76}dKuauaP@|m_Z3zw`>^!mDM z;ID`iv%f-IYie0pKbgjc{{UT?7kMmghP5JlE%>rJSKqhg%}f%>RwvDi=_^+lT%Ne# zMw=}X;;O(o;ogPgvg6@@>TPbjO^1JH6~VT3GpHv$y|dv_Ei|m{vouMBSveD2!PsO8 z*ith+sWE6g{pt8YSdccSXXd@9Hvj4tr3P*+eD4JO&n91<)zJ?}tQ%W-rSB4hZny*B z%Z)2Cl~sQ84B&(0^3_9YD7CIQ=gNaYWNF^+9PM>q=)w>jR%{Yz(KbL#f(En!mGTekm36+kvWA4>fs0#7L#YKAc;0FKO^_Lc z`^LrCivoZjfv)>HJ<&IsYwu3cO%@MhE`wT$%4WTu+R4ma9Muz!UUOY&ywd)yImjHx zYnLc*16mmyAiqMbGxF5!?Es*X2nV7V@0`;-#{h2TtaUJeKnVwE<6_wh`l4SzasXoQ zZ-Q<9 z2O>5M&Q^ecDP@9b25ahSV7n`5WY0!L_d_<@PB{f#4?hhc;3I#M+tcg8IRrCxfFC{) z1avvx1#oFu+fkgdH|fLx#K&h8szaV4wKwt1)s6v$J&Ot&O^!KR@|g3QylEx>@O%;( z2%T28NO5p!Tr$E;FlfTLW00 zBMI^CNz1jH`7Hs|BMjNn%H6%dPg=xh$VjfF%jw9QhE`<(+DNK+ zljijldtZa3u9p*Hz^626FbLFhHMsfXwk(9|h$`#V9CzKqS7O%rz<}l|tW|^Q&vLy4 zA@hV}=K;!ZhYt8)T*7tD38U%WhWeR4ea8^*I+br|iG)uNzperzJSDfD!%jEd3nK#t ziq@{8d?b3)flE1O3V;R0<;9=Hd4HhIUIUQ&DV+A?XI?m6p(&tRvu=~p>p`f}KC3`% z&jUxikNw|t04&qP4e*xEmy)jmu#kHv?6Fap1FtQ*3IuQ#&U3H+bLDvdr$JEq7f_H# zVXlBQ0^364*cbS_P29HYJDk0>Q&Bbo*K;13ZoZ5v_B zkWiG>A4c{7NDCTkc15+?wOk(~pl$+xxPtzl3Bl9>m$Z)yj!aDEuN0I>VK2#Bud+-Z z|3Qp=*qYaSD805r`(adiDY3oqs5s`@qmMgPOc~gAgxqj&g?g-mylvZyV}{2Jj|PMT z^i$Uyz4I>f6~>d?pBH)piU->n7ya#pBEY>;mwhRx=M9YxfAK`vq6K;Le%5X&;EE?b z-Bk;biX`3#t5u7P-Wdf>p6-QrT7h%cfB{^hKB5x)j(4<3yuZtSg2Qp)IgQ=fA1cg< z4#6W;$~`@aQKqU8IMdUwObaPrMwte+Y@^*fnLiCCsvtj=oE-znq8cz~+Oipizxn5+ zg14>KG?UM#-?eiAHUN%yq=2c!dyFQg?K+M}a^^S#XieJc$cE%VQ&o_srFa3)wEp zdeJ;-wDzO^aA}k7?_1HTc6Z+3sgTT3!<*8y9xO{&=i&tFeX+6#Th3fFjMZ|EA1N= z5fFJoI07|$r8D4$gUJ0pfTt~C zo}M;&wCtt);yTsYL^uLZ_ z*P9=${eBcB*?)0^t`M1<;5QLXUR5TYnaahSHqc%ZcNLv*C_cjkLo?+-b8-W4u@7!F zt``d>=vId~0M%*TTcLj&7&~9J09W8S?{-g5-B9)2QBA1qkM(id>mLE*Mj^14C1Y4v zNU{`k@aD8pRcXzr#u~rl-`-}nHwWJ8fLTT*(F>wIJH>%Aks@9~63XXXow_b()IhgF z@aD^#yEmU^yjw4>1l+&=V+@+F1Bml^qMyAg(FZ~7&ejClT6_rme?VykEX_G~-l4!% zAO`F?h@a@gg9w>P0D;6-ZNHoOMR;qu3-evL0uQk8ntPy8ELFuV1SOsP4V0&IFkY_LF0ncyhg$-4?kG#QA9Z^?m@n99AQ07jiM;K}` zJe~`({mud&{Q$`3nbU81LoNMz3P>TPZ+sW&#uZB=<-lZ3NDnF^d}RvYJlA)7bx^N? zM?8RO^6t*s~Bi|Jst$HFI{Pk0+a$;Q=K_5B8_MJ zfBK-O$2(c_)wrMZa!&xG09K-Cu{CAm&O7e6M%%EXnh7t@=vI}?3CB~vLlVa+evidX zg-SK`(a>qEb`Cl}4B@);DH|8YNg_X&eJl_6TmcFpB2SG3*MC4G&MI0uBLdr)e6gM= zf9|9oUfB-nWwJ>DVhCU9H87o44G`pdMR677!B+u+FMRy26=lVv3<6K`4mz+ps(B?7 zCMKI3xXXXB(a3f7J0!~2BME1-0g(WCUBzGfWoE=ifGb9>LaJTr9jEJ7-?Flxy?SoDj1>0E9 z*Wh7&IVyQ0lAQq1EYJq!Ps+#weV^98%#kB+ACG}!hrjjR>^KFgdA@0#tRm5w?xQr7 z;b+Rd%sV#8^m6jsMVhueIYeuYD`dYhCJLyjKTh36kW{SxjBg8*P&Hhu%~=+3*Tc5M z$=sfe5D|#uH=4IuN2ECEQBpo&v2Y6WZ-$^)TS-%V=U>i6{fR8iqYV8SMF z-P~HEgK17PHw+>|RVdyBV@9XECg=3!R8-rtcgAnx2#{lNI>}JWoFJR|Pe=?q-$~<~0(zyGJ%|@ifDqC-%awBe)-Y*VKtmpX_k_g#r+KhR zsDh-OwSeP0=qm5uFGGi1fEjeoNda6MZx`VxQ{FNYeX$7S0BarSyUCVE&)t{gnR#BL zZI{#*FztJJe(Z|H1ZZb6`ulMDnj9VI7KlI)?rUZX&y~(Sr)Eu}vAoOF&bQ&nYdMeS zE1h&(u(Q8i8XJM;R(7fnQrn668p zMN66xo>{@cDR)?iv}-`*H*enj4OHbiPfj*fR+{;|7B+w`ibiL0m?;^m0IyvGEc!_{ z^g!54c1=H1g#itjNkVl>%lrSDKt6%uXQcP+R0QQ605L69?9&(j>;_XaCv*hQst7yT z37N^&z$Jvkm&ZutxCzR3QUxSz=@a~XSwjA0rzuJmXtrpMW`XW7n!ox)M3UqirLkC- z>@Hsjtmn}OuQ`Nl^TYn`?MJ1Dau{@`Y};WXT6V;VO}5_o{q6{k^7%nT7OYkU>(`51 z)<)D zH4|M*x=clb+_$ntboDN#=0Mp-_SWhSU*GSu$X(=9Ps+Y|iw>%O!+~Qs|9c(n*%b!} z16Gt7yDB9&%eBtlP|@X9q6w_A$cv#%<4vFs86^WUbt)Wc`g1OvF142$p?q!ESVDd<)iz?|^fC7c$<){T z9nJ&;icO#xI+*ha%ih)5L@kk)0_ZOzJLyp=Y6(&dUu5 z(}#g--wzaQJo#tP4#>+>Odpjp7RFYfGtGm-qm zWp4A6Ardy{STgJ|lb|S2)A7Gf1TEkUyuR4zcHYL}I9cy;H0m?R+p&jqOM>D-s?uXt zAirX^z#0yHpgcCdt?TP-rLIOBc!P(zA<{a+0<>AM3V z-@+LA&DEXh*W2r?Q&d_>a0L+YZ;Ak_*J-WA&Gp-DrJR9p4bL@u82Db>XYN zdFF{`=c|*ry9B(5s1Yv5P6yf>{Um46UGx7PWQ+)=>1)b)gJ!4t?PqzYv z0v3U|fU~=y2U61dPY_SvC!Q~!7nyfEdptdm+De*I$m8N?d}NR%{&JVW?l(mxE}ZH$ z!~qR?L3Hjqjts@u_&htqkw-aINFIHvN6_1nUn&p0pqSxh>y7w41Ti~79XY7-jIN#^ zYsw7k)J%zJBpP1oC#}+89f1-{{&toOKu%1P>v5T+?AZZRDc4BtTzX?Iwrqt4_ zzw;7A@-L#fyvMB-S@B`ioF!_M`LFI`w#l*d>-p{jO`_ChB1+AU+l?Tk%bA6(E#` zywvs)0RSlP!|Glb19P@~ACW48A)-M|YuSqYK3fZ5YI8N)D@~T(dzS)|0eP)=xUnGd zVNeujtQ67bos|2FJRn;j&R9o2GWk_rlC4E3hZR_=$pNr;KwZY&2GdW3l@l|XWV;0< z7#8$~nBWL;_XQLcA(<^V%=9QJMc#jx15ps1)irlby=|e`C--@}qG6o#3lRsox4Nmu z0o_@)vBW2VztHz&bGHFedh}fKGy|lux0O(#nUXXjq<|)uI>0WP!zwyfL5(=fklxCv zAUWu*PDum55ijqIj6f?SZ}VD(ZDA_cTae9EvtJ?`+Za`tp6o);;LSD$A~RIFJnRJ0 zmI$7{j^d7|4z{+mn*`tEL(wPcfx|1ke>leIrjQ=!PYY${0MbS-$hlBkC1Hg=<75C+8;Y7K7j== zBqq{B>yaU@+>e-V+J%X}c?n1E&U{aW=LaNhbLyH6$R~!bF7h$zN*#w@TbVuFy@8 z4vcfpNxo3a*H)Kn(0dE;_q}?yI3?c9Sgb1dVe-9XuehRI9dSPa*9kdo5>lw%8zymg zWsNM;L#QYTGXO3im3V@NIzc#oGh&gnnH?fQF)f@p;jZYDv* z(ljEQUYlJ;f}9v@U;`@a_`Ii2xv%=Y|#B zypABs?iNYvF1xK(f}!T2C<1%B7F5^eX0ROCfQ#^ho{>3YZT(W!^#2LfjuSk zR)+fnxKf#Q5jEWl`<~>kN^m+GG|mvgBv|hk<$l0)q{uUsAB?+J755`aDAZ<6oeP|^ zVW;X1k%_p{I_teir);TB!hQ{!V>_K~3RgbR^g1ut4y z4Vh?XOR($5jm|hH1+^J|1A(^KIgv~Qmy+nsbZ%$!sKQJ6d+#$QQ65-FU|Utu}m`AcIHGl%Xp#%hjA>Ml6URQivUPliF4Xe?=k?=7w`2dbdYb`Qox>KBvI9A+a24`u_Ye{LPm!Kk<1Z_YEKi;MAnJm5dYL=;FQC4=cmTZ}0|L8m$(#8%8jTKq`SO{7zZ6JO%txnv*6Rn&pr^l%pJ1q-- z=5dGe(GyFfLO(F6Ji$Yb>tyn~@h=TUD>nxAUSe;^_40zg6r;;5iS~$F=4>M53N9DT zNQmx%)QE1UFb%UjB4H;jsT7y*3ZJ^nYCVmtLO1La!@2BRkN>oayV~#ibfd^6My%s| z)&H_bg>{o9c!*7_J=2ON7$*r!zkic7&f}q)vijf1w|wym>67i!^Tw)$J1(_(FS)ai zp48%x8dnUafTXsJ%XY*Z&|Zdu`Ozg+8U!%Zi*3?{P~oaSYGxB0pFSZs-`4qKc3i-a z)j^RrGvOV%4!^&wj%4w35Pp-5!R`C&5=m`HcE-L&>5tq5(QnAa365NL+-)La1{!QXKvmDb1I>pt9ZFCR*ia^y8j;zvqXXW;5@yri`kZd!M8GRcxC`=GGn-5#WZ#%; z_9n0k0cx(zFAh#dY*Dn=qK$}t(VhRrDMAvPz@{a>V0L+^bO12+x{h->M{DmBd@KQ) zLs7OD*sZeW?T>i@0{;@EspLuz#88>`MUgha-cKoCP z-HOxcczRCFOw4+Fb|eVpiV`0+hL^!U#QpG{7sqU{*-(muuTcd$pH6AfkW>FJde5<`Lg8h+6I}cc0s#G}= zp}l#JA%v^bdLhnJ)t)7~4q4~MeYzD6i`uQ=Ou5r(WWL1~03E)pS^=d@w;c-yUim} zduxAQO?QT2M6(m;;LsgawY4jm`Ikl2i8$;vA6@E~9#&ojoNv}M$xwJs47iEx;qk#~ zDARe*B}rdsHY50pl~4QQ%wyvP`kAQ_d%J2w z_${77vdh7r<4oL)*)monCU$ZGWcN~|;{CT-(r3yB715z0MVa~44K~he^Gno?U@1lMcpG&&ffwY(nrCsq$L7($D*a53ANq7 z^eyK{Ei=3B|J3nKo#oXq;#i!LYC0(KU&A_dVh0jX9x6T?dE{`R%zo?p zl%4JRASX7t^39E>6nJCVT3y&wpfX2eJ7-re@>k(pWu1-pvd83)#b~Xc4$D?Sq-$Xv z`B|YF6S}>E7e*M9ESCCZ`wgfc*@7m9q(G(I)6u!=evk2Ndc>w(Ot-k~N2}Oy>?bSo zW~FuC!CysOSb?I40hPRUP_DM*cAy}mny9?BAC+R#j(l42O0HnJc=9 zg_ajpvQ2s)_J8cqi45MvA46yRbX1)CdSqvu=Std7_yZY+rW*LmAw%Qkztl#cQM@N3 z9%p5r-3B?;fQen%%`qgA{`DU1xwK- z#4veK)YC#Cz|_ZGy1Y#z_WVXC4@m_rwEit;um{46F#e;8f1Fl;cE*3(0!>yZUwImN zrRwLiO^~*S?F~MAF~$zgy6u}OmT8U5uxE7(Y2`OFcW8xp3W9&A3AVdFvshS~(R<$N zcc`J-4}P~8dLyqJa9^C7x-0bcn#i+r?iYfHLqgg`;$)w_dUX{=qLlhl2b4_oIPoj9 zdbUjoAT4WGq(^kK`AERZi zTYv`W&IzpJt=J3IPz-9|%&X{d=OjVR-nr3Y~dba5(Eg_!1W z8CzS%QQqjn9DU!1&0Fi4$cqy;RqmNXVHzis_F!UjVzH|G(qaf;Cf3^RLs;gQSxO*1 zO$_5h2?XlAr}Pz}v-fQ4zm1ka31ss>j&hMUiu<9WOriLlnZQz-9>xPfp`2m8WJiyQ zH$hI(-zdQirfFQ+L3{y>2KQL`4_clFF_yV=$}B>9_hv`5Nd}aLoK9L8!y2 z8kgU`WKeUr4+-slFxfz;n&Bn7>^AxB92fbQC{cPvA`&`vLl-vpFXlPa_>v~NVISl2 zb!y~Ml(l}F@2%+Xk1+>>Y$$anPoCZ_QC$e@jk5~uQ z@Y-Iv{a6nbes*;>p;ch4dw)Y@5NBikQduIB>(XLBVx7;1PUN{W?a=<4Ws)ZO6oZY( z8Zvs28%aylzPTz{F%8n;W$0&GRkYz@Y5QM>G&M!cS&B@eww#*CR-Ws#y64xV<>x86 z;5@S5F4iKUihMk|9Qx`5+<$f^-pS>ec~C~mrwx3W;MMv;(QgQqF6VS4k-O)ricF{w zIaDO;^bvEVVHLZT#C>AHbC?JnHJDXWM!$vfFP(c!IxDd8C?-db>zbXUhVm^!*EkzH zj8vq$rACbcFMzNEakVElfHCMcFWC(jx}`{|aNea@JGBZaQPEz3BNg3fWX+eJ>bQ2V zC+d$mle(cf+?xUt`H+w}_kC-pJ<2pY+kMIIskbK@2042g0}OP61~Z2oVb*_=*Wh^0 zIGyD6=m8*{fsu{U#@Jt`kX^b~{q1K>c3W*6Gzbrf>~#1v$Xb+Bu^Z)nGdKlo>xQR% z;a=L4ZaypJay238r)k{`W|EAN&<(;obz0SBXj3+?Q&W7T|INePTEfZl=qJ z^_?;-bb$^bKWn3_H`=z3VmlH4A zxK#$;DW-^P0tA@r{|YeAZK6pZ5`7VMWU&f3S##-P;<7Lc*xU2cj^B{Hfr-ijJ$!cy z?PqS~1*d}Wv{CG7#cK0KQ}ei4$wCPtQ+;$Y8%jWmfMPb&cnDv+etGC{^Fn41-|Ic$ zXZ-yoLfap0ZE5(i^u7$+@nrI?{qVylt+Jq~;+N{@nLk}N8yFvl*gGjTl~|R_SzU6W z3|b4yzy$G4)YIy!0`FPjCCo2T?E!XAT^(8Hhk8@U&XoN&_d6H-0UECyH;RL2bsNB0IwfM)+c2{x6 zsHe7Uar((d2fl_}hcX);i+M9_IVJY+`IROMasQyl=hugx3n-$6C4%1?9Jm@nAJ<+Q zNf*f}t@4!_w;Su!a^_vg%>8sh2gO{TXe$2#`gAw((KVwpZIg-YCguZ69i+dRU&GxY+>HF^&1 zUN~F2w#XkUd1EoAIG+(s4)xS+rV(w6J#GuSHbqZEW$ZaKaN9h%3M3hmI9vq+pbDTs zu{!?$1V910d%M?S5q`b@yg3e_jrX5FfS*Tnz0i@nc>AARr}0`bKTmaoiE>>>cgpA9 z%gOpW|NP$n^J5H~4D|f29uad%Sp9=^`#*HOc{tSJANMY z`MlrnS8bq{!up)qB9H^;UXeeJ+5L7Bq?K8q8vXKQ2a3pedN*1~XDG1(Q*T|Ns48y8m~T*MuInZe{IQr@0%J90WwmoK58d)Ajox zO54%DQH%a-dGK1n2cUWB$z8;p@YsR{M%RbgU@{X){#HW_a%n3oqbB)qtdq9N;_?nU z)-9$6IDC8rcW^)h`I~p=J)!5zvj>K{UUCEgts% z+3zWHS})z0#%12X|M{F%wy%6Tnsg^WpCLWZ;i)0cFa88$l69269N@2>H1u9QuWSyN z9DsLl^cr)f8@d$0U|O2OXH+mf>GY>Ceo4I}rN{?~OAj{&0cuOK5jiq*pRX(Bv5^5<2_`~%N}r)`C!`_9?{ z;*ZcxwQi!WKkM5Tk)H2I_cIQH?Md2e9G0cKyF=Qn`ylbl<`sQXQn%kq&I}MyJ#?!1 zb>y&}=#K>ofHv@(PX4Rk;H3k(RHbq?hoDTmr5ejIUb>dal~@ZDZqmy^f4;hNHf{Xk zI|W**5 zLzVBz;bMJFW+wlJpz zJVk-DT}>UXA?tIn@}PeO<=+<4_cly^JNgLBhX4g#o7bqhA;3Wun-Ux~v4j!RHt;l# zWLZ4wG8zsc1O)J0!@N}1Pa&thkN-79ig#QUNFEMa>i<;433M+{x)ErHICoL2)`+e> znxUIpG7~2tIX+hkGeuLjMVK5vCGc&4tB3Ukm?Y^y$-KxWvJib;8@W`8>0wnRfZLtl zw@`RshOJA_c9pA?K+S3vAFK0w>DGOE5T4i9Tk)w(pbrb&Dh`2PiVck|7Wapfj+Tfu zqZS!Foc>uZJ-j2gD^#0)5s0LOXs7)vLqG{)aZ?8G{GoaHvpfGZJCXp7y?%w*0L%n+ z-~W8<1WsxZz_HN{Tn&aGari^q2ajLIfUP>=Kv9O(OpAcmuWnm`sC!|5g2@*n=SqH1 z;(z5?tG)ZrxnWD74XAqbz~g(vA#?@9T0$pzY+gqRS7=oZA*;CSr!K>TkA9!gy?XDt zWjJTp{0ui(A<`d`ykI)G$wTtOCq;Uk#M%zz`;GTNg8on-wQS{+XY1_P>+La|=7AEP zTQl`cVs!<^GWiMBEVFM&#aG1exS1WefQ3irXq0=<+TT9CZiEjFcFd@@xS zY}Q+C@?_3%q{h0wq^QjFw8LM2nyjgfq)knfc%t_u#mr~2rVZq&>jdtN+tbw-;qLJUx#4)&F#_^&FxtCW+)BI`CDI}^UUH349s(y41PoNN$WmXA+o^wo#CIH=;EnB zdUG%F^jxB*xB>u()L%YYt#%`tZB!S_y$ZRXx$eh0WDL9EyGyyA5 z0&H{>=SBir`knEkPgwt^ZGW=gN#*i#@<=-_*g607I#$R(N`KUJsfrjB$XYw_`4%B? z#^jG-X%1a_2iMW)40sdOgFL{KehI8jfRgECpk;t~TAuVU0byW&!trOysDr5(mXet|1LVSOog2|h<^5D`y&Rd}7EBXrBf9we! zI(`1^83f~ZhAu0w?M*^PadUo(jqXRpm6jnBd|rx$>yp{k+aGVml&7}H>eYRFGWq~v zU~aI+zn-)YHjzPjx06y(C|d-T5j9Dt{)d5yF;s?T?zXc{XKl63Iu6WAJxVZ7HuT(9 z(qA#i^GyZ-!x#%tkyXz}?#L`6UignV28KX=5f4BHGy*g&3 zfN4DgNRKlfoO2m%12WV^i$W`I>CMm8p}2ymRQ4P&+7dqi&`EeRyPyZ4($ zOq}vmYSnFH#7C}+)I0ysnAa8`fHnU-PXx(h9|Q);@V!(Z9UgKW!vDyS7yJL%xUMx3 zf%r`ABaqz}-y;m`4FTmv+NeMimanP3;Y*-SgQ$H#dV#gS$NJ1p%A!zvw7(0_OwWhw zCda^OsII7px^!X^^_8AuR{`tXd+i*w(1vqH#V`oP=S?j38SWEHKs&k1TFgD=h{b$VCr1RO@--6H|iv-{MC~6M~5yBHn9H_OCs# z&>WR5cg4riRBKw>4nAj`kF@sE$7iO+CSVDcrVquqbi z_KZly9-uq^IPk=U@tV@~r&kcwFSB8+Z}>KE;8)@ChSSPOgPwit3uVP;B@ORxh0Irk z_QM%3sjX$rr)*?`bJ6;!omcr3+>NO3z3Ll_!MEJJ$I@qna#HxHimqhCc$OB!6OJdk zM3z3>atuLaUdZSakrDdslju!$H?7UCFDY9bXc@1*lmqkw4~NM$skMATujoLY8!beO zpk=)&&o&8kFfnb{hny15J~>wqqZ97WKK@3cs@fl>YJ3dYe5f$>yoK)Ub4jk@sMy`O z%%k;WvT@7`&}QIe7<}=th?j>#{U|b_98+HL<6N7%VCK8+v*!Ipn5xVrm|tP}32=R4 zjwV^9|LPr7Z%UO_XD&HO8$7A5Ro%Y8>93{G3D3F)-sb@Fl9wNF@R}QnYYY11)c2;e ztLJOp5rh*bC~>L%N^TFg*l^o)6%ws=m@>Ox|7F}A^xoEn!wXaF$50ZTqdVJyfN3)* zAn#;-x*}L>t%=C`$BWaM1OWAx_a~M9d?k0Bn`~%Sdr!9q_9~b#ejuG^sQNgZSwmS2 z)Tpobl+lKZjJ4M`A_@mo54&dp-|f0xn(YXcba_HCPZHFP1+ep+l~bPIq~g**#{$@c{PD=!Y7@Cs`8 z4(bU0!P-x@G)$d{1U^n2pS9uu!>EfO~Qb^U6+m4atc zLn=sRC3rW6oK_E-u~B_Gxp;s`z8inHgv-{WP=^R*0n^5*Uv|ypnUJyHI-*GX3fwWgUU;ylR*^Ox2)V*RGr`iYI)Ezrf7JQ; zu7XX%7ebA)8#H-ZV>9_v%wA-7;gCtzes`4)x9xnow|n-rP6L!kv#`@tfgG}x<p-&U)7@(Oo=@{Wgdevt@H9=Ik~<6R6f^XfRsdE#D&AUkfV_0bZH1<@ANvEYo<3v zw)waNnRG$mTJ4z#BbcB2W#DZ-#y(3sqN5A|aQg}v61pKbItbt}BQyVMHwR%mG^-`EHVkQA z=0e??COeI4+QdLHtT%WDWuVY6_?yX{(Xwz`V$8C|<~%5+bsfu@FWO~#)l#i@(r}Z% zqCm!QEZO!1RU=eE#mGR(LAbeNNiDFnUK09TVfBg;B0V6JVR*TJ!cJ@-LzZ4k^8CGD z*IgZK2wWHP_hMiOx2p=fmEfEw$nG&W@%Q_F{x<4iRwE!9CCNR@sdWQrhazDY{b{_( zw11=pz;A3=H61Q!?9~A#SpGA*X@`=n;;bgb6121yfadOwnC-iMzdQoyRJrA#lv0>q zl%E^8Aa*RwbO!8dCOp%-)q6!Hv%|GxR#C&QE;>yoIf5Ty0po!?w=OawXn?Oi7;n0l z-5W^%FUo&^eX=aYOZRSUM9nX)%Ex=)jYhX)0>(f0MNm`$qdi~dIN!ZdGY@{#Vf1h3 z>PJ;xeNHg+CwKxcm|#0zJ@45}{pSILnrC(y@zpe7AE^xbTDk$Uu#+R)9B+D(sQLEb z#pKbS`{!@H^W;O?B7q7(-B2m8Nb+$4gV2%t$(L{1q}2+B#$KsKvm1l7-@Eb&;-`7B z_^E=vk+b^Xqz0&jyVikKPCMrmjZRLkJ|HfpJ3+E?l1}^W2bmBY2@@^!gbZnat#JuI ze{X=;(%!J12o21KDSp}NSf6|4yD9Qndx;{VXSyh9<7jW~w}sZSHE?7VdCo+xWXCJp z)%q{mimi@+ovV{=v48ur-&;{S;5|IR!V8B>oC4SCWbbeeu8s_hyK|Th$d^rbpP&J< zbe9t{UN4y;+4LpSZRAUuQfQT9L)kTm>(0_8! z4IAN)Rx4U!9n38oDs5{u$O5#PPTshO8M5}kMr*Z@7Lpd|!uT|T04bm4W0QY!WTvpu z5Ly&?&{`fMhX`nVqV@XCiAq+K8r{EwLw=HmeQZM>rFG0C`>SoXE$tWM8~`fA&L@*$ z``$QdLs#JbTQsB846VxO54S8|t3yuYFN>Laf0)nv-AccgS%<1l3DR3_mp_YIQb|sF z>J9z_z4f&2MW5ZYtwre>AdS<~Qnp|d_Y6M5tbX-0KV@?}A=8|gw`Qfh@+n8H^r%ha zBCf6tol4v_nk_15ASlRHJ`FxIuoO(lHk@4sY74D_pjGAZ4o;syOouNbx|*3Pu1{%c zD0H#cyt`T=zD~5zHR7h9i8k$3)i!y?NvV%}`U7HIF44%4uo3cPn_-u*loKZ) z7goMwXbxyRtGm(1e@)v(e(Okj7dHapva@4#aE^!ko47k03;^3!Isojs@S;bw6%mR_ zG%(+xN7;2vnzm^L2hQ_bYf2x#hkZ%=wbZ>p>;Gc%k5exv$$E{g&-fKb@Ik^drcf}W zqMPtsJ#g76h^=qO6rU^Si5Ok&l|}`>6s~zwMg5O`I2=A$KuP^!$dzVauyIh#G@%3R zbt!o8I`L;#!TpSUx~iiwGS^Cl<}YRRtT*mA0m$;h`r*qNz4rzjK26JlPI!dkSULOgpgfqca<^^@$ntOS^qhA& zoC!t@l#KG6Rl6N$goYS>HXNSpY3fr7l;Y_UJ3ZMrO+c|US=2+!Ew-T=c&ZEAW~Qd5 z6h|<>xq;y)iWedM4X*MqaQuTl8ZojM4joiHqsUxwCj!om-S}MB@X?e3;uRVNp+bGE zLJ5ogx~EqOf;S3M0-Van?T!q>tv7hudSom)js6DrQMUZdAg3{a`E5YH^e{RZLZxWu z!~ziy;?__uqGVmBW&FGf+S#ShLdt$ri*-k=dWJtFNAXo3Em05oQdnnW=RJb6@i8F=hWA z9w*dDV=%ggQ%2MKg`XS80kP+D#{5o~3 z=M>Z7#eM8t-MiQKtbCu@oraNL%^77EUq&mi2W`k@IIM{qTKcxOZ3}#5Zaf^JGgt2&aaPeCgx{&=e^f7KR!nrjixZu_H^{R_s4e2vP zJC2VRPbW9$+flVl@~=*9>L%I?DvbB91sx)6#C37?Sm_(*mt8Dyh}AH-k~ehgEJd?# zzo6na=$c5$*=uf2ua4gPQppnm_o(HSTYdq6duo=!j4vacJ>FE7p$`->P>GcXXs~Oq z?s7J^3eX2z9v`?dhsLT!(4Aj3=ZmOf0vE|emFvJFZDQSTpWCyh#a;(aYz#zmWClVw z$00ujRrQ~DY@8$pClyU~l2fnsf>;s7;z znJP4N6*9=Ha9~vH>gopw@k0laPsYw31#EW}B*G$Td0Q{)4%jOv9X`emnI8vVwJM7C zgr$Jo-^bLjr@wF^T9khoWStt)d}`3>`pq9NS}umP3%+C07R;NyJ!yRb-v_#W^^@%u z>eecLcqdM)AxiUQ`xRclO2HGw;W+mn)9fY6oQG`{3Vq$@jrq>0PY=QFYv{jVM2^rc zz?#1MzlxPDFq0^PADDX$X}z{RJ+aR_CiNL+I4w~!wJzQ`FOeT4L3ahVs&qt%`BoME zj6Drdq8=e1iKR9-YV?cyO@5!$nJk9sNYvHB=i_u|5GA%Ve zZ1cQWoy>spv#PBh4x%oG2w~~qw~?;5)*DmHYbP8X{?S7!J8=x|Xmv?u-j0z9~4oclr;R) zuB4}25h*yxt5R{RTTtiym*-NEz|20_BLp4!1)k0}UtBsVxYD>m`6Gxy8 ziy4sY6m?PN`gN!0^NktErpg~}`0kBjX#BZ~I`8{j#3rx{xIsKoCVL;(m0m+fmd8kZ z#Iq2Sjg|`LK0q{MD_R?A^b|o#c=RjL7a_d8mI)NC;DE4 z#^Ys_Y4PTrP(@}-^WiJd*N&_+(U3KcQ~M9y8k3|m&d`JyN=xgA7pS%1X~}H&VH&7) z~nMOtSo}Aoni6!VFR)#S)!GXxTC88RJ~q`$8!5x$p)Y zx@`P6*{bTi$BIoAZ7k@g8>Ia9fDt*t%7ld6+f$2M)ytyQThnc-grEwuUsufg0%pJv zART4bT5^Xmr@cb@s&#}pfjc|s;FWhx-f_mErOeKFXww3W(d!sfErq zA0lp9iZjuLDi?$Joks(LAdAOsVvVpoSKnwF0Gl(M3O*{;hj~5S$A3gGC0o3koiu56 zV$_5<1?#i7pbZH4bWvMZN{%RJXW(Mhx$F6mq4UQn0ej+83GA)~vI(LR53jiGzFkf8 zTQI(88TaRYVf=M%WFb~c6kh{y$(N4SkHB6&CC2nr)Q~NOc=gal59R+FEzz+fs>U&E za6%PH^Z5zVhFxyi+6f|`$zYS?KKyjN?0XxCS{qsSsne)+wk~sz8>T#Fmapey@0s3+ zmR}f(e@2U7KWBAP9Kwn)7&*dZ?b4iHv3?)JeG}L$N3MkHDhJ+R7`NNp=?CIHQg=G02 z?U2fy3L1ZRIw>H`>NU|m*#=$qP=^nZ2ls_}A?7~tsyhoJbNa{BTv(?VOU>8iduBiR zB{;8bffTXjFZA|?#Fvl!V&jGoL_<@R$u`xO#R|DT*21g)&8S)y(yIIp%)A&I9G4Vn z=^r61KX6qSiQz)X7nl28IUDv)_l=*0--IQzIu4GR0tZ?c=NPKFOwLV@9$Z=cwkgyVm&E7cQMw#!f_Z@Pzcsou!_Ql#?)=7#l*DT6P-Fd9een)X|N5EgdXSMfxxP7)?K=B-WNAW>@ z#RKK%5Unn-#HU6)n6XeSA?QZTX2b_Mqlya^3NEqn`4kYOmx5d@Ji;Qy7zF zOkw1czjCPNKgGR@cKb!ItS>T2@o&{Uy@}9QRsIFRD0e`vL5`uh-F@QC?9I@Rw{)rg zfxjz5QXt&K0a1N#wj*N zzg)`*(VWz>@cnnFuQ1?^rU8pR&zO2SH={ zJc1_4w;awhk`Z$HjDPWr3}SS z@!I}!B-Z8uzW7&m*_f`fTg0l;2v46f$C8+>ck;nS9rligTcyM+%Ml0n?_EKv32ykK zbk`IeX|}>D&Srj%Q*i1lZW%iVbC$$586r=NrYYQC-Bsk`Y2q+gVrxx#L^XBh;G#rH z(=iXv(1!Mna1me#yOuD{pk%4snzAvj&7p355t)&1pWwEA;VETc;UY&4O|)T%O?Pet zoT~SaN8coO3{J;9bbQ`tCs)*R^%?vl=OH6g$1&`MpoAafB^XepP`fkjgjQK;CBo3` zHh~e+#dS?b<)=iy^pf51Z{F{X?H*avT3+B#J-(6T7Atl0xL~DS^hjSssr}4%Cy~Em zk(e`EXW|ife+zR&tT2fUJAyqmUaq^%t3VRoHaHq%-==bB!-nLGOtCG$k=ulAQ#hK! z`YXS^4Ts-U)bx1FmqQ6jsUO^kWVMw*Z&r|x70na#{3x0!moAaiYEXyY}Wg(b2;fZqH2~ zm=tVQ(|-Tgg1EbC$SF%n`O%)ZlSzBm5Ac=`qWVqQjQ7>**HUuLIkZ;-{GZZ{*zU6W zIq_n9M0UfPbZJ0Sc<}NZm#!P|x6*A7Vt$BuGJEk;Qgf#YDhE15VII=n(J@&U?`Yd9 zQ3`aRtgm_>lhc$ON^8ZiYkA=-Vmhs3IZXRyTJzq!6~ExF>WN;~RA%0rKjkLY#Rj#f z2`)(ct+o+=T4}%Km7806a)G5)rO65A&L(Ulck$8BS;PxsI^{4C;#JD!UNa@m4E@!z z6a8kr9j_}mogVEEC1bv(1b<$cd1h~uiaGdv>*?%zj)IMaR9&oE@N2i2C9UZdLmPGl z_r=WpFXct;EdQ0Jc^CbTgfqX2{6jIieyzT1b!O%Fe#PDIsTUevr>Q-9ez{P<2|<$@ z*r06uG2Kk_y2X=*&UPJMi>ZbPS|-U#amr`G=enp|ME`|NTg3)1^=Vut8L(0aOakBg z4GTL8h~s;!1k<2=naKY~d;jIZeP3uoL#GKt>Z~-eqf4;qGyi9C6xyOH^d`ilKCCyX zS%BN~@QA4+kCys`?OPa%7Im&!@JCBj1Ci7BZ)jL%$HE0C&IG}AkLbr7hs+30>iBDk z_F!VGHl0?@=|XFBmJ_lutX|(37$D+3G8i@aQ$B#rP`jD>qg{TQotDkzO6E+F(v&|! z$d#M>6oEC+RLU#c998Ew=jEAXBVHCh5FfB+J9`H~!GL<-46JCYA84k);OU|qG|(>f zA9j!!Bfol+6-NoVdiU959y-W%o-XCR>kMj7S!JJ}@rC$ORXmHU`6O<2*84;v)QV`r z@-l*Tjpm_|5Hx_vD_d%n8^IL`U>Fc<#w^hC@ql{pnGSMsaXCBp4$iq0K!VuR*2dJ(aF4l-LA-|xqA83&x_`L(Sof6X z*}1Wix^B>vnNk;o_}Nm%zw7meByK;ra=fhlJGW^No}(I+PHR%X8=BsE7Qn%j^)Fjt zocFmR&FEf0Z_h9BzoxyIBl~b|CAS5nDHI1aG=3P#KV3XMp3e0l*l!TiN$ir_pJ-~? zEIBtHqd`uUm+gj{_-wq~t)_#-NXWLFE);rUm;&|dc1vHP9D56o8_Eu^u}>?L(H4Ir zb~-8oE^)sA|2JTcZTk``Ec%jE_d-IivqbhfWHbvwF<9-tB4$ew!1S1C&~*GE0>1XC zJgA|KWu8YxBVu_L(kDtP}ZKCLy?SF>Oaf-8lKO-CMMrtPs~&y^_8K< zoUfj>qDneIavAM(a z#Owg;^60yam9;wC8sNf+9&iQI>HMG`k6F64@r(F$^Y3@yjQMBhPsam#-WteXT8A%` z$3UuE*22x;%uMMuitpOxipL;&4y!Abou=$Xz18N%t(5F0ylG0@0KJe$(|%bSN}Y$X za&G}}K!vEPz+O)ay4V3aJ|1ZAsKLsC?^gyH7R2U6PtSaL4@Y*;a4+??9aBuhsLBJQ z1LW1yXdQx_r}VuKga(X>g59EsqVr5ab{tC#E0B}8{vz2~x!)Vq=^jgw3Xo?qGkp!O zTa|o;tAF01yMxjq)XV2kQhoaNQbCe>*&vN*o@+#sA(BbTQ4{=H!z3M|ar5xdsA*cNGaTz@R z+4jP$tW#+&gMc#heFC99v{$tK#-#6*2mWW@)~6=x75t~uaeDKKcqXFC3p`xMY=L)v zJHhs7N^S0nEIa~^9#1$lVt*GFvG{8IQ^04s=tH9olk)4@nLXDV`i7{f{B7(yV8)X?i@Y!Y|{C(FBewwciG&INkdEPSpo7DalxLL{CwnsU`ya>*R0$k@WVio3X zhtAjL2x-0$;Ije_6KYQn(}3ni7WEi#q~QqWQ{)YlK_hQFLdmmidb_4G0662WcdJG z^^QKK4$hw3v(fdGYML44i~|4e|i4bCyznIO7U+LQz28Li$lJf{yl%7Oy_-qS|&m z51|-4$H?S9_f_QYOTsJZ;Us$$6cy4p7)OLSzMIeEy3SmWTIsq<&rQ`@;{h9UosH$} z$*P=vvYYr~HyL9x1R;cODxRB$_6~a`s3?!GqKV=1P}^*_cz6(TSN)^b7?a#<`CW$F zgX=G(tr>zBJH$|;`_Rd?I|WXN1!$)kBJrB$J1C0g&W#Lu<++ICP0o*o<1KF*->sf$ zw(qD6rf_tCZ>k5SAe(&N_CpJw!JuZm^C)eQS@(^m^L|{n%H3s#Y05Yh!SkFVRcfWv zA!Rg<37EN*Djz7ie!esx84U)AL5W$R&F*)<*#rJ{L>TLOvRX1YbW7mGJy-k)kiiam zKTF0talk!UtWEJj<YmNoDa>Vx_K&wlm2>c&MsE9SYcZDM;w7KsYDc}WJVJlZ|&F0 z9JSaeSgk}9!}^ERvR$fd+gP^u$g)0N$`WBZ{8zwePOCa!7`6YXDXed3zn#VE&ppY@ z2XyRLQJbwYHzz4GXN`FwyLgr@cjnPKJb=)*AAb?j-Ae(WR4 ziMKYR@1c|vfV=%xX<|i2;;(USXf8tr9a0tD8Dlr?#otz+^NjmMYJ_E#%D5FpvK+G`uUl z-kZ~2w*?`h(jNDdrN_wBsCU{a3(R}-Yd?yO@s588{J)guZKJYM1(8R&Oz^ol?G z;deT(w#q$aK8~t7kg;{jvFBjsWU9bBPl>lL83mOpeMxGX@!1*HYrRhDYWbsxzy-*@ zs?ax_mb%tF!6K@LSYX=cE+OPd<4p-e)Dn3?c*mxN#bx!$CBw$CV)?RIc3Y{)P?vfU zV?pETrTWzHIaY!s@;c(k<@saRK>J6`)-=}~Wsej_5{Yeg3HR3TwcCmpy&@ED{# zs(eMZsr9K2{+%9haBJFo-Nu>(bn+^89+WlJLT!>|rVJh2X6>0nWpUC_#(86SNfweM zVCuOa=9VQ2Pw=*{9g4%W+!&dpv#z#V~E?bshW5)2K9Mak#|s zK|)H+4Rh5-KEEeN6(-1OsO_-$L=iGx88=^DwwKMw{B=Yb5ndGc-3WIr z@vFsSiPgQUdB(bMMOmJDY42zil%Oun*S=tDv*DwP^mipwv|6oDtG-AMopK0O&8OlP zH65hwp~CuEQ!!V(%ScMIS%N$BQ5>DFXUtaI!P~9!RevrWRWIRDEL#8VNi-IB65EvR zHhK)3ZL6t(TYp=(=0W?1V9J_$j}6AmCp&};r@A-ItK}o+WLntdTN!YaOJ-lGDvljCp0xU`!hQ_H41`DA zr+R)2kK-0%^7t3U!?3TMwN8iCAC1CxpC^es>B++8M>ZV1PnQPF>#$7qTXXwBf%dM)37dMrAbgfXI8|-JC(=G?JTzba1PH zEQ9=#X6NvtCoAzT|Dhpzi>-ey;v1O;J3ck{r83K{n)n67HSM(*QZjtA@5$(jgYNzT z*T+TWXyO>9B~K{`G1XACPmWj?{-#a!VF}i1EGFlZcQP;6Tjmw0#paDG@t6!5qvB3) zu8eK|p$-iz?M_XdQ>`JxJ6(1& zPThJ%*md4wj0~3S83AVhzPoZq2XZwH*8$2Ix&o0iDc2aLLeHF5L~Bv@1=)Ua@ATt1b?`Z?Smi1 z#@%fk<}J8G8auM~uOf9l!UJvOe}1L|`h?%xWN~*^OTehrkmRprt^o@lRY}H);-?}O{koF>dx|)E7g>&u z_&>{p75~-*SR5kAy!IdL0LG3e_u2M5LXZIvi|#n+hIkW>clvy;hdA1_ zMliKiJZ-_J)EhLN5LfiwY6*ZVlfT~s@>FVrO=Gs z0R2iSGE1%D6(Ed!nGetr8>r(&#C}GuS1k#8Ji#>)3NW4mSz;YH5*Is{xTAg*W!K^_ zXCpLIxM!s~ob$IU!$a^D|Yn>j}nMD**W1bdg>K2j+Re z+!vVW%koLa>j2!Ot4-Yv-6E0VVTZNJ0Odoo7IZJ$+Y28yNzcBgiw~K75O7>NQl0j$ zkIpG%eTQhM$2Zb*?5iDlJiZU4vzZtDKs;Mwh#k__v>lg)ml_z=n;l9A2GalWdb#{3 zOXH4crtge;8&Llm|CHO8ZJTf^A9}&$0~{MHVC)!32I`m!H*E5>0pfjPnP~&KQ+91Y z3T*fROps%aU?BEvW*dC7w>OVey-N@}?6L9Wfb70$r$ zQPe*9IT8j>=i!O%@j(cS^_yG9fs$4Wdv~2W?!I_Gv-1>tv+1zdX(zL$RafIy^58k4 z&yR&0cki@;Ky2wpN;z2<6~DWM)GLxFnS!_T#eh%G>21LJw}Rkrh&eK*hq${ermpA( zLSD``XXz?23fGR!w1G7FBnc}o0q}~MlF2qB@cjr`KoMy(aVo{VXk^+3P%2hdb59-8 z3mUdNb8S{YJnW2w!!w+H0SQ4kR(89S=)&AJRyq6DI*#O&f@;oUnFXRA386CJLnRtA zcY*7z_rZI@AqnXD=77rW7Wlo&X28e7QOl)NOF@I`g8Y#6BgUW?txO1k{&K*J53Mlx z25>HK0Zz#>+ez*k9DDq*o=a=>$;H1QM&>#h<3pTxy(CW}dvQX4ls9ZU)=z$z;?Ld$ zNhANES`p`qE^f5!PX)Nq+_!r;2X37cOmLJ12&)Z>7l)(6z|%y=6jjUc-#qvUI4iY! z*@Yr0=H7nz*&CNUE>B+~5r9RnK>7o?fVWm2)&ZlH_fb#>3t)9=x`Qt+kre{GqrW?j zm>gu$&woUo)qeH{)_L@X-St^r2A1!)6tIQl-}q{g97@>-2S&LO;ymAgAcE6N-+k<9 zc-`;sYn;0;Kv>gd@<4fptXEp{*X{<|czq6F=hU-Jx<*_oD#$8EX5Cb}2!efXm`^}F zfk2ZF=Ct!2I4I)FfM+!5Kb#!-(>1rn`~Z_KV8u$8*z*jpK(dF?X4rK^=tz8)n+T(o z{`a~nt9K5F?H^h@>aN6;os;_$xYJAM(c>esJvB*=62W}^3Ix=Hz76Y`mBE^wKI}(d zU|k#X{|7R6S9NAeMMg^6^?ka*olN=fcJRUf^#Q;>6+{`^rPX`|nm%TxYLKw`EF(%ox zMID2?H(XYta2e2!^m|fDGy}y`$V4skkn;khTh%|SZbi2IkL2>6eRxE9CA|DGi`&of ztcB&&sSouBGxLXU-J0-!u<1f;C)6B2fl!(3aebft9Kzd+65U=d$Qk@KM07>UxUNx` zA%LPn&|Z+FB`rnJh**TkCGj{z&NUYS&AlWP<%vM!#1pGRGs@iRCcWFhVxEXH zbb6doL$Nsu*Fk#al@rVCF^ozBm;P>mJ?SQ}lnRf>cmr3zpnAD;R16bAdM+NzQc{L7 znILf-PSli=40Kh4$u`F-cu@D7`QC*ozXEHS-4(D>nf$yrtLacZ4iwV^N|N%3cV`BN zi5Y8c;sm`o$Kg=Z{83!f@OHhfM+Ez^pD@$`Me@3adxsbb<5BKU&3jCoC#J=UaA39x>A6_ zpqvO=_#Gp0Fg513M}Hl;RyxrEFkHqLuH62p6X)CHxHyH4lUaYgdkps7-`A{ObM;c< zM9a&wIncPc_2R>eYjYWRk6SOF_J7~20zFY0Cv8?d*py|NTETQt735sQ(CthuCI@@or)#YKc>-;Cp&LcJ6sl$lB2}_cg zK}nn_bPlk1ezWg!5D1@p&o%Ml-yRp@M>qn(+R3LY?Af2zkFC^JxQ4fqt;lnn!Yg5$ zhSrO*wBVEC~1;l%MsmS)Nsk4w)V(zyj;S zqZ~LMBk0`aJAeLwDo6R5iR0-hB1hJIA@$1)9+uIfGN>cn#9gzS}XZmQA9>Le#ZnG`R@95&in)HlG_eCLeD6I&TIeM6rBXW$kLJ1-5MRn0ABIRiB|ZJzW~P%_ zFlUa2<($rodOJHP;Jk}{e!_sE!jU&yi_N(#&3s6SG?y3Ib7Kv0gbWF?IU?o!x6q^0 z1429(mk=ScDWm;V0A63X67?u)3vh%6j1JN}kKL{X?ex~M0MbFhmk3j^>JQF&%q1W$ zfo)(^+3$F`?d2knMyOdr;tR)ZH1BNT%$8y}<#&FHwQcvn6_~CYf$EctRsai>o<^Xt zJ*2re*Ybk@BBP5uW880mQ%ctz_9koiuEjF|I`9PV~Qm-;Ew3h9XffN`iXx`Y#7Lwtz^~nd249d&+)npSbrmR5q1HT<54@*?;yjgeiB(}B z(*Qny=C-}lZaLk^UiQ2iP@`VbxK-o%wyBVEvQOdke$@~+I{_=veHs_?=jK|W>fTk@ zga!jya?gXDQ<*;9f}N&y=fRvNfdx8S)cfSv-g{K~;SOjRY7JNr$33QGmX800_vi^U z`1Ie%zTYZ1DO{yiP8Iyyi)-(p^6TBT-|(O*0J3`vz6~$#JK#S|`HCetc|Jgb6+?aY zTiM%~5a1J28Kj%3YvQ%Q0=e~PWauv0!*P@ryg^pywvz&yimO+lMBWBL1|4QNJ zp?!Wa0?&n#Khg)=c4o4~iF>S|M;lIHL+HH;+YdHIk~&PcXAjodju2{c`M4VDT376Aqv^0fe*34BweJ# zs}ovN6~&z!b7W4>N2!nwA2N*)Wo^y&sYUW=s=mU1nNqRbe+i~OY%mw6_!#Cdb#gGv zj(7{gOTVy<&l#`Xh4}^p{Ae4{LZz=Whb{%oGh2@~CN?1b@?<5pKxyt&SMq&XEJTr^ zdvQ^viD~C;Ay)$X1Y51<@a!*{p3@i)YxJ$^Z_?jbC^%U8DFpj3+o&jC7zlGOn%q8r zyFp~t6d4{-Ya3qL{l&FkV$I&CQ_|Z_UfJxMmc3dc_oHYU3Ht`e6k-M09WG5MmA|&C zgml>X6u$S!MF_gTpA?hcLnt{J&<1&wUY-m`mCLDd{coORn=i-_4Gn`;Pg-><7AU=d zl(U!H`H)UIcdH<1e;TQpMZLw3^s=I13hf9PSly+=mbdvlOc_TKAQzsvjc{(Nu0Pk)q~xSexz zxn8g9^>{uWkNZTB=RLi|6B>^=Xj*uiDwWV=;D%#jgkV)*gut>w29-E}dtROJnurad z4bsEPzh6MH6u#*ep?b$Qc*hd|E$C)Utm@Q(+4qm|zt94b^ZwX1n`d%mZn~Q|{C;j; zmy+eW65g3l`~}=q@g0+R66L=T^NZl%c zr3Fd?)2@uzxz(l3M_nk52JBHyM;Oe532O1V;YEv$U*xM_wnpO|UVL<|I<&+^?3qR1 zlwyaQ^;gq!Fk(7H>2|G+%oFEWJm1KoU$1gSsJQCFuk%cAdm=W#OA03zi&oL+EB#l% zBenbQnE%}bHZ_4wfYX}qgw@!Dxn9$U`sj0Uk6`y>Ixy!~pfsi2+jm!k4I%tz$Gw%& zCO?YqlukJ9m&zx)*4?zIAGgU#=YVr!rpnJ2z?J}TCI7?xU45Aua6;gwF9ag*jSh+j zwXyvpAJ|?*eNinlv&AMF7n9Lg{)o|4vx&V10Tt+U%tuH?VYkFy689h*z9oZ6l}M1E z9?hcnp?nRZkH8#0_+jB&SlG$d$-kg2&1GP-dhAVngH;|RvG)fcsxr-q4iJN6%FTdV z+ZJ1cyd9jK?)l#1n1Nw=@sG09p6Xo>W^#zA+&LBBaRXs3wc;+TI!FLZY!0J>yi2|1 zcy|iuN}#D$1-kt5Ie`{8{$n=n?{p@zpTvTXNQ6&AMIBv)`PFBdXb!h;YB+}qm~sm1 zt&-eT?f_0-k=VPZ-IS}D7h`E%G~9~$uNTuFAeVqc@*sk7mA>5*L8$nzfA$ zK)yGv_X45@ndiP=lN=kT&xHEVyCI)Iictw9%~?`7>Wa57TA2!DEm0O0Rj`sz0E&bDs90omeEq~daq@O|EG#Pva;GJfD@ zQj;5h+cDrb*yrYvPIA+b6eU!UhBs?+qTA#XdHv6aP%+~2fv-Nmk9$c_V-V~HYB`Yd9;z4c01_`*+hIR|JxgdJ74uH+9{g_&5X?%! znMbQKd2E*TO9szzoP+9Kp9zGUhL-3k*0?XeFUZ~D1HWQ_E5_acLjNr~5p4vg+kuSc zgI1y|D=QBeNIid(aXog!vG4ez16b14ail(T^{Q|-d)&<_p(OwnoR*>3v{TmqRWTQQ z9`hLRyStb(@5D^Ag_x@Bqy{;d+CqXeW{@fu6p^velA8W%9bPsHjh^IvsSU%#kwFD8 zvSJ&Q3DRkSPDPkCMP_5)+QpXh&2Uhs^o9=$5gSsPB!uZi$jM_6NfQSE=oUrzlIS2e z7LRyOwY_&F0cUI(0fN}y&RGFMLlr$cUNX&VMHAKC{JrV2xyUAVH#>Zi;|gHiimuZ9 zYM))xgXq>agWS>v)NL17WHPsx8v<7BDbe~I_PQo7RL)=NpFQ$txI%nnE9~9UG$P% zi=SB8>%|1+38*cJz_y2EBCIs-a|=&<9D3BB0(ZMUAwsFlvsj`wi$u}}cuZ0=&+^n; z57K@i{FzAuh$`_T?VEMaWG040gcYjw$L6{{5 z2sc^)66N#$9Fb({wGHWgj zriHnM*bI|iB+B?1M`d=A88I3|iCA*7Lv$4n56o{vQE3_GAjE>Uz+V8>Qcb5a%XDlm0!= zi!w2`+KdlvWkh!UYnPdE9o5SQ4_sTKW*gwTNIKrLqN1v1-Y|R>X6X)6ZRUzV|^&sO1O#SXXK+{%JzhMS+8m^bl)MC&;tq?i{fRdm}>Ew;0H%8nJJ0R!&KRMc3r} zsg1H&!oeAYhkxGYNpj~>?dZoqjp;v+Z8n~?hTggmp%s&7CTs*~;Pcv9FWVUd0T|93 z{LM$QXt40;Gnb6)Y$_xytwIRAl*^aum2rnZ40H-OHMWSq1)to|gmeIU04<)m`r`tk zqvJ$^#dY`!xWSjI7R#l&Rz9sTiT8AO5Vs5khnanqp+Y&1uny&gK4AC;60HRj=faUt z9Epaa+$jYWGei>X=E;IwhAE#yb{u~0&<5QwaUlIoSo{j(?!`8|5`AS3Z<0CvFu;xd zBca3ph0ifr>{Ay~ca_A9vAPo@AAT=65d>v-3IiP%Ojp#sO1g&wGA|G{GOMo(&N0l3 z(f;4EV&7b99|RreQMpp`SDs&fm%En-*BBPnTn_rP*c}6>rf)K@CI8p0(2ocMttfok+9bh+Lc@T3MqaIX}kz^qigOGHTauUFk?&ML{>rDl`fMWa~wk`SHKjEWv z6%;;c!r1~PYI>hRqv`DRS9wC-pqxc=(KqeXqnSo{iOD-d47-c*R&;WVaNl zUreL@(7idmY^elT%D-n3lC|ZI^S_A=FVg{qbb>39pj*D3mA5w!opKh_p46icY^WNg zl$D`MA?`g0zI8he7kWu~Oc1DrND5>iok49w6Tn1+6X!@IX6WQiPee7kg(YZ_9eM$Rnn$eoOJ0uK4F2p1h0zY!|^~aAL)fGUKjSkTM0qwnnz%QA%Z8T z`Jsj$EOBF%3rnEdFF1PgDzd(YFZ_=g$(X?!gCbeC`ULA54L+M3MsjMk6^xJGt7?1B zgus7q95tqXzoryu`@{t~ey&71%doE%b8-W&$hU^Nm{zW|?-hMJl4ZR~dl^7)iD8j{ zf|tUNpucOg_Cz^J_pt{ zwg0oOo$Q)AG~fK!Ozssaz0?zw^p?Z8Qa5EE)mzjd?W?%m7;wV=DZez!tNZUCzqOBigVOV8bgH3fns+M%(@($(Z@*bqGy zH)d5Lnj-*@D9U|(OfSf@N3wu8BQ0j^r1j&M(*VoEB$j6I4m6`bo}-_5^1EFT`pX}Y z?8+}wzXtVu$`&Hns5IUP)y|qn(Ab8wZsJ>v-zK~1!2Q(0^6nklZeb6I z%176AV#Idkx24~Fr4F%34&hHNqc^}r7vAU#mZRbxhyw^IclUuu=fq1zZG@fwNxaYH ztT0<9l~B0+O={Su4Ax0{#N&5UOA>Plj&IKgRr6dugcu~za-LPe9W=rkvkUDG+CGhn z+N365Bj#nX-e@B6kz;HdeKZz#-%2@@G6T&2B<-9^8zdp4*iji7YkZ#_iX%RIfoc+B z`w#>Q2}1fIN%EOyU5IXuN+Pqv2xl%v2H%q>-vAMZ?%$f6#{?)Gx*#$dVsv; ztc3pYxSi9QxKc?bjThF^Z&T+WL58y(BHDbq#ricfCHF=Vj=R=8RccEP=UIpbmbE`^|>=efVJj zwqP6CI8OQ5ZO0KWd)g&%w89fUU8bAE;_(Stwe=S&L7Tuaa*M&_yi0tHXUlT9Fl6D8 z`wn;`DEyCd?0MSJJ|XQMbMnS7q4Upsk?WJGb|o(Y)p8YK?BG#@iNbyVjiJXEtxE>f zC^RxuNSI2|sgkWBANB8^{BJj$PUIJXs{|(M6xNP-oT1!_%z7Kug#k;iySD$%-y(md z_0W@{Bt-SamJZOll(mg}BbP#byyHCPjn1fGaT@$XJs%=TrOJv|H9oMAyz%=*T|u|Z z3V9Cv_Zw3RRx~_0Q=NJwqrBkrMr>nk+s~=c6HOmTOvxRY<+Yx|RW&0rm^HM~;e17E zk$T@S5wtvYNS1s%ivsbfxe8Ll0_eF90~}q|_(j@xYuD)Z-5J21w(Xo#LnWeNW=#qG;pBn^ zlo<9TEemvej1GJ5+45WmyGj0_*u--c>c}pbsaGV=gbYxEG=;>vC7{>ps(sm#NM^jG zfjIENzEfBaDD8a|%KLDXiCRA`;^$l-XqZX`t8*IkRMt4V@$nu0fcG_&rKJ3SkL&&3 zBW1ElG|0Z~l_g*N1V2D{W6yTy*E*0X@sFoC6Kpk461fP+ zqI{=df*QUij&`C?!t87~(^!70U&LQPNmMexW1<260=DV=;O+p~z`T3>N(uu#V_M=^ zvNU>7sFZ{sY!Yvs8OH=7DwUZ9`;8cRMKLwqcSS<(lQYP{SuyE>YT))Hd0t}Dw8B)B zohf{;c%CIL1GV9%u+>rv#r`k~NTLJkb$kd`W`8{1gI}Grk6;ElYy<5#_`jm^qh)21 zZJ&#~O<2UrtrV-<$SXb|@B)e260bs#^~*`WT0@2UzJL(O=p%V4JUl9-SWf0;`k8Gg zDC|qS+A0ERPp|=@FgW9Zlagr+@g5J;{#(~;#Jifo$PqGI)MoODpuR4eTUCJnBolTS z6bG~p6;Tt7J`bIhI)B}LpkJf(R_;IemG7Sc#T)9| zB0-vdRN=6lz4B+2Io-yvVGbwWj@Ck@(*!TtCXJ>?3H+7VcC0Lx!)TSfDJ`4MQ(kSt zxBshLWi_-Z40+>Slu!0nah8zxiboW=quke@Y@dSEQ{ZkP=CU%{5oc!0lQ45;@!Jdp ze;`&&$I@E^I@o&ZJL!$!1>{UI!}cR!3i7UWpME|Kj>|AQZ8MTfzPBAIOQDL_O)6!c zUKD-ex$pI?<)laf?Gd;TQIQxvFM9v(X90!KW6{ef`u6jOnq*Cd%y)l^(H9}j7l_X& zBDGNijg>Y1?;#&2Bf&4n6=e8GU@q_WYSP}HperqzyVdV9rSdg&e%nc$7aQ$-e>>-{ z;KyyA^o4M>yC4l1HR$K4!u(T=kA_q$vka<(w?PGi#qX#d5Qt`G0fgeaVG{V|@qeQH zmG9OI7GDIOd&;3pWZi5w*0{W-p|;Omk_4HPJ1^Hw3n9orST)omj}n)g-h#_}LSN1M znend*PMjvDZzZO;WeL3P{dYW)HT>Hs=jo7lk8CgG8PGLv6L42NMary=e$VRzDfdfx zIrQQBcoC55@^`$O23RxGS19KfmlBi(B|1QrOF~0Tr|A-Ht_x2*53=P}BIQI=#3wz)hxcW(E%d=e%*zX9md*37=^XY=7a<2?A0eRjo$ zD13lXAZ$ebiedO1DK{15mL+bn(Rg*qoxJCsO<^PVBggD9K69kkI;Sv*(wmi0BF*pA z)$Dt(wYU#m3sho{!NO(fne_%WKJ?ZYx>1bA=hVkemjXuLPpw1e-BVS%N>!WUykBud zZ{Q+JvVpx?2#M^L9$2B>|N685n%@~2$dTjRA{8Nqeo`=-f4riSWE(=iFY-r&#`0X$ zG_GF0Jf(uj`-FME`?&3HW0v|s#FMd--^EYwvI$7(E#L3AhWl9E)Q6TyW9V5rzK%?B z`UlyL+S?Q(%ogG6kIfz|G05pKy9V{7(OyQw$py#04M3&QaeRrADPe1FK(Fy*d?Y4! zfj3pJK|gsvZ%-@vrQR;Ll754q_g-|D3CYH@Co`0W5n+&x^5?}2;%&$4MG;mG+u8qX zK1v@kQ-sm*4Tmf4?yZP2L_gX641V`1v4t)!-1r(_{R!}bJoFV~HKt*P(Td9k5q(;& z!u_L+{4)+4oqdjhEQAx4&!mNZGRDrBt*UqiuFJ+vbC24VnGNHzii73=Jv&VcM-(9b&tn1 zs5uBJaLwHT|3NJLI}XNDbVyLY9D6sZ5@`v_jzsuVv0yC8K%~_vdX0mO zgB)xSwzRH6Aq-bA$brf~KrK>ykj`WmXd3&2Xxm5u956as^^p#ax6OFKr20JJFmYDK zXX54XRcWZBHs!xThcLS;Bac7u`hhY@S^8E7S@7p`H3gmTZ<}}z3OrjUU9N{kNP2L7 z@XY6S;2cczBn?IN?tljIeH!1SLclCbC8n?sOnaVhx`w7Xtq3>WaeJ^0Lgy0PGyvLz zFDJr{Xb1gqFDRQ5T*c=8cry9QivZe)V=1NGEx&BWTZHc>J=di;49t&9$WaFIFxux- z3LDIG#7Z(9ZS@_RDYb`rS};=>IlrlwOM0K}6fp;qb{d>ee(SY<*q9Xu=ojKEnd$Gd zz>=!#ih-35Pz_K$OY=97$~+dU)(*EXAKV^(B_1xACWOQU)vmjH%PqIP+i2PT1lq}y z42byag^MzO0O6KR0{TQDBf*8cwviug+p8f+_8qo8Vla$427)AOT8_rL1W{U)hKb$? zd9O!!gOI?tbMzjgD~MEgvmY0oj1Juf0pYLkQbCp|qqP+ehzz!9PcO+Tnd+GE)naf{ zj>0MEY*pIW?SxQkg0zvs>MrsyD1#MG_nsn~XsgnzjCd95edAjgDa72>HLm@R5n{x-t#79zg@AJK;` zz2>&vEXxz!Yj51focWafTA!{E_!Uf~LcvMT1g0=Ac0{l@nF!MJbFw@2alRx}x=YA8 zv0xI}@oV8bhcD6`!ZwNM^O5w<&Kn!7r_C5d%oi@_SZ;myQge3&{v_kw4l7>xu~_hY zV5vy%cUxf9Z?^Qb4BZeZ9S*Ua<}%^aQpM^qHH$FtIc~(eJ04};o^>dr|Q2t=Q_L3PT;wXZ-#AFCuUbR(G8;wAkgX%y4T*2 zWTK=Li?7K`7FA_Nc6gShUkRbRTvqy4b#|?-d5azC8c>{is;P&1||U z>GakmA)-LCl4h>Z(QIB6Zn3f3jQ%ulW})4BB4ev~HB4fl0XIk=M(jH$N#skK&@%~S zjZ7pzCbhTCsk*eQEHfyfyOd6t;CUw^xI-;<`@*i-52Hx}B*+hb^mJ5PC>~HHaLLSW zd}8T=m8|+p&Wny9Hbn~a)uXn1UrV2Z#s4b5!vqL4O))0dcc0us74)4$K+g0`^+%KI zT823w)CY1X@6-2Bhp~L-X||V13gUusxPn3SlTaeI&_%0-Etv6UCQP2zjjzD`fhrkok2 zowO{hA?jX#i?t@ipGx0LER#12#6=vx!Wm1pcnBg*ybVI!_$qBq;+5r^mg1SUy?0r{ zxoMN|C&3>~_)Iln)g~EoI3m)}Jw79`n)wWza*K0No;SIKN|02W00fj_ZT7H?sWpC} zQ)Co=rk{u}$Vs>?*MmPkD-h9aKqNN3iH3Acf)ld2t_D{g=SDiVr1*)5n-5N|>eZW* zsG|CoOG{m@?efp;U9p>|U@>9sgGVBV3SH|XS8Zx2mnNtSqvd9HG|Z)TCLGVE7Y!mw z#Meq?Z2kH8g$xand9-Y-uLv@96}fmPuBn@KX{DHWF!06k%?r$i6)Jnx0V{I);zwe3 zmgBIs{k9EiSquV+Q$Ih_QCr)#Xq#4`FUMYp>WSHu*){PJBzDIDa5tR*=U09q5LW*N%?^Lt+_0=a|zYgRHqZ zlc>b4&G7GMPif$+Ub(|BZ2h0vid@}_1Ez#MY9~LVLNm|Zqi=W%-+e!8{S*YT*gYVj zKUZX%(A#W7p@L`3x%%##_l1NW`6A-HcD+)C{J$hy3Q=-)evgshJcGqS%}a#KZjgMs zv7=M{?(c8^ZlS~MW|=a&J99Q|*}yOEf8Vjp|NG$T1hT@|6Q)UYa^Dd6j$g|@C3RUl z%L%GWSh^-X=BgLf+bWh0^*V$)gsF|SPo;{*`4{i-UVrKJPvo+Fsg#o$%`3)d`2NQ6 zBNk8mR&t`H{PfKX6+(So@uZ2xwSW(H^4tGO&bNi}HR)ZyJf$FXZao=G zzVq1Z=>T#R8LM~ww#wL`SGxawfB*SrA2;6r`FbDW>svN_n(~n{&)J+mW#*~mGNsfx z|Map0X4<*Sin#yJ_|GE`O8_5*x57a2XH&D8W2krFhBu)kjMZ}#n~$v9}7muV&ScEDzrmAl;=eg@*&LSN?P#FwW6TW4A*t_1tzz8UzI zwmla`<2O_SgMgpgq07HG3v$BgOwQ;h3tQ?;Z$36SZ)sm5*E6`JW|*QK6_)#pOLnY# zw@KpnOQgzbwBScA7bU-Ds-@HWk$Js6N31@Hnhn}1M6 zVyXnjSU|0>Sp)vc0SZ<=jFCr?Z7;^60G(waN;mHEzB_;$83iB&6Wh-Y;4!NU!2cC) zRnLd70p8PZZ^3`XJ=2!~8(8fod4U&jB`*xu9pBp2|N-YXyk8g#w3 zj{LXPa%Y&!)F1E0+2sZI+s}Ti%9}yIX(nns?hKk5r9A+N*> zp6%@cZBCnOfH$k~awkwK{Vtk!$9W)+r`nDoc_>f%@raQ4h&*K&$%*&$cxVO0?wv}B@HGZEAB+JCj4aro6|)d$)nCh-p)h=QLGfng1AAW`nddozfnB zKX%ypg2`K#J=<>dsQ`))GW$1^tlI}UzfU3e&$7$7;^I}fT!a9ZsLt%!jMH>2-o64* z53>%F1mj&7@#GRBt}A78p?gb7XIHtWOJ};TP!#Z)n1=4;TTkZog-NI`uUp&OjI?IH zC%8e!#VaHlS;YQ{V-C_|Xy_T77K9g#eXY%UzXeqWZZ1>?UcmQUO1bUMyHS^#Ve|r~ z@%i`rkB>%NOuNxN?J(=^TrDEt8cBQggU#n?*%Q_68k`zYoxWvz?RSP3A+;Ab5F!<%yH|TiMX%YRYAchU+e~{?HQiObFl? zw;f{|KiBzKVanV1GIEc7$-i^ir;-k2$%`6!#=;blPcAY140>L7h>$AmbyP=L=uz%m z0wI*QMm96iRXNk{UDd!W`ZHzS@yuDR-AQCuZMDbj!I$YR27EOTLdXTeV;rN?e;$)Fq67Sj$W&bYUMR9Q#RYZ@O^YP%3s@=YKaymUnaA1iu%AfvVli zSOsz%{BlQe^#`Y)6wdth?#=vNf=!NL<#Pc38c%l)KnI>x0PDCO(5z=wv6h>c38U-U z+q3Z6d`<9Exf1XkEN=!Dc5g&dUeh~yBZY7>%;jvBJhJ>i-BzjGq{@&@{eYNiX zey#fBh4ayj;Qn%K5jS6&`VS9v{_7ylb6=5tiw!!rY0G0kKPh|#L{4a`sZG9@KHcUp znCm%TtpglTDU-nNlm}SdIXWNmL&du<9#fJa=)|A1^GuO%?e|N~PX2%$XU_w+WYw#> z_Q^JHf+z0Nu6j2X7WUnvE7pN=5jfk2D*GiN)QSu1;%Q@p(5Wh9$#r+UNw-EA4$jXATFY$3?pkc)W z+PX6<+o2MtRg;d$#k}jCg{T>5W*_}xN9PS-$|$+&<==$ZJfX6@p!F>P)?t3T>QV|< zsruYN)odQW+^sN4N24YGt3h89mX~&b@nH4IA@Mhw!;drPVMw(D_Ay<%k^U&|6)*g8 zoN8pqMRuJ&hBtYyz`ei=Ac=i0{deH5r>A!~T7-FcC;(fhyhyuwAxDus5uH+eb|4F} z`BMfsv$dJ*${lNCje&me<58d#JNC18&bTXHpaG}wRCxv<(G=rP`+WwDF3EFdegfSs zo;hcphHtlly>j@z*`Mm=&-VMYtmi+N=7Y963o)~+a0_nnwhCnNHVzClC{4V{Q;28~ zh+z=B{h#|U`oY%zgL)>CHHTQEe?G^+n$|c34dDTy*A1-*qUkeZU5Gto# zVHnNvXm!b$Em=ioN4bjYmgK8xv(S~P4m_DpYW;cUHm>-r`J=2N{)@v-obgE~?uR<3 z#jN}6MW@3@?{rm>>GDaSu4Bm3?zO}0XGx7;)yK*pzY z4WRivTt9eh?tOJpd+SBA4bIZ{`KG16xnSk*<6`&C*CEs`=E`SV;t#@NYm+>mezbr0 z<<3=^jWAVhre1%b{RM}6kI0tRN2dzFBo^EIL9$#uu`A@PLZnS~x{{vIOw(ubsR!Uo z=BkF*@Rl018 z*M=i1&Q>G0&{sfn{lp3<|N z$kW8eCEDSphFbZkt8)+FX^dUa{c2P5Fgv2JnQrAnKf#;8t*5l}90$y`9M1XR5#^+B zS;bm*nEeE!=%)>O4G-iN1Algzr~|+Jk`s)LITDBTj0phkSiT6bA-p_nmogq8ur9%9 zDTZ46Ou$h87%ZYB4yM0IYzW3qJL4=ZN(o&TD)WBE8hbwRUOuJ25-7BNR11(whJF$s zc+B6@pXtwV+LvU9pLMM3Rn|_%7_HYC7b%Rz)X0UZ{ZT!wQ6KKR*DVCTKkT|9vcZoZ zM6=8z=}d6yI6ukI-ZyK(j@Q0+)lr93sZF8q^v#97aZpWYlI!%+537*f+hW6GfWus3 zb*$cNvqG=*u@eK=>)BnyLXp}xe1Dq{XyWD;UNg zbToYFvpRgq^WT%BFs0~@g(g5uq0Iy2a|t4~+_mW2K`((bg8^;%nF_!{xA=OhW@Gs! zQK9>x$X!({x6}zoo84-FY@I>Y95Mu;Q<9Cps+jlIN=2xkj8|4aNj_|V8oo2f17~k6A!|XQZ zIiTkgrk!9(#xf!k=ELQ|Eq~S+z`qI$3)hrUcqam=+w@`8D$$gfLen&Qy+~C8d>@y> z?xo;OV7@drdwEpFO&vHshY zM^UFTGuN@xy84hG!tKOto5atvn_c-<#`Fkqr2*yvk%OEJ`p`*J3299KgYp3x?ztis zh7%XX%G2d!sfzx3yxZxbo|3P-MXu%X^zlkVCAy1+W`ipa87!&qGT5s5q!B;*)w(ae z&cVm46Ef3zU`h!@K4gwxEq}a?$k0RPc=_xn%x0uidv>r!&Rqi2XFgxsNzN5p!pO>^ z;@_V}eJ+=wpV7#tM}JRsboC#v+5$f5=iu9$&T@%oz$s++VEP(&F{W=TcNC3Z;a;=g z0nTNrrLSuqM$i%j6^Tj>vNTXH2Uh5AP3Wav-Tae1A={C#Wg>ePweYI`JLU zslaqg)kbo$n{80I-+&efjCNUv`JHnlc)_%k6ofxvj_YP!r_Ppg9Ij7(?(0WqokzGm zIs@A%+x5@NmtU|n;Cc}CbmK4L$GE&cnESb_JL;T&bqJT@{EIEl80fH%d3!Xmyu#~~ zHKMM@MwQJb0vi4%IJ1Jwf#4_c#{4QNFl6tH?VGYfovM;KF|XHC2Z|0|gC9i|>;PPr zT9$P6HZaH4S!3E}zB+QaO4{gj)Jb|Bh?lznIFbo>vzlTn2V##YkC?%w=uom71RAHH z!}|TcI&Oa34HMUO+Pk3j_)C4O)DO`Yk^oTX8T84HD{$d5O4K;T?mVLKw&uCG*Dg-$ zeSUe|T<6_k|4cIO1@`?sA=%3=&S3}JE>7vi8*O9_;O}pJv?5loP3ke7ZD7ZUZ(|Ggnij9olQcrDJqp@^iFAzS4 z{0sC|Cp5{l&>iLpFq$BTmn+PUqx>ny-F}&3n@8rFlsoz4sgADUCCA0X&V5g~V$$m+ zZOGpw6xN^`$#8qi8`nAEXXBwL=U2AHAgUYMB*@oQjbV4XhGX%RDF{$ey?L||(F$u3 zCLFIA{*>LQ83^^tFfy(23y%~S);`)UHcEKjvBu(SF|Seg#T%fY-JM$K$BwVPfp;zH znZIU!KPSyS-0p5(`nqYq>qW$N7)&@9W@~q{Q|R*Op_d@E^HOu1jO(2DFb`ScWziG~ zO;CqTaLx5p4GY9w<2`kCf8a-D180sc)CS4)Pel)>-_NV&^p4S3es_GcMkrXCI1ue_ za!BqH-Mf!BiWGD40YIUdHft{^JXSu2t3Tb%$fyFefvmdzB2_zzxo&SXIg>l(>A!pC zuJHyVSyv|*yRfqpL(ey6*O4E5kM0D=PV9^?{I&SPNEN!Ufj>FwL-<>RY*+gk(2jvk z0E>CziJF&1UBJWY35@2E% zKb78x)`US#XsLWz!jzA94Gy49S;L@}Jumc zN#LDjh%d2ju0E>YHGeP^eadjs+h?eiBsQJp(9v7z22{S5_nDEqWS`S};iG0clq?-T z`#Oa>9DifNc1W&s%__p<(pMAg=z(No*+$5=Dak=TW&ZHNj}4(iE82R6rL0OuzQ24u zt@xJ{K-AKmQD`#L)+w@`{)L;w*CSP??oQ|CTj`powjg@B?eiN6`xEh9LT%95AE;BU zOU)~$tgf)ipubTc8oqcP3@UP6*}}3Z_7ffdmX%mDuTr{6>zRu*MsEU`U1i~{UUZy5 zL}!XbMW`YtC)}%y#U-L9{0~ygDObzu574 zkx2qhb-eZ3b*K~xziomSs4KjPrUrmjXY61M%$GWw=%=mrix+F99>D|kzos|T^tC*| zC}1BMRu=I#5SQndL%cFkG%EFgyfqgGS_CRrMXhZr3o0k?6IIQ6u5aEpDcLGrRnj1O zNu#e%aOS781{oR*^E5vYzpsvM-goXj9M!He0ZsJOT?2<+%})U49PDpkD@&fd`wfH> zUC%GmEH%5&a(qmQ>cWyMv zaCHbZ$sgXpfJM<+5=iM3f>RJh=~rB)NfIc&AFF!QX54Y(JEYG#tEBvVk0$c*}&Dp!4CN~k(Nbf;;C@?djaLc{`Xo~JUOez}Bio;oq zpoTKGMAf&!Rw)%`%w8nV>{3`^JlK$SVSOi}0O?So0&fBDAi# zYRy%a)P;3zpmCT_rK+W0R4GGe8%cJ;G=mMYPU`A(XoRBajXCXJ1RgbKoIrRY==LSG zAE21^u0b1LavhRQjH()6en@^Z-zGtG&()z``N|<@3rNj#dC!hNE)m|JZmV*7Iz@KE zHVkCPVhEZK=6x=*uV9<)_Oobb(<&C`Xm_O{Xd?ZT<3ZdqL}0smzHfw6l71&+^StTF z@;R&bwZb7LsWP!Jf5jgCwejvO5YzsGMivY!Yo=(QZio^rPJ&cIX7gxX(q)X!mO`;x z_^&^g(irbucoXPN^Wor)v&~6ZXwsb~U3g*+s>Xbr5+vXpee`+M`M_%Cb`V$H6F*A+ z{@oZ`BzD4kM!NotXM7vBJHIh{-nRu*WW;BjlD;cMZ#^E!{knab71Z|cmhvDkB( zxYGyyOtHd2^vZ2aQdG zHIB|oOf-&E9oVwz+VL=><*AP!0SD2M9Vk}Oy*19PA88A>U_0iFLVbXUW6I9}e&*rDMiv!Qoe2uDeN#~v6dxHJ;XZo$t~I_ZynALqi(26Vl%zW_y2zVctSMEz7 z{(UvDR~;{S0(xjp#|)r9=yL0Es@cBJ1dM)_d)6+710sHTIGNbgH*;9X#@pD|g;QXoFCcQUB1~!F zwHMpgZa~7R241}fx^|RmZ(bNMm8F6jAg-`Gg->gtNX3Jj%+%SH2?_OU=`RCQKrSQX zku;DSniNe0WKK&UyAHbj&1(=(bRauQN<&=`oNWADC+ybkh8c# zn<)sj6=q8zW@KN(`pnIhP-NwXb$UwH_7}pA3@revgQ)xLT?^na@9r2RGgtyop6>|q ziMoxU>HbNONhVHhlji27^Q>IMDpCqkQiMyT zd}n|On8;}#{kVjH5vfzWS$UW`7+rE_ky}>7$V6{IvvHV--I(*R&>^(Ve-0!r_xj>Va7t2bEGc~#Yp>n?X3>0+alAwyD!5#LAtsz7afi@l{giug z%$Wn;R9XJ4rjYC>(>#{X_x*T@7vb&ZRI>zo-@i>TPiM+^{%TlmTP_#GI^^Ahric3+ zNDgm%e-Mb^9^n_w<<+bRrP8+%PrkzmZgPj%HVqBDw(Zl1^$;aE93$%*ITjXu*$7fu z?X7?LaTpMw&$&t6)gx>L#Y&B%KSnxdm|f?g*1Ag@oM%qoW9S zEH>mT<97+q%Zy(;xNLbfM2D~TiM5Mz@l5Tg;HV_%o10$MW@#<%O)VV8g|&WL>i$CQ zt&rui>;Oo+1iCmFAOdF##U9zk+{#{X=B%WK|@x$I@X zrH@*JdJQ_~sEkn9o+8bC5qGai;qcb!D$mkO;jVT;Dke<$9U9=IrskPU6X}rl=zgw}7nf9#n+^A9r4GMr z)OvqT#E0|!?f0%!h#6F{zl^?UE5q{HJ^X)`2Q4fgpS|&Wv;tkdai!T1e-vi#S$&6- zTsZW;`M(kyoFA@?lN>_7|2Ygor-G)lb^NZO^|7DHAo67ZZ_UDI>leN&ZNF(E=NF|B z&Hr}2X3vaYDJ^;C_JM! zvPe$izxzE3&yD^Su)%yyg&pqB18lQ2(h|OuzPjQ=_20YHig~3$L@7}z0#rA?aZr75 znp5U)!neEt9{dRq^BiN!g|XiH639@|N@WvwK3-{mPJO zNm#!9*o{z72}GY+;JV-Lho!Ig7By7~3zbH(J)0g#W(-xDH{)Tc`Km}>MY_0`)B<)>6LxQc~qtVyxhXbjyTKvsdV6~ia)Kwn*p2>bxWp&{N2)QnlQFK_gz2XS$plahW z>C+nmlXuKy6WB;|eL&KMj#%sw<#PHTp}W1$88CYmhKhEqRUY@)`EfX z-X)0)WQS-(JJZe3u{DP5OEuu>hDoIPEN!cygF+~mQhy3zDvUcA9kP{ySvB0*2 zLe=Nxh-3F4{DdboU(SH}Mbeh6-zNU~Y17?B(lvrSP!Yfdd9oE;KLF)d0u5G!RCcXv z)aIvyFL2TnD+_w7BPS$EfYl0b{pZFUUr7tiv!V_mfXb=5M~V|;8^o1bBb$VS63LU; zoT9WMjdTlUE%)t-Q*=o_JEj^7cJb2&ahYM?5~P~*zaa{)FFIivXos#cZT^eKj)He-HfW^P z`v-_n-=>0hWhmQzhh^1Om2RVM{<48|Y?0AR4Ak7D9V`l0A)vhVl1EesD5dI9%&4@??36BnCe zRY+WU`kAW$b3tb1+p^;q$4u_jk8~qSpp7{SiUh;EQwf6MJ+iJQSC?OWE$lz3)PS() zel_fyCC9}zEwK6Jgtw8AQNdiz5h1c;uB?PU&JdrpRncrHS9C}(dcU_Wm2f`XM<*hm z3+&@f>&fqaBY(LGYQ$4zKZVk|BH2;kJ1h&VDBWFakQ6w5*g~1D^`s;d)VAX;Kv_&) znP!N6=tb8uu5f6&D0PIJ&oMg4Q=3o+XYY7ye0zF1E`lFIImdgXj5RNONgSg%`K0py z5q6$IO|=WVPC{scQU#G-1Q8+hUPVNdrl^3FP(=kqS`Y{jPywY2(jh2Fk=~^xph)k% z69q!=1VU(MvFF|2-rqSt&Wtn8#F)geeAZL$>$;Vztel{Y>M(1E4l?B7eQmpJrMZix zyeACqs%BYoR>9l0^&ecWo29M%b&uQhcOKq&PKhg0ZK;@f(|pPP%67!B+EH_l@0=i3 zf7)eO?-dbMN7mVQ*%X*If0L^&p#k=`>fB|-bJQDUNT?I=5v|V-LM-|tukq+vJ+dxUTG?*|?-4|N? zeU&@C=up1~F91;I?dfeyxLP*?_tu@X4FX%PgNkko2l1f+Q9pw#B@1@)`)%#QNwZRG z+U!JHwgit1UDD|2F_}f{(_Uo?q8pkPqjpw9(L_e%3qxEtIx3`YjY1p#bEIr{ex%$P zD(&2Q0SF|2`(R2RRIYt0w{2#10W$vn#aD6Dryjl^3Rx!!VPVvxVe!;Md@Y@y6Yhbp zG3nJa`&*7@-LSHe%h8fJ@y>&!CgK|2Om^Ges!U|P-sYA62ZGeIUe7VA8vfuu{acER z`aeg=t{^y}<~Ssq)xbTrv8lozMutg8*QJ0hE^?{KY(3trN8SizMG&5Pf#jhH4H*-r zSFzmJasgA(Ho9=O!mT;c={l?_NChMrc)Ll_R7PD~pz}JUX~B|I#dJN#ptjtfT?t_><2K*s!CbB`Z|XGvfhW-_Xx52QJ%i5FE0QPE!S6;1 zU*h$Y;+g)_mx>>?OIneI*yCV*XvDJ==Y41{mFyU&1$UCygA0s`JCWuSzbH`rBdHL- zvQVhh*rmQ!6%-5|s6EjdUyLDr-(v&uy$!zfnQo2B(D6oSeNvbb)5wo1uE0MXX;Z%Suy=1%V<+=Z{8iMIewW@e6*UOLLk#i)b_0yIKQO` zV`tW=JzJ{XTguy{<(y&cxePz~b`hlJIgqvr$!nqJr0D}D;}$@pF-_K73%~N=sPU;MLzp3u%L$h+< zE_vnj&U_BnuIgc_>`fe$#l1gxpeXg^?~)kF0gHSGl2p0yECmLA$v>#80(t`W^wZUD z`G6R0BDqqVd{|@fDI32Pov5;^sbQjFxBe)^HLsVTqOy|&s*tyd5F3N=T+JvARasa)MaDMN)t|+=Dhes02W5Y}FID4S-)DQB12jtEmnMCJFv}5!ZY^|hw3P+o8hx|~BXi8j`LFMFu-DA3j zKKAI?i0^1_J_ozx5;{Ul>GKU^v9x9P{R}h^hddD`dOS}yB8Xy}4z^kGoTsLBW3 z2EVC?$P8`ZlHPkXrr@p>rXfnj7#+3sl3$l}o?$eQR0gb<>@WxT2u)fnn5kAJ}4{_184Sy^XR`C+oy=T%GFPX>Ql z&E?Nh+T5{xaJ_z%Y{wx=2SH_Pq7UVe`-h}OkuQonhC)}KVN><8*wpZPeBgKY; zvtyI6cGyV4Vb)_AKMtVlJa6L)9bWL{me^cZa=Zbj+Ua41zu~2$Op&?qG1;gj_)m17 zCkTEV>AFblQZ!nh$&x#rdK0M-7Jf|Or+TuNxFIW_UinX$`iinqq++umBn$=Urli;1 z5c84hSe@2aqDq%V)2AFH)_!+}l_vPmyo^nx?hOmcG^~G}6gLf5J3C>%@{#9h`Drhc zV>#=cK!qk+Scs}`Q>pxAP&v)87ew1btJj-Na0<9ZsEv}?aZc;)bldz z5JcW&#l{S}wEK0#Ra#thW32;c*!8i$skJHg`vW!@n%xRG5BaV;CRCjAir}=SNmOlLqT&pyA+J3Oxj6`J*<^_s z0qcxYnZMfT8FdjbrH_(@7f)WXSqyw$gz^)jYN3{>H+y&`Fo*Ug_+{bnc@k+tBL}30 z$m8D2K}2hp-1S?#7_A*J;o?v}>tjSmPrmd;HE;n)8u3(i`=wttPcW@&X1p(mx?l1z zWsk=2_m506Y?#nexBs%~>!%q^#4b!=gML7Ui(5?Z4;HE?biS2hBiI(^Nhq{0Cd1X; zX<^M#om%<*)e4lx}MM+NdZ{OALb{Ua2+WF!JQW#6NB~JMx2Vj-5E+^c-pFm$Z$=3FF>81P>YN7FqvQh}>@UQXQTO(R zG!{zqhO}&U4}lu>sRd*=P}|m{>dM-8cNCD(JiKgn)DYqE&zTQC^Ul%qy*zH&9N@_a zZsc`SGaE1n`s8QIw2GEjj8J)Vr-~2l(j$Q%=T~dV4%a#W>gjbAx+2#S9a6|1#_~otyqxw=F4qm3ZV0IPGl{9R>Jr?d&YuS z(2^PaH-mf7pP=t{b}p_0S^m(V-1}y~4oYO$8K>w9@b@i@#pu)gQQtkFy16IA3hg~|5Q&0;1fq@Em4C%67h^4o1FMZxE43xUKM1!pvBdq}3{Hz??W4!0{gyR1 zE<&mLTCr}}8ufGAF__S$k(n;>92BmlTnvd^(m$)=7S9bZ&++oR(+W9QyGVb#w@BoE z9ZDX*ljy}^&{{2G#9fEpuw!aTQ4{n`z%wUrg9?iHye4xZ>x;`;78Yl!Ubr~ z>~7|Y*i}DJS6S=XB?(0eJc?4Fdj6Lr)jQ<;)u=YEp%pf2HE>ETUbdV988R;MG|dn#}hC4=?T|y38h=pjPUKLZ?vT%qAH6{cd$p};=bioe+(+Sr!>_% zhOtgl$3HiSo+nLFCai3A)-)tVBzo=|cpcB-hYnC%Ao{%l105 zRyIcBI0NxL1%Tagy4fOWBy{97t8Ev@QiA|Ub1ts9B;8QoXtX1Ptaq|aYjjWF((x&% zq?94)WsyjV@}Hy6%a*8pAXPo;9Hrg?;A7{^i`BL+a2`wD8Fq(*i6IuP!#{Qg9!S)+ z*fo!{ZVIu5{1jx0E$clb^eQd-&`evCzVb84G|ZDa(V{@E-2H!Vw~yWbg8Is;zl+#C zdT}_tvYt@QZ zHoFh9w@p`A=N}~WKJYJ3&vqK2hY0Zo>?YN#q;+(t$sO^G1a=>#oH^1cAO&e^Dx@Yp zxhvHu7rT&GAf_K9hU10XxK3EyhSrO`XpXQ(=y~*Hp~K`f>q3m}-)0}@5&fCtn)Hd` zn`%Mwd-EpsOKJp})eifPGrGFV9E|E03_DdkvnTHex3OiI4&wcuz>P-I=UY~~*7-Uv zdr-tvWz)QFmoC#(UipX!nJ(X+!dT?ct-tQ}syxl%r0ihGg{*55qH#-Z2-4C|( zi}ZKi*N*$9a;QQekW6P6=EOR}L5}5J$7es}HiguG{tXLIB6p8D1RJhj>6?hp5zFBe zF>ZXy17z22V=cg(ZgtzEXMg70#w|wu-DJ`2od;<873v1b+s0ham(YZy{uUBgq5nYoLxK^x9xDSvz;S_@@S zrA;IQ1$L!^eou|+>yy4qz1-K^d{p)o#7QtISUmuA&~bC+89%~vmvWZwSLOPhwj7hu zciQ(%#!rTyQifAU8WfApF9b)(VtYH6>qK>zGmlTBU3{FtTcw*Af{W?Gg3RV__4rxQ zk=n6-h0x|A=|{a^&JDE<$BC!PIra77=D}N{4_rdG9uoAOKgu?bwb`#Wcl*B*au1Lnecx*xQp2IrZl~Ly);nQP1d1`I3>wr4=ijMK zKbn4)#A~qB1$Wo?TU%u?YR^=QTjXN@>6EdgH_8joK^$I+rqbOMfLl~o)f=lE70h{b zJ_z|&bUN{z595e6-1Sk~SoQlc*u2}@A)(+k5VlHAO!hA#$i4nA%Iv`5B5L*wH}@)i zieHY5a=GRp=OWL-tW0rb-OMqT$-uzkq#>kdZKY~;IC2XY2Ez#0LvO2p%h~byRL*43 zE9LixP1N0I-VNIrV$lW>MqKr}kvT{R#4X48`FQ)R+PHMfWI0M2t`$_T)*02L6&<|a zbr^O@I0b^?v62ZW%o>)7zAvw^+fikjGV7D6pp*c&Ls<{Bk6%4eZ8oo7pHMxge*mzQ zb_d&F$S+AY@C&=oytu^MNv`XS<@!_0>8F-1t$-#^eF|_C8$HWtRHz$hjnt`M&SqEI z_lzaKy*^Q0`gK}LrG0eS$7NP#^B3fsB$jVA!`ooDCJ=WTKe>FWKdK~guD%&d6Rsl=f|(PtM`-*uUJPV;4cs<+PHKya2KrpW zx;qE;T(BxH)H_Aa0oVWFqUYSG^lCqGo9W+#X%{(>B=iM>?7ngtm;Zl175K{m|K_EM z=BVNT#{VW~H}`;;jDXnxdWQhSjejD#s=beJtH2WqHh`EjaATTv^q0BV00aupnK9Y! z%5!&oGHx7*%cSKe1HS76nJHhfohSU zidun>!x~~(X%6wcGqz>pk>2T2H$GN0S^C98Pav>zag2o93^?=k1Oz*efn=|Sm#?54 zcH=V;zr{W}?>O1rS^U@%&SnP0MZAZBHjkm>6?F0pz(uS8>-0h(*-$FYLhvOx0h*=-yfd238|zCLHm;jxm|d@0#_AEKfpA;GJbK*D z321lKB$IW>V6}WxS_{-^STC(tG6q@ zzsRmRqJGYG)Q+$Ny~HBJQoYK9^~zRzJDuyD0l<%qk&gMrZejJzL<0+;e)(=Jxp5ww znRj{i4q)H%R?cfiR<0_^%GGb*S(I@Mc=7Wc^)Rp=7_z{#cNhCy!@vuk-)TvZ*a41k zdP*>)TO)?+_>pB^XS}1JJ~IDs*x}aym1IZ+s!8fec)7(Qe3}>LC6Ig4fkD>W8X))2 zrBX5;OMN4rurMGk1pB_}=Tfr*a6T>;*ttlh@95J#hZnbQK6(t0YOgDQUJ!Y5E@>b5 z1DpsCAs!h02j@4O)iES`VUH|4+jv7B0pfJ;V)uPuJa`XT9jH%j3NLJK-)kEq!TkP+P$8tDNF)SNZp|BdmArS$fjMAbi5PdOBZ|PNl;8 zkUJTgNvF7!u>xFHI%ja8gK65a44sWzEzk{m2wY_zgnsP?O6X)vMmtd7HBdhl|Ehkw zz2*BTa~3eqoi*g?&93aB0O3e-!ao8?TlCN{wfA)YrrN zw*LIe*XHuB@UH2d_tvNkU}k)72{Z#RHbw!lJ-T7h>LEWq@w+*o54|6Wmiq^bYVhRa zVlN3)mwL=TH)70sdegBOxTrrv4WkrSa+$>`+Nj&`w{w8jjfF>}&nT!VxL-F0l3i^h zR}Sa!Zt>`5^SkCxY$T(**Z4&iusoLzo4F9UhJc2%uzUc<$@Nxf2$^gYZ;-ZW4t;gz z>bL6_1&xjud>gJj75ao1K(HO*ZqcsA{JqF~zVpVY&C2ZS|uXGWl%r4D#0Uey&q=z2HBGMkbJ zX#~JS8BPBW*gvrGsV>2ku^yF!Dg44R-%fHqC zsqRG;IH~c1vW^?W#Q_aQyH&T(nap#_@iEYy^QscaJX93X#5q^=-+4hfv65d5h{GWw zB83%hPcvA*zc?X`8Z^n+c?oajPxn;)*T75Cw=4pa1LMnU$h}CF{jn7w?!KJJ1||9| z<}Ku})YQ~@&%8K1^!|Q;PQ{Xze!V!!X4L`j0$FE1@)Z71hB~7_mKaZdTO>eF)h#Gy zeLmq4w0FpB)PgoRVa8woiuexfqe*@idVNitC5ieJ1N`6$-3kq$EISsv9c^H>gIs=e zW}!1+;TeAUxA}wcn;|qU17Yw*TS3wGTuz*`S3vq7>jTd^&aoBbInE1jUbVVcA%lo; z%tr-Q0nHBqg_xbs#gBl*)V(Gd&sctl@5Yz=g!$15Q(SQUNjq(Z6j+9hTp`bR7H_{m zP(I#@^BD^S&7RYk(|!jkg=?1%$|LG&tE8{x?&_=g1ztfiuUvjEh z2z~@y@sD0&)%$@~s?X{;9v@puOs9_c>gf@8qM&@{y3^`<^=czxS`Fp_e7g8>Rl{eO z)K`q1*_04dk8BMB{LhZma{YH=D+k>@4uQZ6CK>2_${CQd7_rv}lV`^%$Of7}$X<-M z^hsj&=$`yWd%`@2Cyc@kJSo`B5*xfvf6Qh3V>sLOGexTOVBI{atMiw{H3JrH_5Jz< zNiR2sw2zHdDZ&lP_?`DTkG2aWbYb}gWiJVnvl}C|Etl#k+30Lr!ks@)vs2>+#%Iq| zJz3)1Dn(q1eO3wc#663p@rId%bC5SHAD?)qIOn+h^wg4iug=fQ^Ey;fC-hL^Q4gRf?qF;N*e(blN zX*Akq=tY-aQl>)RgV)gI@RMknaVZ85_a<-+fw0b3Ow({jD*t zM&$t5|91iw^Kzf5}X$yFH_A2<*b5vu*} zq$pkWSD1`6(dM!C@~fYZ>Soi%IuIUcrz?c$YrWytn%*fxt+Thi?bcTmDob(6pf?ML`8?%>LCs!2Ti{ z*a0^xJlQK$rD4x8aGwUAlD5`#M!1qJi7BMJ^{n;}yPK(B!dvHgg#r)=1=6{#@n~-2 z&!9d2{iYY}D9qEsUF8NQM^8gyCfQaPKQ%?SqD^o#Ql0Jdst^|14eH*wtF(*J?mtDQ zaXLS=R7I`4s+!NHjUE%zBKbjJsWr5k9Gu4|RyMsI`rd(4a1ct~2_7$a2+4xc4! z>OOY4S4gj<7QcT6xRN)(00$gK4+FHtIOz;1$uMZjMbzoO!W_m+*GvqT0+ef25!`3l zez@;TN*1fSl9}FBOWb+He{K1e<@dRTwx0I)EKlV*SNpR5*SH2oQn!u05Znu&^-oJM1if3_qF}%?CzIsp)EIO=OLB=gHZMkNQ6nSJ~7?Qsr2_l^$CvM=}mvwdh*ByCt^&( zS@PjQq|#|Ed9wNIruT)bD9!^K_3LtxNeqSn!ur{W#|lt!=A8nDf`+Gv@y%>o4xmkO zKTFC{S`nBeTzsg|F$pC6AA){+`j`SYai$;J5iZE`&lax%19(nlI_RBMQ+CR$WW*`| zI=Jt-Py>hGp2Tu3Xccf^?B|JL(>!iJOA_){nLWnK8681m;&$J|j*SmyH-$>>i`%@t zD0|obW@&SEtn*4akjKq9XH?TyN=2qyMAxpdk%&~EsN)1_wN37X>aX7Mw$-^*y4@h@~x4?2r#5qIF0see}PXaA~!Fp}3D z6m>>`!*y|AQI~5_)7Hl4e85Ai_8tVORNvmfg9mr>`sH4-KVv^z*y;54->;>zn%_QO zKYVfo2E=q@})L~mHwYj6~X*;SE~3uH_AF)KFusY z2Odlf^1CaW>Utyg_2EK-i#^pEpvN~la;ASo={?5Ic6x+86IwQUcG>h$$p3Z#c!^lI ziF6nT((6R6vQ=gAnaFb?0TjPodOQcvhwzh0?1}GyLqIt>A>ZFlNZr0QVxgG@hWT+R zk}L%*(!$IhYE_HzjKF$!gmzJT0OY!$?^iPRYvV~gSYP{=EmsB{)Z+*Dwi1xhDGiu= zix&6Nq8}oLp2|c{g$--#XSqPs8KQ?bHzZgmwxhxwQz-kGqj7&8&7QO6^RKbtNmNuc zkj^T@cnL|TG5^AhV&CbU4gN@Z$+Xi_Kp}uwU2kK#@<*j_cY{@0Fo9NR{09Dcl00v@ zI#P7^3oyH!`h_ z!M-W0TZf*_UM``DHtlNGk79Ts2a2e?E}p z{i{Ad;!pWLpx_=_)Nnfdp8wLXtoY5$k0BQ3xt}t%;az(`UI_z8B&^#Wf<~$z&9>8MQ@x-z>)Z&MXiJ5u^z}xHaO%W`|g@Zi)>oV`);!+ zzS=)2dz44iYz8WXiuiE-#N5{7FL~H(;=a_=g)7A#?Zrv_gjwL5oSb%%^E_Bq`e(hT zF`ooMnu5H&X?EN;>sDuNC z|LqBsW#~7((V$IIg5={h9iwXSq?aNlHwU$N$V?rn_k))3}-&?yzL?gAFs3J^dmyr}^p12HT>*q7dk4dq3g zBOv=UEI*&26HCX$QefggFui4e2d@>T~hP@UpCVbM_n9Mgw z>Kw9Nt#;>_C65MI{oo4ZulfP3aH`ZvyeERGzbLr4_XcZcz^Xl^Ul6ynq3ZfBsN^NXmt*X~gAplRT65zceS zy#s=QI~%}owbN~bPZ9X0T|iKsu^>d!gpABAM0zUgvdhoLOKL zl+sq`4@1E|^CXm;6~(}2*=V%C2VIH`<-04761=a^P{LPro9lgV_xzeOJ;lX!D&5yWbqfgd0emBYVDfyUB0I8FmuT0~Gst z7oAwP^^lSN_6AORek2FrlD!vToBGaRphw={=nVWcda;66He{O<3HciPTuxGm;<IDqv_eOAAx$jYDgl^j2ANv9N!A7|UklkB10jm2feUx=A=HmC_swQlf zzaV8afP59-AN8ZlSlk7{kx!0`xXtKzEDuSdvdJde#lAq>;gLrG>;M2>X z%DQ1FEtF}fbkz{|#Zb@kDL|-N^7$;5=>pRH+< zKJqyeafc0uyQU_|mOV8afV;mH2cz5hBr-<3v;%or4MPDrqL3$W(m;Y4EK^}iOTSK| zdgA3ObL!cOF?Qs_UztEylMU>Lr$v;&mb$ve)*XC5T>vq2k8yqJ3KY4+Cz{A(xcOW0 zuIz{D@*tyh8g(stZBT;{<7;5>D9)-n*-1zdQUIztEB7Rerne{cEJyl*ZXj zOGZXhI0$7W?`}_sZ4|(XK$szdE6*vB)ER9bzj8Z6)S>;;uznt0nA~XWA`p6=hvHGR0x*~2Axr!wgbL4;wx^~p8q;b3j5PvQH&G@U-1}s& zOJBt_FgB?wTD|QUj^`N#>8H=ygu%b@Fx#-Al$-92(@B;pg8#Jw?Dc=*(7>zlFyn*d zcvM<{s8}3ZD+3UAmVmy?GSi(om-+bR(1G)SVqrtagNiaYT)q(FkFRw+>bZyuH@`FC zS~u*nW9a(l05evCdK}PNa?ReU)I}CdbkYv;D*FNrK8*es%Zp#)iTo3r$JScB*qD%! zR7;lF-`euX|D8!JDl6!N9^|eGnxfRjwoi!>`+MIJ!crU)RFX@L1kbebf^L$@hR%d< zrnm6hFaOO_Ga}45-xIet{EwlF|vm+0%7Xc+h&nh(n zpi6C#&E5`eyODO5@^g9^%Nv;8#YCc`x*9{(b0eU*{ z^SSEJ6*9f^uQ|tlFH(_KxFtI+L8qVcC>$^8pHk8@n)>f?vqS4UwYRQDaH-UiALmM) zIqTuHQO^xdQ4;?y(l_d#ksscC9<_IXK7I7ofOK8$FT>VnZW=kD zg^mSBT_C}-9345AF1Ym#4y!Aep>GtzW38_l2_NUUcBv8?N*Qr7(E^m1J&6FPNQ&ou za!M~Kk?r4UY?x56EG`~>(W75^0mHr7-I`9~+(3DU#|!f$Ih*b;n`Gj~6ym_tl~$Ff zQMA_jZOk>DJGSB6pyXEb1Tr{P#+`(X^56?cFCg#<2HWWLCAPczs)_?4q-%INH)u&4 znw%#RdCBl96^r(>6vw@{ij`r?Enza&PJ(UiDjpn;2BgydkV2E)BeAynml!Ekej^c5 zq`pm2>3EZSS#!akS>NLHoH08)H1FJM3;Sw8ekx>)=`0Uir}FT~k-P7+Ec5NP@QB0^ z{@5YlFxyUKubK5KWodesPExw2(@;Hd#&9nGJ6${9Cl+MVc7Z0}{%PI*K}Pc}D+a(CxN$bUUBVTIxcD8|)?@Puqpz5wLAwntXn$hB3N? zq*lCegF2VmTsAQNU${ObGiXvHNJLT<+6)p`rICKq6n`0-r6I{`!&=D0Y1r0T&=?GQ z4dT(m-Nz|Hyn?8L4!46~C_dbcSUcb-Os@&7-R*778fO>)RX4oIX#UBryy%1N_R`(x z2BCpht$Z)(Ubhy(omuVZsUqf}nV9n(1!_Zvu?J|`R=V#>jAD^*8E_07^w%?D-&j&a zOBV9S(M4aN7Xc_GVk2AA2Ocmvn=nY&R0n)F=8v`-=|NN=!td@ZoysHbqtKycHY*FR zt>|@q7jVh%?c0z5N!yBi3ZVq2N>5;aL{FXKq{hG-j0e0XS$p=-8IEq|aa$FJ8uBGK zM)?@F+o~Cv>MWeO#k*XiM+LDS8X0yR6|QkPbsLu2syVzGav z{c82^vEqxiK}1Lj%BU$nwP1Q(T29t2(CE$Q;Tn5WjuD#z?K6}FXd|{Q3i3xlQb;IN5hP{4`S|lp05efP;!FR<#s3i;}W*SG0qh=#-v#s}) z8@U6X?EA8_I}80(dGm(_<)0i77*Z6p7_(6B{n@!`!-_E&1%v>)$sx>PIl3$Do8k2waA~_24%BY<(^=$pV5ea~un| zk_O%I$e8xXh#F$6igfxB!4r_S?Wy;Y3~uhgHhjW>JrgDq*n1WZ%)Hpm!Z5?VkW=8i zRvU74DZj_&>Ih`J&;{fHIYHWjSV>6%IUt=~`z2!qOCU~DFMpRZM`o7Z7 z$Zn08Yr%V-b{*{xC1=OxyYy2US=q$Z?^UJhT^Whq6Ac}PV#5375%e)iXru7V2bp(r z7MDZxw$P_OXT43E=DiAA_F|uZ&-wO(osNgk_E?hK*M^YJ$4d@=b`8xNajc-68VS_z ztIA5^5o-HA+RZ){z;ZzQ4Mm%NRGZ3Lc*7cSuU$3;h>R^KR7U98g2+p_*CQI@$D5D+ z8ns(|f{G)&DcdRLW5)n{Dp%^r2Lhp;8YPh%O$r+KVOI-RtyP z;#PgkHge@qF&fspNNfAqZz-p3g^xSnCzs>d{_WLV&1M!(?JILV3|Rz;NFzJxqE!Uf z&`Y5Kf43w9+f;HYt`ub=tt@FJ--Y?VbS8xPSc7jCgMyA>%&q2Db+TU?(UO$-_xgu`XJz>vuWZ7ez^EWJrNkZ}ULbYnf5tly9HHgo8uy+a z9Ecc#{0CYQ-Lfwau_{ZjcD$(m`VdkBw&7o@Fz z=z@Bp0e>GZw^IJQp>M$hbF$m=Zq%SGF%6n=3w1KEF)BhP%$H*~XpQf)#Gl`$eY}#f z&?D#KVOP8)zlNjK@|L*<)P*W&{<_Z`rjgA*6;-$}ZSsJP&CiA_>q{Yk!DC{w>0@83Y(L7Y3v}=pD8&9^a~W(Dn__#$oa$(?&JjNK@uPy{**nN714Tl zZo?pbu!EES){}7v+O;CL@|7LzjaUnnTUHM3#VQRLWG$#wo6}QwHP>)1Mg`(i3Gds6 zpApXP2Ue$tT7WdbprHKoDgM(|)t6zm+;d}MehYG2kPJXavst08ancRngCfDJpr^=h z81j-*7eomzh$cHD^K=aGx=z=-`UG=l3f<;X45y{^7eglP19iJUUnrLwk3W~(TA5`w z<6HhFhs*DYOJf8XXP0Wvc6-&Pw+WWZ?_2~|K;Iui#>;{tA~|Syp2EQ|hIoyjM4-By z11C^cfVxmNv!{JhBPiSB4;49N@sBG8@88D#=t&-j+0!nmZDv9CcrzEl84OEu=^QRX znvExG$BDKpr~=hRcr@m9GrA`D;`P{5hO>+`R3^whu62>BC^!mO6+HB}1kgk1^KJz& zQaD7P_@;~!;Onl#GZZ0}kur0R5lksM&XJ+El-){rra2-YI?&JPVs{w=U7Qso3wKd> z<*;?K5f=h~ws%n34R=*5kf8uE9`-UX37x!H>kRiMEjtW&TA_EqijX$hXreAW@+Y}X zKiGnz4ZIfDuH%5ehtfQ*iq#YM$Z(m04eC6RuM3l{6Dk|2cwYn^9{!~dqOhiM3KMPK zPVjJaN> z9?cj+(oyswjFPNS31!k!v;UrRWL(uqveKe@+ys@*S>}$gD>7o`%)g&PHounuA2D!6 zTA_SaDgy7OM7X*qcF8W3YVeQ&|MUvh{xH4X2~iVr0vyVVY&Rxf$gJZ^t?l z7zu5jE|K0H62qHg&i-qUldYn=UYm05T3Lp2r}le4lg<+N)f*|^%a4z{kEx*#g|Kr; zpb}dZt6!k$lpKkYN5^+3vpdp^8czO81mYIT*oa=`NF0e7;vkFt?}Pa_lKN@XVcR& zFZNpM`OG^4DJy-IC~7e(`V=zqspDr+jbSpu5t4W<&_`M{(HR7rixk`of^G4>2C@nF zv*rd-r(!FwHR2dKn=#l+15M6;0R5A{Bq!s+2KAfg)tmY#$7DWl^k~D+7b7xMj zG_bowr9S>8461HxAwUml{?PSGLyqlkBaNwu<<@4+;*1A#MJ?0^M7wb(~P~IcG3LZ^GM@OqxJL%=R-ra$XTY`Z!o0{u+qqj`C>15?)Wmiaz0Q6hJRjT zQcf*P6qT^={{7Z& z$+VGXl!It1cE-4ek+v-DT!ET?tzzbx`E6aGi82wtO@$^tx3b|Y#_9^IHl(dOQ-;^f zc8nET6IejyT2)4z;=fL5$eW44Ckc}5?H1aMc)# z_jr*P2$78qOe+huS**jwFOuhwOD}6qMyr2slpPSnX+`@p-_%yWOtO_iC4+jRZSj8< zH1aM#F>(FQ|6?L1ij^#jr+woU#! z;krC;QLj=*GSvQ7yF^=+;YzJ!`bH!iJm|3>n6kKUxw*=pZ@-TKG7d{JbrU5qr z8^|mw2h#0o9Qc8TrW1fC{vvHXVU2qiF zZaPZN_k$vN&n}b9P%+oZ^+Zk_+t%IL!~hjru0H{&7x+hW=&7QRo@Gt=6`B?DM|W64 ztQt@XZffwRXG33lm%mcXq!*FrJAG$Eqv-r-#4kisc?XL*Gg3ViV|Q0mu!#*hlpww3 zS}DlpH#%{W1{suo_2NxZ2oSgD;#8=F~I^UL}R^9Nv;#VRgF!LuPv$ zu*V6#Voq|{gY1nWc429QDH2Y|zEa;V4RBer``Sm9Qt~P`>>{+pFqlB2)>++gr@AE! zk!TblA(`a+OAwr8r$zc0fP35iDN2rHoKrP2{**s%u<8UoWZHtz)ULZ`PM-l{TwrC4e#y(YA6j1(LylQYIV>5aGhDhXUwy$Tf zUyFHEfx=+`Tg6JGgpIQ)_u!kmkUsvm{fT}1B{Tx73}bdvZ(7PnsM9ZJ-984NJP+G} z-2OZH)rFy9$XQYH@GdH;c8JZL@2ktnUoJ$4?6u7pXy$H`~BN34j}vW=RO@yDd@EpcD>vx}0*l4%h zGonO&Xv=uvKN`8?3%VdnU9sDsQu&3Gu;8z_u2-x;kY3#TR;c&Mo)t?NVe(u#OFw3M zh&T>5)=#s0tS!_qyk0c9;hrOzJhY6Nv7=iNFwmc;c+(scqu}`+JLoCxy)(4>2~q2! zoi>pfJ~Pp8Jj;olzMNs8pEfaMHm$JWTw;Ak)*)80vRr3^sW#srK>ll`_01gf4TR!z z%hjPC{ZZdf#>YAT-Wl(dx=aKj*oNn=G{A26zth=!hrCUqR&iYyG>BHMwv|dM)^~JT z&}-Bjo`*M&GKD~HJUWK-R)2mx7>|@>w1@S**Bvmgddg~?Jts~XG~;V27)Q=6!w#joi0P{iKuQJ?|eo9Cgw721VdT>tVUvwuTp!u@6n(EGNy zmXx+!RP_Cl5w>XN>|@+f*V#Djb2gb3;Qye*23HijoGLhku-dTTI;Z-|(K|`Xp(jq-13(NDjGzd#= zt^W60o<9&Ut3U=n{|Lv$e^kjZv}^vlJ3vhShU(E;xiF@92k^eeYycL~UZJ`Th1ub^ z4jcLKBaD>AFo88SusClHLH2lE40isxb7a!Rt#8@5zd5OkKOUF+ox_B6?nZk8OVU=| zxxC8TbuB=@5s*R6>mqqgTRZZYV^u4iFq?(CD%Or4G0|8yFNeZ0{Y#UiNjRz5ah4^~b{7%I zz4rY_%tneZ@z@*H*lB0Ye6PJHqjjC#f58^6Cv1{??`KvB6@LSa;eAyI(yh85j}Hrr z4&pyMTw2jfa+0dted2jDWumP1*JIxUnS*SDeSFfBDfYA{N9xm~wJUQQ-HpcQ-`auN z0ri%>q1=`lVaVt=V9+(IKbY~=%GddQ%Z9jR{84F><7qgDLK%ad#EW`>JopU+++%|r zB=W8w0NW}eEs>o6hpqRHYBK7!eiNz)0t!;3Dhetk0s=}$R74OElpAeOBfrPtr&U?P^o^kJg42MSGdG_9GuQh*jPUKOxBd_^-pMq&; zq>23TLewpERu&!nAX$x2!H{k1`Cl)JY(YuQ3aIpiK=R_MWBo(V;P*_etUPEBv)#Tt z8MhscXUD(wJZ{M!<3i>JzUZKLa_52k`&MDi(sz*&YO?-<;p;@MC^w{bFj620dsR6NCBy0ySq|92SoZ$@s=b^p~^U=^8^S;uOMcD+bQf5T}`p( znr+LeCIZOf-Z%r{2}`<4*p^hPlkG8ZxO_qg-yk6XZ708W9I1K9h4M_d1$FgM_j&wq zubqjo9m} zr4JgnVyBpN1CM?HQ+i;Jd9qp6UuOy5F7s3;{2RzmKlNlx+f?whmCubF9X{ zQIR>Z#4^nf0NHeJj{O?*GOseBZdyzMeMY42KS9=wzp!xtX-id6#Gz*NoJ>-M#3(+D zIC0gm%sGW~Xb*we3R56@eV<=H(yKXd;?~;f1vKVgf##v0OKe?T1OxU$J~^MlPSMz1 zQH$7;3WNMCW25nt0?Tc@=jPH*R&qXJ@(9G4xCJAN+)mU`PY1Um?LKFvyOo+K80{B8G+ zRrC-mn+YRj>(aM*y%u1DYTPZ3fwus^;AB5sU(hh{XVnt|Ht#LKA}(d?a{d)*Y@s<2 ziLI8!{X|N!^cJ53GQ-8MRt3L~EqU@>AKA4}xaq1Tpb@im3CwX`@=y)|N#RMn` z`&z(qNHC#Wm*ErG{bT-1fQNP7^i}=M$A5d98^1-WU|zuS{ji{2o(65WwDTTsuJ;aj zX`exVf@rFnP<9YgWc;Y1Fa;=KT0m-KZ+m^tH0%2LyKj0^%|WCEjEaFfa$HAPVC}+v z+2xbbnDsM~*|mkR+rw7_gWEZsJkW1jmh;uip)y-f_T^BT9BY_|zPWY1YTjMOrS9x& zr8RNg%Ae?N4eMJRWR}(xnZ!1}QO;>eAy+s?lTJl1U|Ec}E|4 zZf^ggJ^Q|<7{Cuq=(Wj?@@3)-9gj0Gg=^VW?12~N>w2TkW{_uuIZ~uFvJcF%_49#& z)rEJJ@dfY13Az)58$RM5VX6Hv{{94bGCVJ}PXRoNcZ3jIAzyR7H_s-f|f z=|P=O&B57wIlF;5@XkdlvJ5^IaBe)KoU59~Gtgl7&pHLqDyxA`m%~E122RRe3j!LH zR4)+ab7j$EC^Q%2*3x+?981KF+wpzn3zHS2ycZLKkpK6AiJyG&SD`!N!{5A--=Buo zolReAR4%_qf(*SYoB1R2#5M84wzGp1Q7-aL|55kG{PC>9qP4TXmIcE$r_^^-yq64A zk4Pd4Ta{?TIKkx0sS11T>DseWodSICR)7#qP4S7QRVx`0EYv4t_j7&h-5Y&JaUg_A zZ9QP;Z=K*aq)Ge(yD)J!@wNdq0vCv1`($ul(9g#|i17?#rTFt}3hV5B3I?xTVSYhuTVVMc~}xO9$p) z>wy(k&waIAx4m!NQMf@;rdez{zwYu~TYRz3z>XDgkTBr_pv>F985^~KJ5RE(4Yf(R zM&9|GTyRr zl-aR*?FnR+tm55wEZQ>e-hI}Sm%mpx7lxetdGWNFpO$tEq`x=bJ6_P~KLg@2tr8%T zc2hr}@{{!^aQrMQ*Rie`(p977^e5web#P2pj!DXp9aE@#D6An{~$vO7L^OxS1nW@%S ztLRp9?VI#>aAO{ZYcakG?{_plwc%O#@lUIRFNEXN9z&7B3 zo^OEgztu9y9gf8*@Zl{_1qqq2Pj1Qpl`zckD!zFI4m2+`!7DspDfBC^5das?ouS~Tri@rAh`+Bju#fLq+ow?djxIJ;nTBh zV|8!_2;p2K3=ECCbSr%UoLs61v9&=~Iga>ZY*QdSlLPZ#M&*JeD{V_bpE<$tzm6o` zf20gNCPj{j%P5f^PqRO2Da6-ue;4JUo7FjtXmY=7f5@&cYCl^1Ni*CuVUUM-g}|#Z z_)f00oIM~FWJ8d_G|K(Y=H%Bk9H*QF&mU0 zULtX#-h+9SJr>j!ghp5 z=8$M3n4Yh>@ejJahmD7gz7;BnG(Df7-D_Jm={ts2HBNs%N91U%rvsQ$9W7kGAmNVBNVbnbYqIz3TeD*7Pw&1kPg3WGduwmgCt-sN?1F>vb>1M-H| zWimbxBiiz6#_wmzC$l#3o_pdBj(dr|MpgXRJy#wO#2SJxW1BRF&?>{ZVKpiFX|yA) zp#_|Jj=c|R%S7n>*RImBZ;7pBC=%v%;ftZTPcz_s2p1jb6ZW$Me14|25-+# z_ev4|u(e5r{C0c8Ex}s)vVy1hr%m@%ny-&e;K>j8QMULn^r{*zpcZUU8|8=M^8Fz; z<|cg#=G+Ie)*Q37lKmGNauPJ!wYgG;Hl-ClOZZye5uDivj`I$Ij%Tq8Ar)!c8N?5$S|lM&C@=zXuyh0K3N5Jy(_tI3DDA~ zuzWEHNLJ+e8SIwQ@WHX9CANi(R_L1;qwM^RGq6{wp;|!Isl8D(Oasnjn0zSKMyqvmiQVBgRML%_WXERSKUgewEf!Kqq>7jy;dN=Qq7KklVrczpUBPo zI4QyqeXIw*>{PhIHiHZ7&4}nq#^2 zNn_6`VL4Y&k+Xo*0?kgAYZKaP!ZE{d?Sa2}*9~Lgl0T8EZR527_ss}SR5*Q|a6L~k z!Xqc_0w_Bt+>99=NUpDVLys{l9WAq#%X&{x$r8cqAi49^cb=nB3rzHdOm4_$)Ta}v z$#?0@iC34wH)E9OHTUK1-ws_e<h8B!Vsg?U=_%8#I^WC$0CVx9DqG%CNhnh_P%1HErw4Pu}Kxb~p_%;-ld zXZs2RmjU(ec77aqeLJk)B`?kKB^*dS!4!kFw-r5HOQwUReDZumsO(gXtBT!#t(36x zy|FXj*|>4sds!hhz)ITh)~C-DBV(#G;O>7>Hr>vL_Eh7J`& zI>@vj_hAF6j>jgvIgo3Oz5JMgTc*9NmTFtYze7WIo7^$G%6?bRwfKQwr(FrCPOozm zV@Qn5IP@F9SrV>NPOpqflmi+hW4YpYS(7Mc-?%9X64#xVLfBY-%QT$=GRJ*%KZP|_ zNzS+pJjF&-ax@KYs4Y&&x~I420sXE~OZH<;8Sg*)O(-;5QznGg z@_er@kG@Tkdt`8@d0w&b%%FU8InYvGJC4KyXz=VYq*$KbY!BdSX{y{FrlPd=WGQo*upC4PRc-YxRc+G`%O*V|;XDKiEwh zlQS;5THXn~$=6vo8_=w(v3ce08!bLY+QFFT^D+jxTkc=KZseWY>fEaVwMrCM#dDU7 z;BZhX%2R&umrfR^x)~+AWD2!ZGDwk|Pj2TtSxI-kW#}&)DvuuxJ>(jBj;VRuQv;Ih zwNtc@YL@ArB07%nb2DeeqhFkb++)IMJ#NMmVCttfFWjWElqWjBUetAT79P(XQstJB zM7$kj0%-3QVB@SzaMNF#>&~z#eO&pOx8_2*cQ}X zf81@oK9a3p!3)PXmMFYp6rK=L@iA;Y1+NGJ&}Z3ME@JuUJHi2E)D?{;njF)l#k?B$G>S7z@6K)y%YJ zgiDFFm3OtW;zQ(>Jh9zUTsp5p1D7*uMtoYUZMYurBfcA#i&{pDW)kqSJW^C~vrV`R z8;h@Ef2~Shce*3`PHpm~sz~>ax3Uq?Ot@Hl0yRD7H@A+FK?`M6?P~9tzRYMB)rW?| zahVPh{&}4+p7^T}9LC9=B_ifJm@jhon^BuR_p`c-=iA)61Rw?*%QBlnC!TLr(2wJw z6oP?;$4Qk`UQ~CeTD?gT0?#BbgpcZ5Z!Hg4N&+0Cz7H9*U)5;bzjMou++u~AC-@oB zzPo?saNI8CdXx<|3slt#$1xsz%KKTOzpSALxqPPy4Ok}x1&1CO-fpW>4}XV$(7=W5 z3S<4i)c{V-67p=_vAYO&Ey=*OHwIrIBM0+nr=H9@h2)3OF1$9h;5FS1RZ2l8+)3j+ zQA5g<|DH+KuzT*_UuP$2;g#$NxSYXqc`@BJsqrFiNlB41_J&;rX>21pyU0f#1tVzs zjz-Y09D=UPGxylTdkV+rcxuo5RCYo!|LL&tkveRmC2ick8LCZl%gxk0Phip}OY@7J z2$m;JSI*khV)^vd+gYSdGPs=0f0f$}t7Rlbd!WzpdS#lN9fj~Q&fa)nc9YyRsqACg zN7mV#`;93qf|5tm>nG1LR@S30*KW;5~kC;quYBJ8%c8wRgoTNNpbW@8idoMt! zo$pN+J*+|eFE*&P#G;7@mKGo|d?VnENoPa!`C^|46|tcPb-w+8^aTI&$@_YeAJ&07 z=aX)M4DlER))4(?SVt8FV{!(HPzu#{;WyVHFx<@@$`|!> zU-zo7yO6^uvPU0tjlXTHJfYAU;=5^n1oQ2`Xi$-2+6N_FXtqGg-)QpmJgx?4rlpU0 zN_EM)i;)o1fQOwK4DR8E;i{0w33>)rG0f+#cY!0*_;fF>EK`w|aN2lw^q#rtDnn{; zk$&6x582f1D3A+OOZC~DTE@7`&xI#ksaW0YVIAeO!5i)Ajwu~flS;#gfQN237`;JE zT87Ii>ysObe0M0!vTeG&s0IX%^Yc0!8yt+cOco9FO63#?+h@cS=8Y0Z=$mu;`vzuN zyllt7S|V`4wUlG*J1m<62gRP71xJI_v(#@h3iG}j*V*WT=LO)@Y|!d|JSDn&CRbM; zij0tO-wY{t9M_e22Qrh-u$AAR3Aixc7_ndlJStd%@O);c&ii7jWZ??=R>edDr`rPs zkg&DpmXyTU#Kt#IQ%oCvyS+0RUx4~{#OXc|hbpOD2Q8I%dNRj6T2J3qjXhSV+%s9= zmULs#iY$8bzT|CD!_y|rE%tG~RDt=6jGrkCpkCiXMTWsVf z;kmfli}yKuht4b)1mM$%R@q8dW5p8y!#pRu@rk}c(BH&uO-8X!RMEg5q!{~Vp$$fq z1Vu(hw^>P{oNLQ?FTvTMt#&Fi9?9;Ax2D&7 zxc7^{)l1TviT8U=z)a*eiAc2o>e|$7?oFZhTfemxC)fn}I^5f^B{#TelJ^B95AuC* zxqeiZys%!C#z!bBrCm+=_;5?lhD2o^HNvC}4N^PBSuI6Mb3bAaLFi=87@?o_zaPrI z($1EHc>|lJ+xbnuq(Kl`l12Rfrf~$hEq$#_ay7w{<-=j!wc8loyCVM*yOFAxa%-LogeF2X7-o^a$9vRXMgb%h}9^rHCf**m7 z!ysGKN*qrq595=RjFTO?&C)bB_GPT*!R?t2QOh5u9uZ$21Ui>rpP8p#7~;o^Ns6eptB=zV#~+gxsmuKCuPm`^Q679uJq7Mz9D1kg?GxsfjoZK59IcC zyysC8G22)RpOWD@3_hS1_pqHxYn~czT%*YD%Ej};qB6Y#Py z&n`-F;Y|5u)C(Y%7&x&{Tjb=nfQ5o zt#v^r+b1OoA^u~E25x^%)+n#59Evi^TKE7~3E{7NR!%hg777yYM0>5CZnr zfsA=E#U%wz!oWDCflT-W8?})5(kheN8?QSJZ<;AF zYBrsG;--?ry8bXkPxS*m_;2SxU@Ky-;?0v;9aSNaVzv$tJ>LJ^Z^G>BH<@O;u8sdO zA~QAp*}*4PH`g+O;J*MX;=dFX80Q94P?j-~R_DF=aCb!GEmJ;CYoVjc<>Q$TFXH~4 zw7P+TRbjkIee~!BC(k<8J!u*Fr&>+FZn?eu-?y3ssA68>u>>8*PHLgS4Hg(mRxsyV^yL z$iFov=9G`~jY-zr%y-2xH#v4B-pw{)OBFzvRK`Ito|5ylhv}G-n#@fJ`;gv!(EY2N z)24-ZgyKhua9)FWxm`E1M-;z-6O;loIThW?8;ts`C;TYI$56`aZ}?Z9Nj6(Gvx9`8 zp?_tw0SCi2DbLg~=kC0k$kE{%=6b@|T>sQsi99uImc~2gY7k{_GZh&dnw&W@L8P1{ zhJgXFzZRRIXs=4I#8g}CH%T%AnEm5^$gi9)5aZwBFc6au`$J9H=TQ2aFscD0xm5)Kuxz73%NADNx4S#&@VU@$|DN%&~5>-yK7HzN7FG zWFf1RiVb*7ykv529qaO)XJ(w z6op_F?DgY!7kyDz8qOQ-tB$O`1FWYL?gba_n#^p_g&*){a|}m9IEn{(?hYf$L1-?o zbZU@wPmdkSZMEN*LIlod`&qjhNV4nHjK4j{21QNC@}Sm*pf^54X_>`oS1vIQSqM3* zA$jLgUZf-ofky29L7a*v<>%I4~(jp=HK-#QH zSJiZyKv6hk8dq|;Ojz-uqF&0yUJCeIafM&<9C^@^WGDrWXNRua+OYCs`>^DQgZXEn zuaFnz&kWLWhZNnpn(f99;j$>c-Xd^$;QsHDB0Eyl`2RC*C#x=ZaD46aji= z45d^AZ{cU$tC<+e~aVG*yrS5k5LqeU!rNKfb8vmfjm#;w_VHMhsIbX4^UO6 zsd;3_{(QD2t};ruX#&Zq2{(b5qN#ta6=4-qTD>t*!jSVsMJm6ar}uQjr-s8hb5xmBS<(qy1oa>a(cDOrA0OPXKE@E`RL7!_`@to=|#0Gh;V$ zIwXDA;A+u%O39~EEZ z4d(tm+}Ru<_m~w+_L**0O=fV>l?ykQ;PxXJf({pDX3PpB&nVcQ(xh*)u($90f6v34 z7thhXN2pVHJZgPt@n9$EQ8;f*2Nlhy(W?eYiQyj;xsWR)|Fc@mAG>QhrpBMPIR>n# z(6vimV~CZsz=5tWkDev@m$RrHERWOt-)Uj>c{sG5qJe@TxP(k63Zv5iMR#^H#Lspr zw!VW<+6L7Llb`B1otvBtPFglM22 zY`4ldlnyi)9FOCPC)+o8MId(_Vk4HgG@G=L9t@Yf-16TD&Z;&3QwRuuGngI0C7mpB z9HUrz#2PQ(7rN&;E3ckF!}XoKR0rW^g`H2Tg`euo+Et6mBQP1wko-P>{9-iRzExzgF2xvy4n0j*k*@rdp@!MNYA5c% zam-cxm$jpL+kbFN6phnpe8F$pN8LButgJ10kG<1|{+U<}Loqb*y5?ovS4qP*UcW~+ zzXRhM<88Trddge*Wzfni$3Y{#JR%ulW)EeyqZ<`e0@oHW(7TbWX4-{+A7^@xA+TD* zNU!e}e|O>0o2%VRLIkm2Y0c3G}Mp^3XRCKC_NtW2qcImZHn#0ki=sIi~}`VJ(1rCHr_w^Y}zDIC_jR-=Fw;4T$Xbl&&QSQ zU)#oIPSx-;7>QICsml<}_0pz3IS-fZiSN7=d0G$g?&xXKTh1q3#Jz2G>j+%Olr#Nb z%@GLk3ApT0cplg0+@=%i3$*s#_FQx;xx4F8Fb#NT=y;#E;WKTM0UwC-St$elAQ|^DPK@UQe5tf<{(gF=?D|r@|AsPe<2WT z*tFrDmzdoc??5;7XWnFmXp=d*35E-2TcI;C?WrY(+_SfE*R)RF^PJxC*LgDhO>$qE zod$I`1xmeeJy2{l#%V{*PEWGm8gKJdTbZiE~p_s zYaaLL^Ot?`!+lT}^iagU9x&xhX{!tfThsOfW)M#*p_h7N|7O`E9`gJYlqL%}&M(B@ zOLg>95iz4|jM>aK@^1tJuR_u3-Si!SKSZ~cHwD&K7JERhb@DcGkHaYdWV@oQPX|1Y zUz|#Y;e+#%UDX?S<=LT?WTj+-l9Ht~3KPJNbAT-~U(CV<&5T{Jfv8E|4eS+wge4x_ z&tzB8{gleLL(%()2g2F>a^Jsc+rTC%i7Ynx-#}?FN(_Hli$d|WEGw@y?MR4d#7kZ; zyPn?1gmGDO*#%`c^NrU|R_x2&gY7e-xL+dr7lkADaE9RNgwVs-8F4r=v7z= z-oO_eqYxn_q58Bn9gh?izVOiwS(uoq96-E1#@{aAC1!Vdxq^wojd*|g1v443^{l0G zT!E7N2^astsvZ(QkfN=?vN2*J?J{-NR4Kjl$P@2Ou@|D&sv;>#tfP^zeJRiAG2B=lh-uf544rU;Ugu5m#eMhq^E_bf9|P)&#rN&@Wtovl zO%WB-J=V61S4?p!!J>!7yA+H$e}^9|J=tevyzghHU32=Yl`*wgtZSnvs$cFkAFupG z#21mj>8+acwcE07ED(c(N0n;r1tD%tBO$B{f{3(whMk{u#LjJit+h^eY~v!(c_CO; zhE{GXwv&Rsa_Vo_^E8el{Ar#H)7!Dh7o~6@E8aVO+qPH#91MeSpP32ZOVLo596b+% z_)VM@89US!t|C*ARNjVIvbG9k6BP@w8V-e@$Nlk~Bv;I$C49yhI`!pvL!=ff{uA3Nite2UBISOgR8H61CoN9(ecthxrLPuTwEAv1 zu)=3Ke`NLeUtfsiK0AVar)4HsdC5__g8Nq6^cPiF=b$99lUq~%_{Eh0xpd9QkBEwA zV_*S3e)YcOz5Gv}-ayd=A$#(LL56}Gf2-!(;^K{gx=hvX={&GJrnl`zo;FwK%;ZwP zQIVXDtpFuVsK=nf*r%;k0E3uDzjjp}J2QE@es#Wb4+lxY!6%&sG7k5AUC5Rzg6hd- zhMUIC5+NVfj{ZmgyDWcyjWOZ6(9P4&6{twoWd?b+1b#B6=&4U8H`VX5gn8Keu9naa z2lPL*BCGg@^`+AOK9y(0G)E$fJR}T_^u31G&(O~Gkcnq@a|P=kME-wdIAoUtTqU!; zzDUBnRr6=`Txj4SzOPH>y7c^}o5eLcih8tyk*}A`PgRPp&vv;k$t(Ixe8|7-Z)3n@ zBGmBCskr&^t#Es;s~lH7rhTnRgK5~T$sa2RA;3kh8u+_4-#()8!|fi9UJwi~2a{J8 zHKCG+=kwcTMPq4i;v7VjUiq8IC>0pg8_{O_On22)Fls%m)Z36|NxV$KlJA<*lwkH5 za{$asQsa-OsdzVCEloBYFViuml3j;_L3mQUH?*tg@sT8=oJuG&8X)NE&r?zaF@EJA}X$RZ!5$FHm=G z5CUIOjSv08#M|fSarw_teQIL*=IA@%BEEU^Xqfr*BIn>^JZOa|u`dXQaZ^0$2%lFV zYu)&(VFl4YiB(5i997s*$c*S){2gTRVP(ru*rS1WGE&?fy~)q0`AO+Lm3#TYk!lKq z?2Z}P2!&HLIQpbZ={ukgUCVFC;7v|eAJpN>Q=`S8*Eb3_vHz;(DKdUlS>=5U-N^Jv zNjhC~VK|Y#9o29OJI8|WW*&3C!x(U2r$W{Hg3RjCsXVLer#kEq z@=YD|2}SKn@&=x~VSRbLColyFg9fE4RDEM$#q=v!x)jK{@ZX+fCFAWW6o1<@rt$Ys z9lVApxha^dNU6ke8S&xv>1RJwLo&PM#$vR8rlU`OT+q;JLx{o(467)Ta51LYaXDG$ z<}nl3iGvhx<1(kg%ju&>wh5{HNwht_7z+XLbi~dV54=A){^S8+qVzfl1FA1cD05$2 zt$AtOn4A?0g?I6NrPN7xA;p4iyFkAdZn#SEK+owt$!EP25(I1jEmfAw3Tv-buxr=_ z>aD1Y^Cky7h7LN`qp@%?x^BUMak6ITBv`y>V)!M8t5O^_dJ1>B^CraoPR?t!pz(3& zn-KcwYkJ*PmOF6E%GX55y?%boBgR)XiqOS#m&m2sA07o9SsvBh9SD(MS6#HY&EP`FszV~zFCxMw%;tZ!IxNBP8SZ;CYH zdA5E!S?%)73xN>zGSx?N;YsCi-?vkV+mpidmWZwJ zZ~e2G?&_NH5e%EAoA8;w*U2*{71o~l*Uib~!r8lL3bPg)RCIKW*BYf)Rg5TbY?2vW z#dX9~k+$JjzZ37;nJX&siV(Bs$5S?3c2RZxc>hU7(Nr=*#{*Z{WM6wM4P^A23IlxR z+nbQz!|=-b?Xh3mV%|MY+40-K9eCP!|3~8%>VhY8B{Y*3Z7Z}WE@!^=>*y;M7i*xk z@C)H>p-V#U`wcFMBI`7!dvYfX+_Kk)?2=~X*BLNu)a7xL_7^>7ZSe86vK(e{yT&pd zJ=>!r`%7?#eyTl)_km3%omNa3WPGLf%q`8H(1k#0)(#4kM-e*CfB1fv9ueC);5n+p zA?rk+2D7jo(|jxysyO6m2ldOAE6pQsp)$V@HO+ZKZJEILI%L z;mv!Wl`+r7h~NLS`7gajx>3+(5^4jDka)#ysw9oP;CAlF4HmtkVd@=RHm@H3oX0PhYPquQq zNZEN}?dgF{uGPzf7Y#)*7jo@t@IF7$BEbhd2DnHM-S^n7FtN3i-^polwl(M?DxUQ{ z&BS}hS1Qnc7dT208z!vb6j$Eb98X>MH9LB^QyxD(c6%~oTf48WPLMd z{OMSLZBy9ZnX2N0==&W-0$ejJR(GRvUipa|tj~PR-!M4p@{#*%$*Q0E+iTcU5!US3 z^mJ4z)cX7&4rjmI0$HOgSCC zeC#{qF(jQ^`p4!7vHy2u6?bcKs0-HY))=w(GM(X*$q&vHXdgOnE&G+{fh)GLHsz7Q zR*=VC!91CK^=3Zxo5)@6B_{W&y1jC-TYcQq%-2_ zX?YlYKwhkzYE(mv`$F}`j)gC~^QMe46^+&(HYoGCvk;HleB(LreaBOl?Cm_)$Ckn$ zi{QlL>nAS{o*%KJc~K&KIkv7dFA-^l1GyTxn{+kb$M~=;Xw#99?S!F$PSywL9(daA z^yR(m0yE5M;?ZLNA60Vu zP^w~;*W5_U9}IdmIxkf(#xUe(LE%V^z)?kQ)zCrmw6aatvfBn#Z>g6HMkyVRp2v@SRv-SE_Fot~cg8bkgq zw&OKjew^W1W8F;ORi3!#7n_3TrL2BHn!b-ob>3BxmVV)JA@yPGSCng>P4Vud%F6EO z+f8}0{&ohMt@y3+vNhV!iI|JyQ#M(#G6QzvTdRZueMA39_*Jnt>qjqY^0wFWPeGW8 z48dC0vezT4UUwwxlVQx*nvZ6@Lg!8`|y#X(sS9kCS zksK!~s9aGdnkK_(CG9dQicUIkcJKHM97+VF@ojDY*#`aQ~~VkNlH6nY8Wkod^NX)KRNQBm`UWNVM~!u>ZK_&Fs?6~jJ;7|TvANN21L9+IL zRta*mgyYxouH&bh>whHw9TzSzR$Ir+SC(gVMg;H2$44O&=uy|YU`0Hp~63fv;UW7DT_ zACMyOmaq$)E#31k$QRD^3-0l)0fy`0F3`>%ej&DzFgp;tk^?)qVX!>|KBFTJA6aih z$0dFDAOz`oMIP?u476Vv&P)i9VnL$Vrr%D0+}xzr5BL*ZWFH{+#zYIJ$JtK6D5qxb zt6-shd0N&&&2kD7J^$#a3mf{vAr5m|Rn%s1*Sh} zZ4AW2@o(k$N9jvNK$K5bzs>p&pz#tyWP0MA9xaicUyfhR$aP}We=rk;E%Di|xg!92 zh*&4NADRP^jEEnAgFJ-jO^aq?+bwTGB!C0^WnhkPHDB8@gkN{k5~|y9X(44rR68nF zgQw|PyMvdHX!#9zMpNKMAK3zuKXej7Pvsfw4R)NZ2Dq|Y28IcrBoUW{L5n^=6Awxv z(#w932pVH~=BLFszB;M&{5O;K_rIC6B6&OXV@2TbY+KU%fHo^`sb>WUm+k7?JsyJq zrPrchz|TyAn0zj4DeN@8{|bbbg?`?-t6(?*i#7E-u7tu1R{GUo!ayTkfM2YPdX#zm zQp?v?HGHmmK2%ewU^>!$6$K?iB4)d(Pke%=Gnne_0pxVse}cUxt!P5yzGNo-gk-H6BJ z$o|35;3WlF5<)gp6m^hX5*j@2+b!Cr4qfxx>-eyZefJ`3R=sW&P&KLu2U}J61_mkr zW7An|m)|a`aJU;j4`@meZsh6i9O+7U;YOkN@Fy=qJOZWqxBZIdB<6~Rt*SxmjD8l_ zv3;CZ(#WG^fmYIuM7(CKOB#T^t71q+ARX{3ITjA@48Wd#4tIzhz_^dv82x9f<*5Pe zW>6lxP2w%9zEKHM*P9s#7JLbn58qQ=xXoe;|6&g8qy|~C0f&b(C02PP*Hx{K8uo>PW#bIUE zrHk*xkWLdhfu<3G-*K?7eP!-DMVSktuFOoP$#uB3vRlgnb&nhDCkSs{A3#h?()Dx| zH^#w3s_K6nPl8NCL?^onxK=#8sJ>+cli)9NlEh-;VMWj9?#sx(X%``fkz17L@6Ux( zLAF>%YJl(H^C{7_Mcv~7)bPV88q2ZKvd-Ii8NU9k2Q^O&E3&>P3EiwBN~& zL~R4YI>BR_ibgEqWaXE5fWv2z4YLt$d`Kz9ClWl}5uk`&xZ?eb>S~bW#EZSvET*6s z-B-6L<->)7^i1Nf*ELZ!xL-ChcWS z;&89oH%c)A$vTmaE2ZeIQ}9Fvh)S1!7)F$G~T=YK_GUS7$P-Y4M@|^EjvJfl&(+$ zwo1v<@%?~k`{yIgYOo7x()4$3;U7+BX;|>|)63@+vT6Px;^}?;!AnH6m`65!sB{}z zvc>|PT{OhA*;EO)W=-ZoyVd2QykMP$gq{c11x?(EuXjfsz6w2F?GOV>d)K24*RKW*m= zV+JRu-#l?e)1G}^d-PsAPJaf@ju&J7fW~=Ym1hQw+cS;FFIfqyolSbq5F-5$nkp~l z1T|~Y62ARj_jecL0q2+M4ERwIWD2WN3qvCAQsi_inEjUx`=Z++N|rxH!sM^$)hrj? zQce^~E+Xq1@2OmxTq#OpKpNAY-Ru%xUgbRt9ZT&zYtw?n$uFJIN z*`{O6rn+MjiyR+D_k<4e0x7^SRk;?nBu3N-8+Qk;ikZ9V;@VYyIO0jv#8dWhqp6Jy z6ZGf2{R_wgB5?;G1>IBLox00%SWuzO1YPbzm(vV(Vm;)mG?Ynk#9~-|1{R%U( zsB$sloLa%OOC0v6!1WF+%x>%m-aM^#JkrHBQ(FuWu+TwT1;-npz(Rb(UuwLrgL^(i z{Tz$j3+B-4oHHRRg|!lG8?`c@J?)%DmJkO*OQ*vk5u=rkJPWloiudsz!4;P0rmi|X z2i^&<_t)+@cC+1kXZGs1;4*mQHg*makjq_*`X35w*Vf!}DU$kLN<-?CfR0=_^;X+lOxp>$NHBdeY z!6yQMj3ZxP>KPa4zx_GGO!BUFUWicPA3K~mLxG$^h{$X}A|M9eL{7*lerjPkac_I| zCh2-s&7+-caWS5nk?+eOC&sl;_VPvbdz~#{t`}uFYFE_2q?N;+kY0|F zBZUhW8zEXpLv`#9{57b^RWZ474IY!uSrq54`4^Gwx{X<7NB9?^A5oXVuA|OZeRW6G z`ugZ^7HlWIT|tE|A8+aLlHpyHfDC?&VajVhB&q-rK_@It{;l7YVuqBldEGc&f$+A- zQt)uG3ltv?r6F__7!^fF)L2s6vnr5xd?1J;@LT;jI{+qsNy*U86$Utc?;}YU@)pa^RnvfZY;z@Q>RhyA^}p7RPxvOF zzQl9gB!JL+KRKAD!kUcx4)nTdd$^}i zOY<&PsCA%sA7~|GEhAS|+TfoPfw8=IE(U?b{g%9SPIb9eeg31mM(Gt=EDhp5S!Afe zq;-PBz%p=5kf~5J?2+Bi$D&oLh&ElfwOqi(NV8IPk2LR{SC%+8#wEr=V>IyPm}F{@ zM25S>Mh`jp5hQXOq~2siv-1mHRb=)*6%$+-r67B2dc4t>W7_W5=cnSedJ;e!qPrw7 zy#K&^BhP+j9;||U9dCt}T$RXL>{n#ATefb*2^F=^re}?QzM85ge_$sr!vX8yf-a7j zaP+muN@U`CPJbG0%Y6g1@IC5F>n(Dl+W>oWV#QQI$PgFtGcFe&Kk@k}Gg z?b12vx%c~*%YPKEp?RRDvN|aQPPC>e)1qMM$K_rzJq>)l-7+?SukS;-y5Ky`sBvhokQ;_bCVHjXv zJm33#dw=`b$NpaghWlREy4L!g=lMHwx%?^wX-#yL@B-EZ-i^PmS}CiKry_)Ly_h_? zX2kuCapPpmtCt*=-P;ioc?zVePP=nIQ>^P7aQnNDF3qEom`{bgP;Kua#90xp`8~L| z4kB6YAE2Kcey^-HKM#5g#_7bcSM8gn`Vh+>mluoAUSJc-9Bw}GuxX|-N%LN}Tk|`A zR8_JU7lmhp@xmw;oUeWq>@#=sT20!|mi?wy^d8;5z_3Mo zU7j`50ddlW5)s&6ap^r(|8VCttlb|-Iqr#@uCkXxBLrD|e@VXJcj;Fk%-(>8&|QWb z?QH_hNP-;irV=z<6NWHEplk(3bxf4Jv!5 zpLT{`%aUumv@JDP&V{=nkIXL4^G@0ESDCuOv~aP~R-N7}V-jqpJ+@3(?bE2U%16|p zKZ3c57F=D*iq8aU%wAdeWo_6O)i(Z?qxvsS)9wpHJ-hBRK`ipmXU}V=VRYqXGE1J| zrTeAlr-P?<=H)say)VM(5T8TYhd9a*Ks3VtwB##ob5cNYZ%2k|zXdqNobsCfA4dz# zoW<2!B6wE1W(5?vsy|ukg#!#sWATp}qVDX5Fa)9Y{?KIija~~(FfB`9MQ>O!Zh;8} zH#r)vw*tIBZb~Zp^(j3W4a+y1nGX~ER5bN3*Wt)JCNj}RVKInIPO^t}R66ifgl!wV zp>P(@P#SV_6@u@sk6^;`xPN3iqhxivznZ?!-Fx=KlVmwL4Zals*`~Rk6QsJ(vI&%)tY3ijGv8L#joBkJssspL)|0_7hRFpp*L6OC#eRYVM>ahH|_TU}0Mg zhErO9dHwxn|J9le&hsd}Q(cx^0|86oLrFv4kB%@vXv2qAIhRsgj8huo}1^ytN{@dmYwIB<-`D9z^;QE)3tmeA)2x2#NaR zggkoaBjv{Y1XxQWm=_YXRJ$SZN7-x1vhyW^bNa&cZg^ozSQRe6I!#KK;Q}VroE(v+q z>^Y`QUNeCErrftCV)q!M`UJ8cuXH~#u5J45{aK>O418dDYlrd+{DGJ0oC}DvTV2)C zy2FT@z3hpGC3eOY65o4>7y)s|TocclRcC#`8KZLvK_3YIh`8s_u*R6V}Q6Gd4QRb0K__cHQi~e z2{aFJj_`N*Yp20Ga3j3i^Da=e8UIT>ZcJ1-l|#Oh_k1xf=p6n$Az#3P=vlZ?>%U#9 z^RaqC*j${}vq%R#7M2C-XgDzgVKk(jka{41rIJr8%kLmb+_svf;`N~KdPsVSFk+Na z#F>t3Z5=okXe@9{C@Mo@dAOag^4yku-uH%cmDVb}GEJ_eDq)i>7Ay&n8a^-M9HxTg z>;=iicti7+39&*u6eWyAJq0pZsQ?f8WfD3 z&lN4NvuGp#*&JQK;4kq}toDiEfe`G$Yc)`gV0&|*b3@W-k0d5lUX8e36g4KOeKDWyEeU9qL&}ede=`he2p4sZfQ7Fsn@BVCkuZ94{DhM?VzfX9$4_o=;58@rv>q!{bVsbg8 zX^cZ?TBX5-z-6D!e$1Jd8Th&VuQ{JL9?DDD*s)SXB?|5a*Ke3Jsu{23k!%11wQPjL zW-kE>dMn!OBP5CM&V+a~Nq}I*l`_?gbU&74GjuMP=730pqyRz$&&Fqh&ymk*yy`3u z>~tq2?6{yHr*<7$79Ly|*qWL@ArW3(DXa5tQ#%Ys)dOB=Y9lXPV$!HRV$==nEMoy|` ztP=quuC(%v0=pz=qB-?f9|hTIO<{Qgl?3^Bxq7w>SjU8Xg1zhRXD@i z2MmX7uq+0CdD9ZlwDbE@vQ*#=)<`lPq233Yej@(Nr)OU%e|l_yT&6E@qHl~S!qTug zTFPm{>+EHazuS%Va4tAifG_G6;%?ZW;nRdPU#p-i^{4axzoL*{D<0U*KsaRtzY2AZ z2Q_^9Nuv8Tbg%dn1LJ;t9W>)Fp>u1bJHixFfvrxL`>@yiJo!e%Kg44* z7ITtWy#N^mFL}9lfv$li-vlGIqK3&vTq;an#8DSKz4l)`t!zie+0+!|4x#bj;Y+*S=h6-gGsHiHcC_7nG5| zn|yk5pO=4?0^~NVMEm=RllVO+yk!UxY}G-b_%3eXkq%nz=p+yBxggOvE?O31<@iUt zzL|C>HL!k}=Navh$CHB`Y4aah^b3ZTcZoHJ_^0X#`}0YC@3~%A=`Vt?uJXHP7D6PN zX{0Q(^?l!I!fPs@)w-g=Ykk^@(e>3Fy}tVg^Yq-2dBGIkSSYsoZW_h6m`^$L(4QAt zrP@Xw`^ots0aw2enzIgn9uNPBUiXO1g5*=r06M|Y=62NKk~9!4lm@)k+f8$D?ISJ9 zr%%$Yet4dw?q@BRcOek>hVA$2epj)|9(@6G?V}wCsc!q|$;bNl+U(Qnv5?O#F-Mm43qn=fWX7HHkA&6W3*_{f!l@*g2%E;`ikB<_BcB)Gv^qkiIAA zSEFw`=XotTs)c)Qb1ifw-D;5l`bRX^2Blka6l`<8_^ZEf|843lt+u*%J(%lxFl1+G zqqT>e>6Y_kR$5t*doVLJDzD9I-+#jKZtDa3o^hQ9$n$h#hD>|h0Mf#^7!Ucv`4f7C zT`&#sx~-4+<>)LRmxQDT`1ZYq^0!G~1&Ym>B<6f2Yww!#X);p>=dU=7s(QhkS>H%1p< z?dQO}BX?z(ey&UV9xMwg?m3Z&zi?-i1P5$A(*l2)u3B9uQez1)65JwM#lKfZHMFfo zugKP~N7x9Fetk2N#?;IJz78jYL(%#oZ{c!7z~Ei5fFhzB7UG=l>x0($Q{xd4QAi@^BNF<$+( ziu>4A3vLz;ox2X>AK={ZX}p^^$B8GR%?P6rGi7z~g&+voAu9y-iijtmxSZ}%g8e5W zS{q(EZr&@k;sA#+KEBD~NQ`<6i-2&KE;QUC#unGD=dG2C5v4^el#kOAQ7K3VBth7$ zPL#<%L;+KkH@RuMWS_i_W>+(kC3#TI=)$Kg{J@2PD8Q1h68rcs&%`&`je>IV(Z@J7 zWJwTm^mBn?*>0GT^SKS|g{6>gfS~x8GCT;^sLzo37XIhZqyA`(3sxU_0yAfI4>gk& z8q*F9k%K%8yu`AZ89fP;JM{p92ESe|Mhc?)asPC0Xo|C6*#D@V3mnXju^@qpwF}3` zn6Qn@0nHCrZ8$wbvyS3k1L($L1uESF@TrsdEPjdkPTl@43+1b16R)NPeigE*)fRsx z=bnl91b>*pgaFMht@290UV+lEAvHGOk&W_mfI)0+n?ig9z$rJ7H z_hU*%!IjzD^qm~fr=Qs)q##jAQTPiMav8jB!utLaYRxm&x5bApSdPY90z1u43RNFL z(yQQqYUZCEKfJT*Vg2C+1Yyba32)*tv#_5B0g6@>)*m5_ei3+%_TLgq`%~(zw|*TX zVo;`?(B0(#G8DLDE|0=^5X>9`E^Ul)%dh9YYFM|tZ{>1ee4xKUgm|x!bS3kiNb)yK zgR49c2Sd$k>99}97MWBZHtCMj<5n}l&S(cZmnS{!JpL2MM%}yuxhvAtZLzq(jHB@^ zAlsu7hq@Z-L~ApLJIIwLtvhGM1n=xAI1)X3f~mqr$d5KIJzv5MuY}9C8Z5;HMdb!D zjEY(L)9Ujc6b_Sx>$NIaO0@OiBz|_E;@Q{yxnik z@P1OW2R#AB`w~lRBw;-fdW+?LLp1Y-1{MalG_0bcgKwZGpyAxXjRm>5Mh4k|39!SP z8m}0=h7n2C^;KLv>ip6rJtDi?8G~uYMUG((FE%yAY~Z8tKOXSW%NFpBId3Sh5G*M^&?VPE#79Jl?co*Q-R~oSknZc2_x%{g zYlLsYW1%8}znj^YuDKH@5riz)0-w~JQeNbon4bp;%Ps&T?r(kI44Vu6+M@`8pdqSC z3c5eZ{C0oeWFQ!Z@^-wtQXW`RNmsD{C58r4rRD}k0U^3A{LN+w^;>1wj%%9)TO40t z)F`0?U(#Z`kT+b7MPXGklGL&0-+Mcc`t*+oT}T$-~4c{lR*Bo@{Ryva>9;^NJPkrVw0?^!5 zqzd^{a>6|ssH__i>Z0#bBhhv1#2UT>iEA9gg&-bcxf7HF%Gng)Z6y*Au_kJFwwE zYmqw*E~{OlJ@;H-LDK9$wM7mr`p$t6|0U!Djvk|US+u7YPzff#RZ6lF3HC4q}Y*d3ZH-#5x*CjPQ>WufI` z#f+J0S@oG=J?Ug3f~YW4qi+3(|MyL6Pb}d1EbY|MLzhT&$?F-6#g}`^wq$0#Nx5WY zaXxHD0m#me8CquV(xsks+4K<_a^nRuVOZZ?XRkapZM z7}}|uiJn&Wl_mrclBre=w?UL~L*uL7(SEBkCj;DRTvQEr)A-TXF9=_j`r=3pEnjtn zlveHDpT%#lMIwcFH0r<(p?e{whb`KvabCGq%TcItU(oNj|6f!&TymU4REFRbuLWFcdx zY_z9hv@;fbhZ?sq8H&lKu;^;d(ipB$SnC4|UXkTL1O1(ln?u};bPI|$pz=Cdu;ZQG z9^%r#-0L9p3$j>+6-woDR>r&f;>*b0m>LX0>*IbxC%}Q%P&Ue?ek&JmwPS zW~H9&4@I_$w>bYwgG83uR1RSJRyrIt239OAY9rvdKczjb@W#c|m)2bKm#E(RN~B9r z&B`c~S=7+$DQ4?A-v5}}hn{WbC|XkPJy=>LCD}3THmaFmq$i1G`57+xlB-=57{g`8bI*V8lDChq3VPXskr4Nxa^jxI^Kbv1x)Se- zZT~^iAg^COBkYsB(5BUPv5Ndrgq9CGy@4^If_}KBI zId?7F%52!~vJufWmGXpeq91ZWa)@k+?zQZ2um;4m?_Ld+hn(~|%%~=g*JHKWODxU_ z!tdF@E-XWc)UJ8PSPg0iBeTWS7*cErYr3Uo%LjlbVH|>8LE|T$0u_snC0@I6sKp>$ z$nraMzv9`eC66W}{z82GIsE$;!Ifs|VDj4T9;y+x-(L26$c96R;^%Wej^?oO4@_`Z zc!6WZhlQ@_BmR`h_&>)nst==J0}7Cb_2|kQ2x>@S6$ye>;nBu4(ak-s69OWelgb{8 zW{vdc?SksOA|B(d+OA(EeBo%JS=7Ndu^M)X{0-YB+q44t^N_7fU)*DTas&yn$UAvE z5~+${=62hKq0YIpy?VYw0tlpWAdW?M^E08!qem8p6mItNLNRIU2hgguDZO*Zb49{U zO661%EO8`zBdq;9ncG_5U*&?6RaIuU6h7$SHMMK5tplzI=((V!<0EY1#f$I(W=r&1 zeY^CubF^JpWCZZj~|JoKFg%kW`Qdf+2z% zeOPpJftk(WJCXX8_+dhavL}&er`98a(_B>7$a&0m~O-9ocjydY@>gY2fCbl4V`+qIBW z>RW~^1h^CR(p$i5t@^l)7-K#yHs02upzmOU674l<$)`j&yU;Qe{dpa!BJ{;K;Qb;I zIn!M#<+_2)(?7DQG)c;?G|H*#M$2YaiHqO5WQ9cSS%UG9@gw4zetj0a%KHPN8N+)wImkE&uA$xhL^&H+*Ul= zFneT<*?8vx-94LhEqxR3&sSr6EAr#_zHnq71!CJ_cO~TXx^Tc(k_v4kdrV(KlU(ia zC6xUV^7XZAzDTzn?w57)`d+U{?)=I8@D7uoq5-OQGdo)xoBL+|woYAk_BheI@rP64 z3(Eipxos@Dua`C~v8Ki>H2Qw4pcIpJ@sL?qU_brv z?KVBXUzedv>+4!Gs1TxFm`@AiCPPdcIRLVi|DTq*ZyN0LK1=SExBkjZ(Un(Y)ftNi zVI7(OQz)HRr{)h{pWI6G^|U9P9Sw9AY&`#ds;yrLBcmv>do+@G+Pj^v1Fu-WDR_~h z%`3IR(M}iwae_}>=Xip=v0D}U2b*2*hJSQ;vHeubbt=5#+lbS85z$=9kfxQp9qY-Rlbt6x+fMV_> z^1O;Bnn(PQ*pzjGFfJ0#mz&_6QRK?rPS%YlSzV@+0za@sb};o@Lv?9QMOgC89(#1! zFX0tp9f?1?_4&D_Q4bTucRoNg8`YoYw}rb0TfT&tBzI^s7vj=IC+ahKbrY!iIq7pW zdluI})E#JOxxRf~St;W-(jkfWD$m(=>%;T4%6i9Kc`{>qfUjVU9A{~e?UT{D`hmao z@+7JgOrLnT>Llj!t~(hSX&-qt?dYBN{QUbQ|02Nr$HoUX`hxmlwH4U`t9+5?6b(m< znBm?WC&gqMw)~vG1j&kQT|X{NMAv&|^v`yS#gvM>0#Ad50$f+-VKi+0=Cl+{ulRmifs11hO+wX{(o@WI_j_?)9+TS_){q_ zt8KMRzc=kz5;k?e1+3mPzL8nKuHny?HupA)1AUGzUefp&G|`SxmE}(h7mUoFuYYJf zKTeK+Q9kg9Ud(Ud(fi~~!hi=uJQK8nRNAfZvLL^>$KrOlV{?y=?Thx3Q-c^pc0pJS zJC_v?j!TC@Z;a>m3=;k-M#?~*viB0Q=?-Na;^{!-Gm#{&Ar%26;neBi@29yDx9q{rq%UPTMc?a(F|>xNlmPNx}&QBi$JD=~`&@ogV%F zChHv*T>gu%pEfbvh(481K6h$A+lk@_YxWemv8KLhsYL8q`faH<8b<#=e7*&{_kaH% z5T`SX*V{Zc{JK8izj~_k4zKC|Wb#}Y=l=)r-+ukyzv*vZFg+MQ@BhZ^e;ibHw3Ckm z+wiP2T%jp2mFm->Jz`1TII^Gh_`Xng5h%JV<~Hs0UmJWE|5E1kc@8_mW93 zs$4EAjtp(TR6MdB^UVDMB2%Ojo?RBnIG6-(jAei9S!hVet7w7<@KOOJvL7CCuA@4#M{Q7CVIVW6|8DWEk08&xA*vcKCyZ^vBr_S>E4=&GZT-g0V;hh4=ei?$@zYjP+OIb6d3&Az9v$#zByj%XvBxBFQ<8ly$uNbLA`SyZ_q&gpMtmZKg&rVt|P;9zk{ouB5CdREN8f& z9LllDe42NPdckz2k=WS)h3Sm^!^PaB4WdXoA@8IMSOHVqDX`)TUD3CXc`qD60m{^(J3d=e}?}oeLuS0O<0so`5I_vq|$U z@&w#&Hr6?ZU<)hbaQ>=k>eKy2Y4E?*p5jH$KpP^%kj$R zX{F(P!)f*s_H6)WH~n!mYNRuhC_GX5&cCY6%E!}uZ&*j`R_3D!wS{%?Q+!&E>s>!! zOz{pbY}EN<+)~x^x3X^jW$od!%SPIvBM<>(JPlUG5h4`}KM9%F-^gte8=M!&SPiZL zLBZ5A$zi8BuJ*!LW}?ojJ4smeQZbm6-xTO8G^Y^a-hP0O^tL*X&d-+?_`~o-aMsPY zP^NK^ahGF#z}m{)%|E5XOpYRZO#_)yx&q$i7*x1c6J|b4_G2cO1I`z_maWsBQ!U~V zT=`6&d1Moc=M9W31u+;nIWU*?0V&4dncl!%rE1xIVu-cH|HXR+Rtm zhc!^t=F96xuMY>5X$+9QZXl4zIZG=0)KI)MX}6>84#pGU#mm0GzQI#^ zdK(K(m;U}I>yD-mxqm&RQ1TZ1s!Eu^$wDrzLKVXk-CuwB@hy1}fF2RYDqCl*%3 zgW?A)x(bF(2ee(K<-MI!9jb3)NkrNX!dEuSkB!U2rL3Rr$h1xNbw6v6vEF_;|CTGy zV>Y?My`vBN=)M^UwPb^uE))L^uj_ncA`fYf69rAwxmKv9vbVS)TQbNHHH*aV2~8?zE4OT7q0>qOoV=}X0O7E5fT zE#Xa*%Hwt+Nm<$Z>St9aL5@Cv$B?SP4n$Rj^vTW)#feYFJ35{zU!)zab(qTL-g6M$ z`7xVAaNUp_>j&<1Wb(bjBLciU!`5W?29?58}Rn7XHz3fj{ND;UE{GGqtv`E6Y#%|$<#MXjeTC(^-!)19B z12n6^;V%;ZncTD;^f&-EAE9$P zi_hHl?*>=s9e96tu96zE^4Y&yDCskL&M7dKvG3R|uekgjx@anVixPJZJBYb(8Q!N$2At*ge-qv9s_$GnXFGpP zu{Z`{@I_95z%)Doii(PHqs5x-+=j+0XWaaSDEY>XOAl$c_PP_xDeKyv(!0gVOkNx) z;T@M7FW%txxRfrf84n5H49M{kmu`#aUP^2OZO>${oWs6rCI8~d1`>I*k#|-@bg53O zEbL9U1t0-WEDyZ3d?s&_MN07(+sJx%j(IE(29Zijs?1u`HjI>-qPLj}XqA1{GAM3L;4di2%C0Emf=I6rQo|ugE z*u6X>Tq}r4cG&;@60oZ`N{Ghc&2iap&TqX--GZu(4@sVXCR>D%oT#PQ#qW(+`|k zNB;FaksSe_$juWJXb48UR#FVk=LqRtM{)&}jk2srd<1+%j}sCJUFPQ6W7snb;2fM9 zp6o@X#oo0a-CkInmYu5c78=ih)BuGCIhpO8ZdB)2I*NBQ;QWdp%^BdhHQaHzj$e@s zni&~Ll}OG$xvb`6n|i8{r-TE#%%~?+ISBdl3vg^as@?vi>vUr`tB}et?vZ>X{fTac z9=&oD(+n#es+d%6!@kq0b364i%Rr+ed9uya@ATFScE=aYW^=%Ga71sX?N{}~7q(4x z7*mtQHf10@IsBdMtQm5@a!NsDF{*DpbQPC&dHM#ujnim4>&48|Z?2i50fy$N?^yEl z{lDVQp1P{7jJ^sZpGrNZR&C0sxJbd;zA5@}lewzW;rr<$Iu~o%@@Er$d9ueC)kBlm zH>rej7&FQ_TRtA---~RStqL7CFwAT9z3>OT<1gj$SV7BLpuBuOos1~Ce}p%+{5PC& zSfZ^pOmNg9=zRPsa0$1!3}>~nk^>7>O{La6iGhzmcf&6y(RLoMYl_`TvWSc*JMENO z(O~1Oc(K?{cdzgCL*E+VdF(>_?BTbS)coQV<7~%eK{uE7xK>Jb%s6A{=pPN#Ycb;?atwAi(YjQGtJ-?$onT>gE z0=;t(b^m5_(fXX+*o5Rptd|#oc2T25DrSGg0Lj?$7ByrueF?e^FBR-N0*EF#5DC4{_NVe3W;wZI2c7$ZzmsMFBBPUjh}OQ3Zq zv3Ec+rIpV#N(eIgY?f*low;oNqmh!|m3?~{Lcwm{`}k~~Y_?WnrSdA=O@-yOqRq5^ z8<+9SV{Ae`Q?y(@JY`urTdQKqW0_UF=k$HUCe7A2r9D%iZPJdsU%=IK%HwNr>UD{{ ze7eI0EQQdJ?N%#lePn>uiz#2r?D|CKdNUej`nc@RbIo@(;Cz^m}Cz;1gO zMCG`hhOm{MjDs5pJ2phJP!@!@y#&o|sB|d2oD8Ok^#_-r2s}BqNhl>Ers{Dn${AeR zB-_h?Y*8O<{d5h^sJE3uuapZTppm8Y;Xa)tn`nWy;Xv=UVE_{Nl$6LO!032R?R?`0 z^#?qa?U-y^9JqZ5uD7-hu1|p^_^-UhM}bO~)oB-p*(S$^ci0>WNT;PYuT4J#A8O|P z6Xymu-}3-Mrt31LiA0=;5FZNfX4W(xfW> zE4LJsDg)39m`?ixj86AzF^nm@OYd&@`p>QU<1G1Wl;BQ~NP^@bH3tQjKpVNVtJApr zm|&gY6>Ofx=pce%vgKqmigEubnH7Z(%E^+9TY?pfH>hR_fXe zm5&mEh5*y=XW(#u;1Ss?_!5ES6n528)FXpL) zyKN7?S{xfZyZda*t+>`9(PTZy+aVYMLP=SnkM^@cO#||IR~oz07D{!uh;yra;0G$# zY;*J=em4O;7Wej9iB3)2KZ(z)pSFHpWfE7;xzO4Y`1XmnFFn5N>72TqM~_>Xt)_0GSfUO6;@7K`omziGPtJ4KY+4U&hfTSFR0 zxH@0>R4uJc1TS0JVYd3eHidiNojT$z!aT+dg0SG5b|YiB^;bc4zsQZV*ITGJ?Rq6> zN~G9+GPKcTO@X_+xryF;SFRM-rv<8{*LLq%gg3Fqkb&4V+q5!F)W-TrVpI6~Bs(J` zJV4mJjj_7TP>5`^a;)!{bNE_8@M(+hW?Eyb$4v3oLTf}6Zd{2T^Vz3R?@Gx$-1VhE zV6yMyGQ>T;SGpXawo0d?)GmSdo_TChLEAo(>Wt5hxG@ipWLGQCjJNp(M?D`pN|QqO zP%NO@H+YT;#Y=OgdGk3_(xPzT^e*TsL*#JK<1LD;0|R8IK7ZP^GdgIPgcj>F(H8EX$*xD*X*)dDOmUng+-Nxc`v6o&C9}=k8oi zW^Hqlw(~(*&|`<(h(Wq?QvN-KizOMaXH#2x7(JJUg{Vdet$l?pVUJE^L(iu;c^gz! z#sBL4vHqE&?=briP{Ms$mz#gt#1VUz3aywYWJ|~*p2Oe#G;$qyXc0p|?NIaY(H_F_ z!0Gi7`ehI{c@|e?ugxS(tFY&#>&}iKAe()M`CK-M+7rDPiMtZ*nBB}uBEp1)c2d@8H|@l$V|@WAZV*w+V8(LB`4i>| zIV>lOUhhI)s6kZMPvg>X9PR8UR|v%jSm)AiF!hrXgFLoOJcRN`h#T4&^qfu*v2A4{ zDD5!e+LHfV#M#7i4kC!wV(58r9qKOVn3KHxB03iF*a-g|8Z#^} z@>FfySPC%{e$&Fp?3+7G#aDTpS|L{eepeKjA1Iq{21f%PDrH`75gx7P12MyfFhl$* zxB{HzVjz`r71b5aR_Z5R8(;<)dd&KD*ljpRIcQhgAdsB=9S8pt3gS@L2Ut}?4eQ`E zoMqoC^7KGE{d=G4U&wa~?yI(Wx@{~V?6?8_;1_e{kS_>g)2rw*#2aMGN7p#KSAl=WzMv^loM&!-2~~ z#pFx>Vw8_(3wQL5ui;8?Ni~~op(50>M6<++N^rW;cXCTg#J;jTR{JnWg?8a1|2f$c zCD#MDh3i7AtTsyt2Bak#bY`i0#F9e-N+ypSuhWWD?AP7(Te@+rRs8ZVSbD=s;(9lx zpdp)iejt~DJnJ`|eF4Q%IUzmX2~G>Wf_>L;R=2aD(~1F(2NPmWavy9s5$;+oiS~$B zM?$URbhQ~bmZ{PO=q07d3?-Z*7=wv>SocM$v1!g-X_t1{_oK~PWBZl~&9bmkM`T|{ znA>kFg}>>}M5{FxUyhhCJ}iZ$i+cCgh&FZ5Ly}tvcR!Nt@mdUcCeTDDq~R^}JX>#L zK2;5@Le1DL*|DdI`OOsjSDMh9C`%^Q2aMUIu8o*7x10>-hLu{K3)_?s&rMJMByDwy z#b5td=QW56dGe;I7IB`yMRuAOeEh*$SN?<@jqYA|_8rP@l{KRC7&Z1|*QyfkMMc?0 zGh2}zWQjR~(ZjXb`09$y0-Q3_sk#!==i-EK%;mCTdv2!jcDdo%rC6&G$5 zZoc@EtN1u|nr11OdF*r{yNP^=REU2Ll~Jh&C9)NocJQp>s8s4zcT(FzwQ$4o zK#jS~)(n_!|7L296t@dHW_cgq;e-v*y7))tA1)=-^+fA!ABd)+k^AP`Ic(E;qW19M zop)JfdPYi!nb)1=3{Bkdmkm?jB_@}<@=MN>bN)+fNPD-7Wrl7(_M&hP zyQ5^O5B}w0b<(?*_tjR?KM320^JraKO1tU2KfaycK8o`d=b-l~Pasm!UM}jAAS2Jg zn_X5sx;SWWURHUO*Ky|2R4?FQ?!c3*wcBRg)Qz`%^OE^>i?%`_H;rlmKTdE=(T)6_ zV|dq?+j)U=AwFr7EmvA}RfDOYzHa@s|G8M`_f&(rUiRrt83s(yBHs-JF8%xq`y7_w z%0w3}Mr}h+7*J=|z|MNgB?HWF1k%{vV#8dYezBtC>C4QGGx*9m?4fSN@R!ecpR;z5!g1hH98^ACW$ zOxOyTetymK)r~zda8-a@K=Ae-^+J9_U~E2+wqrJ?Rf$R4%pAOV^_H-E@U_g|PTQ$? zPNAr16`^a*Z-pR@4nXDt9VTOt6v~)h24fDE<7lO012jzr*(mbTN4uzQ7RbW2Z8p-H zI%%GGIeaNo2IDegv_0s2pi#2~`pbeW}6{&G;rQN&Lv*5M9S=A7U#MV_i8MqjTlEQLvYB zfnM1D)_Dg}16Xk$xvn^87Pktm+Cd%zM&BJd%kYK(@p|!>!djmzdKZE%_l;FM-m|IN+vPZCPM;~3a24=mHk*F&ZUt(A%e5{acAS> zw}(w9!`nOMoR*9KPPWZLGvRx`#9I?CU>sqQ-3H7ZZ4DRRBVQZ+z%)B(RfNlm{_s{T zo+qc8x+M!FvMCza$STJhP$G#QS(OV@P8uuycyp+Z#HQKOq{Rw~Oh^v<9Ss))yDzn@ zmBeMC3xV0j>`5m9=VJqBqWlVVKTY!u=)*#Hs-Wl1TM{sA-97***)enW{E43*^HO zKnFh$jfAGz@gh#AU4jXli}=LFksmUIFDN%pQgE_~pCl?%&g1PP8<#)Z%cNJ-ing(r zB=|1=^NMkd;e%+oWg%WK-twoggAG4=YUn&Xg2Tfc`JR9AQtmd`*G-i(_*j9Mq+oZ7 zcvHb1Drn{N=kpmf4}m-Z>Zy>B!PsiC7Vr3lW(LA>E72;-Jv?J(X;<6(GQ-;L%q=Kq z6P5X=v zM`6y1r_a3(#L{cTKPWxWUviP~ImfNYN9}dl6x;yo4)<;vl%h#IzjI@8lgWzAG6JYH zG<7`A7a(uP*hh;MQL>vZAeL3h_u%Zy(IC8&Y+|&o9yMVdX)rhVm`lnB%oN%f;$1W- z4@a4NZ8z(juCFtnw`)bvo|O1grmm+9ymv6?H>dJHE%vqz6Sll)7f!KK@47sgBL#}L zEw|aqv5O)_SM6Dw2{F6pI|+*k7VWq%E|K?pgj>0Vt5-f}Ig6?dH-ref1WVVq9)~O% zbA@yH2JDXfMJ0SYys%Drb1|yf_={QAV^`j*a;&p2_N5MM;|FT~=dPn08mx_IIk4Y+ zz*p0)du5g&X@q(A;1>61Es1B+*2zfhRWKYcgq%E&k^fs|ACcd+&B(Xh;=whN+^ySbj(oHNgz= zeE79JsC~+wEPw=kJ%$=`6P>9&+(0f>w|5s`a@Nq*mlH0%+a(R{P3%S+)4eRR2c<1eS zSszTLl&T+|ZoNZrlVF*R^c>%GA{f$)QJ@qFkxrNI7nmw$8a2KM5>iQ*fU?_B4(kgx zWiBSXM?8Y9s+GN8^(g+Me+)vR$+L$;y;-CdzZMi$(cN}?#khxm{m?(KyK^M!!a&Zv zi+8W$&k3Xk=r#qh?;*Tf>b_$fjf>?SoXK$8)UD3auaEb?H~aX_%dpU+RuBF+BHV6r0Y(%#&@ zc9qPnFGTRnC6MTr)SBD@-fUlEfq&3OiAuJgornKYaU;t3!W7s)-KPmzJ>;A7KfAX? zn|(p}K7v+3&81#&`)aDNhN3e&KQ4HPiQwNc~KnA0AV*?yv95) zGW5CfL=P?3_Cw}1UN6-ZIY@4Z*byh2_u(D3EWtB5J8*2BF*kkNH(O$1ee$V^6OlW! z3|%N~%=U93AfFPA4S^l~<6^rzj8EkBMU4ygXgKWNGcNIYnO1S5FlnL7#f@`F++^#v zSQ5_tei*B0#T3KF(cAr{KH6^yF=%YCe3FHTpdBtUc5IP8A7B`^!=;fzSRs`{u&`|X z@#pVk#9h3sm2r{TbuQl!oH|TDc^IE{n0`;MPtfDx={jLQ^_nWWb(!BT-o9we^n&x{ z>+zD$EW>h*G48cst`=m5uWO(0BTK?XY=(lI6vnI*FN%{cFB$?x0l!a@Q9 zwpugxPk-#~oLn$m7h?J>{zSed(P#X@#psO#YFHLQ?#Al%mXzA!Em7JVHkxk)>Dlft z;WsPaRw?$w#rE+`p`NYy7;~++%VMvGn*9~eUinGwxJWYj%^}znGsa&XP9($jmeG^d z0rax{y||wOo&S*7WU~6+>VS2yqcJFMNxZMLz$zVYbDp)WjAGTCw!i*;OXS*w_j|tm zhP^D5qg&M8UFXFF+3-Ey4@1}&PAtp5mQim`Nz%kt4pc9;(SfxP_g~v_A;s)6EwYz` zgoC%^h{%;RwOVKfpE43(N@!WmRCfG>Z3zn)Yt45#%duLDWaMxn>|y=mWAdE`jKA(6 z3t!5|1WQNLI(+f9HeQP&rurK$zM*r`g^b~z41Mbo&2b=J6|JxqtTJt!U!nEa!SQlg zI?b61sS08L+VgMIsiDN6-}>ScVR-k=Q7e~Qs{6i;?fD_%`Reg= z8OZ=!hu-?lPC_N|-%?zd%oMA#_Qj4-#U>>;s_tD>JO&G#!}ySKDk2C-6;Y8cHT0s=L`7)<=>j6X1pav8T1v z<*A#|lJaR_Og5~sa&rai3ztOFuT+LEzXN)n3pV(@3~+@R+3)=Xhu;XgXJHe~U2xkm zM-%%0H@e9%G2|`iWov|pxAZw8NT7CFShMThDn>XEzT3xH3ijZTp9@^LSDVKZ@yl1K z{iWHfM}?6fx*py_&1&UT zwDW~O>(fDBFAk57th91Mc6WY~@t!c{a5hlHBp(d#=0jg&CmCTy)1~CF_f8Tj6@H0z zGpv#+d@HI6&)GIH^j1De$Ke?VRtY)M93 z7zrp69E;e3vjw0+_QBp23|w2Wle{r6?YB-XG4wAMCD<4;m&rJ3*9dGaA|!KXChwGX z%(D7Ek)(f%&8=HGGX*54^3o{LFtEc0&9BjxGS~3;MnV@K*hrKQy0eu1@|&Ffx|^li zp}e%`7Xm~h^Tt@v+psTa3TK429nkKArZ(-J3whc4S41+b>a?J>i3(Ei_p%9N=s;8c z@Y`5tA$gs7T2Lbw^PBx7Fj;I~zinB(TbzcHeQpkhO1MMX3_$UORjR190cLw$&xRS_ zGK`Zdqv}Y2+YivcKM!6|C_;p71fSQS#nzC>(qPwU7 z^XU!^Ux&_CtAQ%S%I&Vv{CSBuy88Bry>P1;3jL5ll8KfqenSx!e7v}I7_8zJVqGAo z&BKP7a7BEO9h&a@^$D#C7HZVax&ZWg_F`m1d;R<5L_xofuH1c<4^M)NHTHQD=Uw~`(ln6C%S?;q9p4%QGBwiNvrmu@bi5)q&$8)sX62Y z#1GQ^VS2RioWGVI_N1xhDK`@c*`YUXt+dArGx6tuxZlW5LewQV`!h8X zR^8OPY~YS3j(Hlz&znijB;);PyPz)LU&@U+|#eKlAi34 ztV5u!#Z5N$InU8c3AY5oqC^B?@2`X1`xwi!Pbz)%!fm zB6K`E49FEi;yAk)a(mqm?a5%4~c)m;#;Tf=iON8H0k(6t&^Sh;0Y1v8`o{ z7?KTNmnBar=~lVaQ#hgs;6m~mmCS7X^gZO=FRENr&hq#+7++(^U)b@-Wq3d7?Nkbj ziQVt!!sl0h(M8rV4>CH%mZw$%Ss{1j`z|l}lL{0Zxj>nt{qhVRIb+%nt|Lp#-N*#~ z|0|0hvHtG3!}<|KyKYFX`CGUu8o``+=gEo3hIu7QRZ5RlDTFXlhA`(8Q(^DSAb)l> zV!`mhop!gk{9f=SRgrBDm(#=5Z)v{|rRkuD!$I5IESCafBqNi0C!8vtL&0 zPu{CCu<;nsnb5*RK!@A6sh7-o*~ke{N0xA=!ardM_njQ^J{{3=I#V%JdNJP!4P*G_ zO-~H-jrnx2pxoz$uq_QTn-~-S!S<0g>j{%sC;#@((2u&RR}X9Ay~o&B|AK(}!hMoO z;Yq?*!gWS&V5lGnh}9DAPjj#ToCX7L6E%g4< z)_aAN2Al*%&lczw2yFdWQ0;E{zn*WNSfjp~lST=duUQ`yVpVqi)o22@Aw`y`k5WBBH z-M>}{;jb0KrZQp#qxYlQf!rUuwj-A5@UdZC5GRi)sc`e)`;mUWzL=-x>kG`4ludgX z_Pe{I3O%>x6)d@h!mt;5?LFha^oKUow*FvI)+Q$<9uEZecrn^uRxG`RpX9@vC2(T( z=Z@&fT02Ub@)<_6IssQCpkw!D0RK86hycHU31#GZ^OQyCJ}2q}m@TZbis8NFswC)Y zM?R5Hd}-TKjWy8bCkq9zJeMPVKEa*8<;MzfjD091sC(76xlA!?i)czwOY=ej?%q*@ zCwKV22Imh`_4O9wFMK+a{on^$WwOSrX_yGQc{3ISVxKNnrGz{R0vXE82pS0cGd>n{ zqFI3U9U|#8=}e|MZ@kR?QoX4jXrH^aqZsORixpU!3mB+LWL_<^fNp*Zi>+?OOUpNH zOjkgkc6Za^BZ4WQ#je+C{USfzZjpxoWs=Vaay;u{eL-tKSHwzif+E3uvLP|pA(*g~ zbKK>(cdn)-%x|jZ3R>M3|8T^ZCv7leiPeRHAtdjklqjoXKkLpn|1N_GEgKw7pucrq z{B*;ot=)9n4X1~K21G9E8O?f~3A1bI;cuI*)h%~Yp*EJz(`U3t*}1$DMYv;xZ)f? zM|2sOjb%c`FBv7}5*Cn1O@2o3tmotMYXOwEqJ6ZM$@ zwKaKgP~8ha|Q`FKxquVr^k>LmAs{qsz(0x^ivlnWIHKSfHDm;erE`QBas zT|%*|o7(4i($`A3sBmKw3G3`p4Ow}fk&etrwgvES^q}A#u~C{-sF*Ssmj}4H-a2|? zJrz5<|L!Qnaf!$CA-v^hYu+A{_tEn|T z`(f)A+~~(*iTqxV*^@!HEHwolyf+vz(_~Hl3KZ6;iGy`;Q3YyG!UC89KK}%4cn<2< z*S*N(UP2zO%KTOJ9CR(fI@lV0v`ZLQ_9PFc80^{6y73+1#p*t@s+DORsN@;ukeSzQ zH`$>Xpl34HA=IRhi{ToFbvSIa{@tOKgOeatvuQC;l?Co*~JR1u)+WW;|Rc zK23jUxUKo4*DaEvH_UAUL)~4L41#>c=1>(pX59mfJ^GI8__Gh?_RvknRn~*YdkQcP zt3k>h3T=p5FaLbJamlOehKYG=L;Dunw+@++{ZDPp&%^4gLu+H#r%P35C#yp|NkYhQ zYa2f~Inq{*2XZ+Px`QK46@9Mla9CM(EO+2zsZ7^gvcr;4d`Y6YH zz3j57jG3Yck`HqUYn8)|Y?CMl*!Mfbbg&$O0?ZLL`>=U^4^=>;-WZo<1w zLe4L)?1CJ>F}<$ayCTtylOZ=Ju}z*1EvoT<8-#j6rer*R4J0=0`FOUM_%*K;HyPI% zmbhV>UAx7{^P1M@nz;M~N}uy%75cZQf5k6q);OR_uHjT(VtWCBOt+55;j-#r8-~yu zHq_cU+CW9i;@2t50nZUlu;%rVA;c`!AT7r}$#?m2@#7CUcziD`$FaeqtBL8Zupg#L zZO^_lt+8`u&V80I(U516+NZfJ1PWPC{cP_u=hE~ktI4&0?nq>B&-%#sYa2Sd0``AP zMF{B<@_WM&6FWE7fLM!}=(7Pv*UX;^LN-RXzUd5Gu_`R?x4R z+g-&6!&dhvLW6yS`=xa;3u9E9C59Vx4u-}^vr$Tqnd&w8Ud5ecX$oUWG{;WN{dpMY zJvS_NVs7N&Iw4^CX0`I-p3loR!AgG zl!pog!j{OcPXzE=Fqj$JXXcHAsHQyt@xC!!SR@==U+LS!m0TzIj4#C9SDU7BRBr2# z`?+{F@K9>Ax$L>iIVf0n0W@dXqFUbICp=rz1GCsXxm6LyQ7#Woa#k=q8yGJayLzPdMG(f%T|VMWEbr zqb1FHBKF3TH8gJolI|;)qB9I zh0G)>SI5#%{ynd=q5duSbicq-Rra|p`)v~FNyC-}W{WUYwjXtJq>MVPj)B(hKcDqq ze-x;_BLmF%-aF#Wf40`oR1Qg!Jc1O-0c=hT3EUy3(GK9lYEB^N__6`626;DT!>BL~-Z@mftdHVzf@p2 zVlJc`SC$V$DogDhts~3~F~!CZx*U$fdtuBy6S3utv$er)zwV58^CRbw^?;_t+$P8oRYjhw zg#kC=6D}L#`kLSl0Nb|W0La4?l5ZD8TFR)zo@@q;1IOYRr2?`9q=S48qmpxGGRW2& zdJQY@xSVD!y3OCSoH!By3NccZFuuR<1&dML1UT@=@!T>c!GLJU*&_5bGX-~m@$`gj z2AEO-8(Gl}%m=`Pt2X#}r~6|S!1_PLF2jwJ%MO9+{1;N?L8lBW7Kr;&z(5Q$+Q^$B z>pAoxtG$rnW#lxRrnocY>qtaz^$tLjBJqqEeo#bmWj z{t^v70N3SPDQ>D+J2}Mis|`b>!;VMCLl0*sk-reTfD){W7cn={qP*2se}usTV-5MN z#axwoZHj%jQ9jDg^VgT1s>)GGfZ2XYhyowmNq;Vre6a-x&jJRvt?ciN+Q1;8gvNu( zlbcw;nlmgpK!zl8vKcXby6W0OWUzHV;HaWXLs6dp`V?*Bhhfm@Q+8?ZBTNUKj2M6q{jO%TI zCy|XpfUd-$akVJc@Y0wPd857(e6nVY?@#UB{{6!q>jePrhr!Pr?t24yB?0&c37){6 zN8@ne%H2XN6LM}5FvPBy04%`{mclZCn4^$Jz-Ik1V0gigfeeCx8u@ z2iY412ovfD>Z}j&Ms+ke`RT=|alzPI7FA6%0UxV)+-vM6ZAuSoPh3-h&izv$V8npD zdy75tyMT#38j1{c)~3`>7f`4UUF0 zb{?}iHi|2}BqIzo3|Nf;`S@rpp^16Q2brOJGVpKZ(WKSwAFNyEf7SsYH`=ce)!SCm zc5={}xe)KR*za1J$G7-nOdE!&Bt+eNCgr~&{3hZw7-JmZNN4d^z0`4lkDv==Q}k{u zAYEw+4U*P)n(sSeFlx9c?RawgcrTLEDATDZ zD7f>w&)$9m)5aR${^SD5zf)^;{D(`7A!s&7|1@;jAKu}$(DipF5IQp#Y&n^$(3bik z5xA|TWc*&`v;Sx7%oFdNY(J!wXWJ(zF8tL|GW&KCcxIh5y3E5KISJ(9{IOfy{*lO( z)alIgR|H-2rUh?)<%D%Wdr!CPx2>=M0`5J}D|cr?%O!Pgp3R2QAsX#_>{F>1|G0G( z161GX_Ra}$&hvZfH)g8$M32`?d-4ZP@{e|ar!DWFb$qSFlm8akgg_&39}mSK1>v8J z(t@uvAw`(Zv%Nk6#aYzq9p0}L?I*@&@?Gj(-_Ln`qsY$);Ci) zx5*NXlEz(Fj;W$iDb@$A%Fdip#Me=J(KYRQ!9}syuG+IfFPkAsavy&B=aQ?1@^dzo_sW# zxdW4*VC=fepPhX}0<)MUgehhvIdE=QjJk}j5+Nbu&Drv`T0z1I7NLPWJA`R*GE_@>b;Z% zyA?!c7}3Ao%63pc;DVSGtVMqqnE(`&(nnLR-!gJ&Wl?`(u)Y)+z7h4lDz3JqCM2PK zS+4IrpujyuNp-!)YjFs^r`ZuddXi>Tea?N1chCKMY26^FXq)15(#%-gP%0zp=lhFG zvK}vgEq@uCH>es8?l)CA!TSgTHQt~gf z+eK^s3cf0#h^^rYaZ5$I>$dgu`}x0yj5g_iD}fnE80PS{0;1-MQ>on^Y(|MSx2Ha+ zFv^gqnb0!OJbk#;J~_(;r0#jz?d5_SfCDqM3ZoXCm+d1PB8N{Zx`T5@)@<# zjdrb_TN~BR1HvB~GG&TQZK}Q8u`0K6u%)Y*5>JCzxQ4@Wt}xZixN1*qCc{ss>3WkH z?kIkJL*bX3YVBqD6>-0xTZpztiFzJ#5a8A=%%pk`x=iFH%C2wDIn6HpC7HcWt6UT0 zb09Y7~Eyl-%)l& zmQ=N`;K5ZX1h@t$c>xcmK;hS@J&I`tjIVNs3 z8TTsP1S9HGPd*&TVmw(IV5Xwf9<^lM6I`PgR1ScOhvuYFLI=U8G6gXdP!+Y}0BuR4 zmk1a?h#Wd*g7m43DfCay&9M}I!fR(OwQnw9%#@ijfnxBVPA);o3@(|2-U5P1@~F4f z)hiFWI^K6j|NM2!BJG34%kpPz$(EL{TAth@s?)_z0;-G0Ko}flc9+-|8|5hGS5}5N z&~3H%T+;Dg72tP?jrO}xd;L#8T{+`} zDAx~G-sLZWT$S>snU|G;t7=PWW)+g5Yp@E^GWlul`GcQlJN&T#QOXB8j%4=K9{XmF zI%<5GA4E|&xY+Rym=Q@&nCt?HggR-hjQ*V%fHQqwg*?I~qk&Aa%b>TR@FVu{ZGee= z4Acv|$5=NG#-GgH_;BAl5vH+X`DFVr))=!16L69 zWGH1E4GXhXX`}m`ZjWxSfEi3ew?eRq zt+(q3q<0F@n}7wu3oD4%-!#_P+mB8*oI~{XhnkUcutvfJMmr0X3B=8mjxrX-Eyh0U zap9Mz&;JZu2aP1qKG44%hxes+=Z8>adpMU|5XdtL0M-5e8k-?i;A!-Mpj>ot%q`kh zf#=BG^yvzK2p5Bnggt`na4x+>wXQ)t&zZcZOxs0W`xB`)4Y(+LyyLz(9tWHaF8gw( z=IrXHkyeXU{OPy9hiw8;_1jUufEQ_$(K>_+swH0Wa!dOgMzf`DeTo$?yssGs&ajK2 z#Sj|hP4;h3$><}mFVTV(O!JJrmWS)#$AS1bp2>(5VC|}BNFEE|#CI=DF?5V!ae&>{ zgDA>|Gx!{ahmrA`ugNd;<_zH4&PT3{_2vRfzSRe&Zt6P8z6aY~ZRig7rbnVYB_M13 z?_NNZim6rP5qI905-YMomdru>t{By&g_3S-48R3sE`uCrUOo&=m?wwe<;o2KgW!aa zq{@vnj~5X8d|2=W(P73@nNc_b=X@T}@XsVm>Vzi`f9$4D?$=$8JLMb*`JKPpFUU^` zsJUSQvodg~Myayr#F9%?%^1&rKNhIjoT)&BF zscyL?p#8TNzJOE)G%d8@sz5N^3yMf0SWOhp`*O~Z153;p@>A=0>5X% zI^d?UrLE7+KOs&5<(@7q`;ER)j4?;trSg{)Mc~baiXtTUg8aOVtq%&mUoLt!5R1z$ zlmL4tJ)UiR&<@0&lzlOuO>x;Dz{lU5zH=beeBaYrXj#VRQq9c0WJEBonbU}wVR9_BPpf6wIbN;@zS+e@ok>ZahT+uhZP5^PouBtWpl(<%P_?W*%(%}2FGiRn$z z9T8Q`%N&(#goZL;V%NHy!M;Q4;(=jzuH#LC4Zwm!_B9-+Rjcb0dJs+1rg>7T-ix!> zq~lr3AxGbtI4i|x3?$M#Ljk(_ylrFupRCJm!V!Qy1bdUI&Fo~<`>h*q z<8gI*{!qK^c`ZSY32O`Xw+C9w!hTL%YUPPO?i?W(0x#6UdTVcZMnwa4-N7>jHj`z9 z6J zoX-tf&f3XBa4R2V7cq{D7(8Q?9MK!2yRP}36|Ar;uZC+|cNsQT+qu8GUNj3;R~>(@ zN@9YHTt1v~W;+q8l6nzlLQz~fAB%jU;B%FoP!6-ofFZrdp2<{?r?wOA0Eoe`3!&KU zhre6eCnn@SJ~*ZDboz0Q_uODR3kq?9|1#yXt3i1Rmf?MVI^ET(&U$d|jl!J<1Ab1m zzY;gvc~j=;HvQNJRk|694WU^IORl%=6++#&Ts_8}qtmAkmnpsZZ&itZ3N_veS{oy> z7Tx;NpYjmW6>ocV=9`_iCjQ~vCbKl5z_AfF4rH3nlCaBtZ~fIwitk-m{OcC~L-#i- ztC)R^Hekn@!!Yk4)j*;tnVJV=@=rp8^S-SZxa$JV3%}alhT%VucGtZItJ8-|pmcWd z{v$C?hd}Z1X|`tw^kn!Q*zUaKutnGaXGe_xqBhPh?~ki^hjW)BLC7vT}v`J1vk=6Rs+UWVql!TCw}rP^ee zpLzp3@~vz;K;hEO53AW`dPuxx5*Glg%VOwIQ@%hu2Oq8t)dmZCu)XccQ)$0YcKna2 zhWb@{HZbiFT5-jSrU8dCl%E#f6`P^oe|(4E*3yr$LVSfVNxkY{v&GYW(; zxpBh6j!XS&QTGW**_A1UIcwF1kmb>Y@CO|qVyOa57lnrb87qc{E|o}ol?E1WwaYM< zJSdM*fjht8?`kHV2NQwZ6b^e`zAk3X9D!1f`7{39Uyp}f-TZcWzwuA0TN8Ol*i1|L zLB(rgU4kgRE45^Qj0gW@1QCcleF~L_l>y2TC}b*ze-%cM{?N88yQlfHQE2DU{--94 z8SM;p0Zfqzf8<|HUL}Bj5w(zKN3N(_)wlR=qza>f_RRWAK=%^ey2p6HFYg}sv9tGG zUYcXB5G??5H^Es1)HNxe+n4VN(}(QD)3FePilra|uIXaZKkpf<*CAf8JV^T0I?TC~ zuEwY`Fu7Nbkg5a^>J=Tq!+L+5(Py)Lr5pCkca&cV(Z!fH=Ywuf-UPOZ3vBs=gXZu# z`uCo|!4LYtEFjNpZhE^2KSlrgs7FYS+p}%w8pRgL(8GC>y4O_e5qftL)bBG_|NfcS z^yt;s5PeRL$UJ>+9b^q^8;Z}~jI{rW9 zi@>{b4IuY$Mca;VW`07;GzhqZo?mSV$HJBHLH=}BVO>E3XHOIAQ%A!q+oQ*Z2Bx3g zZ8v{!8Psq~3IdM~)&t`P{#g$e4dMb$zVSg9%uz~W`sZmSY`NsmG&#@di>o>0yKs1R zRkuM!;g1&CX>#j@YBzseTkOX!>U|E@lF1#0W>1#zTJ(Du_jb1)1Pp!jc~9 z+A#tzIQIlo9w|>vJkwTVOMW?ViSnw=;s-ozCkwr2?x9D|hQg#dl%KG2yp4~RiyeH_ zFgjPb`Qzi)8HnC~pO&vMx03J3m2yE=#U2m5iQYowtn};`uZ4wr%779#=kc9h z5udgb&e}UQd0{dc*Hv~48@$Tyh<6oGQa>Psb&IXYmk#$rJz%HLqX^+|#1&!wt5=IZ z$Zs2*TXdNE`|GvvcB&tsf93fC6Q@jKHk?Wmq}cyeM%I6qE~FvdWJx9NrM z@d^9$-M{}Q9X0Kob#5&7b_%o!a&MzXNQvELg2pj(VGJ`hNawt)h25juGu)(^?p{~@ zR5~$m9Irs=4Gl~sRu~K|d`y^7-gD+F`nfWwMcb$Gl%33=_9x77C)p|>`-P#_(b6~0 zWXI$DO!C%O^j5y%^Z)J#0}_?Yl}bbSJ1t>znOk`vJU2!_2 zbOmRo-!T31R+?l@zg!Nq=R!Gi6idHGJOO$*2M#e-r#hfkn&yACK1O7Amg=KyS$p{A zzLpmF|2cO>qUgpM;=0KIaNb_-coXH|KqGKXKQEmB=Vft?4T)U&%b$d=ao7>TMoe2k z&AVgr0AzPsdMmT;pJEqoqo423v86DiajVNdVHq%0vZC#%Nqbm2^X++hds; z#F;xlOnIzh6xvA|$V@+jrH9;IeHuJXL(pqa-Jy{9bbZdHJ_+L-1wbKwL!YKFM)Z17 z0*AVWi#w8(_{YbP?G9V$Z<*KV0KuI7h}KH65RpvUFEl@?7}Sa7l;D|XZ+zJtKf*X{ zb5ovWu&UIEek2N*sKJ`G_aATsgxkuyaX|Xk4O3)YDGX-|>DhFM$y*Vo2Q^2^1`M-2`KtGWmO=kmwfJsQ6OHsuN}VgE_Z>mC;7nbuFM6? zVs}8UL&M_K?gL{|`9u$j6;1*2S?5^PyZ@3Iw(F4&5uI;t4{jR80(6VX$vTQ+*>GDV zT5>V(h9q{Yw%b_AJ{;u`nd>j$$OhS6QJ6OF5=UjHfKUzM8gd;Fq}oX4Pf`0!Nm>j} z-s4dy-mRWdfMIn3hU8*SpRbY{huWV3nW++5P=bH&rz)P=naJ6~DR`)>;M_XM9T9YN zvbr-I;Wm46+k^7u8D;JC42OU-5xg}mN)hyJ4r=XW{3q+I2FpW+MGrs*BxSzQFlv;y zDz+G?d(1A7e?yA^^zxx+Kl@60Hy~ad#|rk4hM^t&p z$n|-D<^~>ycGg0;NvyuJk!;jB(!NDC0WCa>EMB`$Otkkmb^lugYJ^8 z$Kd-jYZkgZwd=rGqS7TJC?Rp@Oj!A~mgRf%!^a*Y(A#1tzj=|r1DDTjuE4ly1`h^Z zxClMwKd&pM(zBP3SgoY>9*7?cjtWY}>vU$e2zp(mmV(*#-f3N@kH`&Sd}GzSs;Nf6 zU~RZVU%1z@+nGBURkJUC%Y z0U;NM-3%08?v))CO0-LPf0XaJ2BA2{_k$f*Zzu}CKc+q#9>(#fFuA z6np>o`Yb4?!*r5!QS1HU+GAEK6C3LZ3pP-WS3RX6S~AjzR(^s8pIWXi-ePQWo0>jT+c?bK}Z&=MXbqH(Ad!A1V7Xq=gHZQ z6g8oExYzRG*AWP3xKKyXS}qIM$p@g5j@lSg#VW~ zz0vE6c0e#)B0Btr$D{Z98ulYs7~n5cy?!vp)69P`zpyOs-BIY3`W+R|9JVC)F<&{3 zHS`m36oiaiRG+_W>CU~!f#J!J8-5H%*Pzqjk_$IsoW{z((8g)3(EJ(C0g;__Q~RTD zxvrZWq^>BoIxwlX*z0eIblpW#w8?ecLVDS#CNv8LN@ZfCg?bxA7U3_V6r=Z#)!um!T_(gPAK5Dimy@)hha|z*WbLjoqHx(f{vD8H~>cz~%bG*~E&E=tZ44ReRmpfzd&KON7-VJ1nk zteS1U{Z>94(TZIUyFOE}&SJ-JuRXVX{|cN;dheCu$sn+iWLjYOM?#33V0O*R|M9U1 z9MM3RT(C=G!q!VZz`hk9BQMu0cXB~KG#^NSLo;ufhtwbQJ~(;{Yl@bnxKLBc9D7MW z=%LTZRUy0TxWy?~P6&a>!)sa%Z6$^H&A3-T;?lk&RVo+l>9@4P1ofX#Bf~|L9k%j4 z=>&Egm&u0OAzVN(byprm>_Br_i~JNT)VcTC+7l28zFvJ5_t2Zo{ZjLDY&kTnlPzT-u(p8dkSkt~f%E&q!&$kK+=7)NyQT@JAf(2pVp`_J9c z@on61>5Y~IgbU0)&a5f!3RDDHmz@2Xy01IAbT>Gziym7G>asHwLQCH+Qew9KI=hoj5tt z0+|Jw4C-HtjKGqv7g_9%CwB-miG`mx6%D8FpefSi2e}hy!XJi?iD+=L67+ai=x11 z9_}dfF!5v8eZrZyzdH{baQme!1uuQ9+y7bz%EGOr=PMSRezwuEdUM7Q<+HjmqOE#v@<*xV!68@H?goL|xosqtG_dzl#FeRM7$n(*n_w^sYyFYJDzh!dbO;=d*#}A0b~{3s2-tdj;^jfcipI{`e3^O~jE_*4w#i_QBLWRBQ6P>Jt?8ka}iqy)n2e znxu&dAL?pVJQH~Htj~R)0{@ucg@VpXrIP~F7t3r8PG?hea)>ty(N^D$C5yrApjHi)5BOuuLEB5D?)uIPQ=s_8MzRC5~0)viAHHy2+3Ym%A)UnbqiBP{XO)m}FoQAanr{J|#I zRLm^HQ%n9&7+WMo+&zmlQA3~HFJX1 z#3)kqV543bR2{Z`7)S3joBf@?YR`}2hhh7S>o+E){$QH$TSxfXjJx1{!Te%J`32X- z*Kd?@+J9~t8iL*eHp2{$Q{BYaY@5?7z&)1kg^_$%wx6E+Rjy^p!+yrUTwc{U2^CdI z8g%6Y!F#`1J1tng)H~Lfi2|K|Iu% zVzpy+@Plr>b@+GA*7gJRJ$o^22VG;!6<*E$&}d{GgIi@#Hxw~gR7hl~Ef_fA^b)vd z;CG;P^JX`%+o0V8NSRTDK&vu~L?@e%M4*?b8f9D>4u>{32H&jwLRf%2NP?OadS7E*JI+jo~OS zwvOYU>^)8GsAu6Zckk?Oto)br&)P`VL50>I*(OGNftYxUS0%TT_;~jg0cYjJGdDm) zB9QR(vyp9lYew|4e9jQP9enV7I34JxrSIs9sk>k<(j$Mw^}i={ayw@lXcB07OGM!a z1%AucBg_=Se~mYFowMGYQ*Y2ZbU*A+-%}vGJpz6`ym2y_=5`fXuvD90AP?|iYvufF zG5^%uD_lXKeT(K`Q|I#HTea7pm@iJ-gEq{qy#5BEX?`E@gT4WcdL^62INn|}G-!yP zZLF71uQ;{cFrO`5YO=ZHWd`i#IDxgbdUg&YkHbUPniBny00&=1W5NT3g=TyE~}@s zU|H=J_j!0nQ7sSkPHSIPQ)oS}@VWqJt=5#Sh!M zbSV>=Dvi-py6HCgbVTo{D(RR;MHd=mV?dL#`#dsDO~%vVaYon~3m#)8<3g*D^9t#z zZP}_q?8LW|PRm5l>WZg4J7Fep!N6m;U1`xRJ!$gT3Po4`$t@%6{?urF`U+h^L?obf zhX05>zV$ICNl!nR{-(4jB9?(Jm?IRyUwYYb;$r80nF{sxxaW=_es&Fb#6?8rBy50^e^eiq!R7xf%8ye#R;=2~4Yp^p8P;lrg#^UTAY$a1BC zf>Yj8NuK-lm&dZwy2G_b`RG6){i{vOmVNz-3Hnj@dg}~iARg!&JjF#6;bpx@VxEaHmn)1?eY%a7T~ zj*Xtb*cRTRT{8Tl#!z_wM{#HNw1%F;&xWw>y$RJsI;GSgT9nD>z2Jc&8}<+$lxyTK4w;uXPy8odeCZiU9nj-5|Sh7;X0hTk3E zh4C1HUn+k$@U>5Y(GUNIAwPMJyz2b*S3w*E>1cF)x92Tj@KU#UPoprazHR1FUOd?E zWl1@?ttm zVB*7>5y%pcOSQ2~JTmI8RMQ`&Yc{H@s`v-(48wQ{vUHwOYG zG6yhHxAD~`^meNP`qFPdnuJZHgL6d-an1!|P z1%}$C5?$_l+Sj-S6s`rJ$g{5&rX`GYWS8B3kuB?vvyU-1`rJOC0JKqek;8V%lSUrG zzcuGds9$40U~p1^bytWFwp8B^ttF=w2Z6WwduBFa(pCPe4~~$E`X&+4QxNHemrsCf2csdMee*`O+j-gGsOw8%#v z43;1~D2o$35{hmvd02iy)c`VbC}Ie44Bq8%O+(eniDW&RYme6vq&gM*_NDFSj0702 zCHk6GkQ)|qO)a*^MJ8oiNi~cmLYaLEz%nIg2 z%yXU@OT{YrYL)6G6%JxWj-0tV13Wu@VDKAxSy|LlodL&*SS0TvMTLnrB)Hqw@-wS@ z&al1AO+onH;Q0clf5U|qdF2SDTf1vO2kAeo^grM3KUj88!@n`wiFMRH(083@04c5<=>OXP3#fMLf_CwR}~A?&gr z_}8sO`72`9tS^Dvf9Yp*Ar_q0!r5RB}F7&B_x<7b0lC?meo*a z3Gw-O-C_a^K71VYA6lRCh@AcJPTrF}JmC5A5VxNnvRMvLZ8#D%45S^qW=ob)Q`1HDf`yTai{}~){e7vgyn`*@O~x^ z7=Gt(kh5KYyLkVl2Z|z8@h`1Fo@t~1KM>_9!cEhJaTRr)Y$FVH;^M^)0mape^*giv z-l5((xcFM-$DSyH6=5^Pj3GUJrTy?kmH%LAesF9ozw;^vIrr4B86C7y+g-uqc^ulE z@Eoa9;y&tX?&r2y1+a4`)za9+I()fqLYwkYWAll**?5(t_6Pav`oNbP!2x}Dgy_S} zpIavf&&M{X2Lx`@paaH;8J?E0$0wc$%1euc7RyCpt0kCfLB^yzIVY&G)nU5akLx9EEbJgQ*q47~; z@a&U0Ixjpxlx@Tf1J-SYryy^zyWZ*T^pA1^R;H@=e|z_Jq>2ik?@x_!V3KYbO2(Xf zO2_bdj1aEkFa48|ZGRm?ZIzf+sCakTqU>m*c^Pq<6#?o{iL7p(I~tFXU@1#b+3gu( zUIzSf%Pn9hTYu992kBRRi^075S_NQkpDKYI`=($3`WCCN*J)ydj3OFOjuOjoTgU(S z0O?+ug3h>>{v8|w;Lhe~HM09>5mmJ5)|0Kalc#cC`*lwU`@GgigkC~cOGQBUfSFpyVAlFDM=5ao`goaXqCdP+ zX%_p3x=gwL5xh8kJe0rCj5_g<^IesckZu8_QZk7^^ub>iufLtC1SAI~>stX8z@2p+ zex0C1T1@@_nH&lu&g2mm5APy_KnHTR-yXHb?nfm+B;CXUVelaw;fB$*<>An^Aj{$fIbnE`J^w!}PFIjPAYG$NKn0|`MOt7q3P_hUQ$kWYL|{WjIt1xdx;w^z@!tFX zzW$!)ef$fL19ThPb>G)@o#*G2##HsZA-OVs#R-q@5dRrahokH2O)n|_*4F+a+)WC4 zB__`&p5Pfj;m&#~2X*EU<|6F9l!e6@6qn?rH>j;-8o@7YjFo1ve+;6(u`eLJn=w-r94yD*KZ2V5dwSiSiKb(ZQW7<*i(`itIJ{XuL z+=*b3IRX0cIh<0K0Ms8yWAG92$2bO&a^F9%S4y#PeB-$piX{$TIVEWC@(2~q*%fLc&{s_AFHji zUviQ|pA<)V@qQQDN_gjAc0o$`2}F;K0Bs$^Y111kRJ#bmUUe~XLf&>BAlZtClK>mxikjh9@Jy& znOG>Soav7N@lMbDwoiYpV?YGiSayQ+gNP8S!!Bc?&J^T1V9z<=~;oF za;Ej@53MZu+6wUQluiN!v{;+&M{C_<$+i5XCYZY%>UuSGRr_Uz@Vq4<<4c)Z(;Qp4 zBxl6)0UWe7(CfrVv7y~pe||`S!;<5oyT7P#Ckk|gBGFGS#(SrizBr{rjN^qv1fOf%@&WE^bt9fDZibcRE0LRv3H%E{@nUA8oCTrw)AGv;pSvX7(!NK|D%x+ppv0s zz45c_J4WhkQ+KQ7L~8H-G^`WTeEY%IJ62i4$KCI&Q;k8z_5X?g=VN}~;s0NE>;4xF zAAcN7{qc&n5#{RQd)DRh5?G|8D)nc{U^~vBD*5}SsBg5Tpvy*}fe4cHrT>T5H02<{ z>Bl>1c_Py`?l$Y!< zOB%?SQiGa++Nlbnq^m&kOh+BK>wmfv_oEbKYH{!Herau7M*ALN!R%W=2wO!)QNTX* z!=L?U;BM%3G3Yd-d#`~pAoiPcSmY=jLuQeO1((IknLCCfD%{!BUgXz7J#d6(wrp>m z&0Pj~f?+ZgG3u(lOLWUio)W<6=Lg#X51;>70c@O2j#4UmpZQ;Gxa(o1yz$JWl-ch6 zqT2Om5n`%%McxD8u+{lgH#(U>YseZnY%DoE=zVOpJ!KYpJ($%)|zj@{`KRul%fGMpM40=9f*yi|r+ljy9 zJeNP2cOPa0f|H^UCII^%(k%0;SNaln1SqfRefIQRs8IdhH0$eQ*7Lh514Q*-5SdT! ze5!RDH~Xa_5_5cHeQ#MFMm`Zr$1L&c<>u1_HsH^K)d3S7esI1NnsU%*F+oYegNpJL zM5Vli)oMQ}5S!%S@XV*Wcxd;Z|0~f z9;Jo436~@0mAcqV-}|3Isu+ixj^F91fo(QZz{>9EcvYPD_EQWNpk878RJc0Yw35v2 z9XyWd44Udp78~#w;nUh|&R7|PjCkTH=c~3M#kq>LTpzi9j*Y3!%Q=aSbsK-t8P9ep z?RUaduJ?2+YA$eRC&zueMJaCORjh?9^ol~&WBibzf+(Vpq0%7=a&ozZS-{(s;~VlL zCimUZktZ_Fkf9`ph8<18tLR*wjRtsECvAo$issl#oXzY!cYMd+ad+b^+Y-@o61Ra0 zL$-H-At?KHQn+V>@QtpIfm5GtnPyD_Y5y8ibv7dZz3ldm(aXV{Yx?S|)NTYV{P-2? z3<=FHJk?p1Qu-XRn}OQDPz`#*p=81Y$I%DpnEp=Gy7mY*RS4dahWBn-ko^68Qh7jl zFvJ7m4La#&Ts}8$FA;wr>i>R{F))10Ad%}(`RgB~z}krLUdsBaJZIY6Miz=8QCACQ zKoT(|`wg-e;~(_0wYJK;UlhqaN9Dto0KH;x2lA6bHrJ>VsAqNVRkbB*)3hF7y#QeZ z-)v%pMx&2He8?Md^|`^YUquMZ;|?Qw>HkY3P#0JL_$;%lK@cASTWBRDmje-R)i&so zi-_>Ny4zG($emMDKjBdN$)f);5_l-VCs1#tXc_cStam}&RBXStA6B^U32btTV$1eN zc;D_67QMUmPM@8KZ0-39Y|GI!iYOTj7|Ou0*C<^%F&Ba_A4Z-qi%sr=(U>owWP@Oe zt6gJXdQNh6;?pkoSl}^43`;s4pvv8s;;&Q32J_UWon|elmr<2#}hWPlZC-5OEiFHR1$w9xQ&hg|uR9Itc zh*urGO;ikC@U>8(1Ngk1Z2f{e`2N_3-s9#Stgj|il1IRtba-mnvld*#SSYtx`G7)B zwT<_oq*tX!$x!dnohM&51jO7tu-=I<>8f-L=Vbq7tbjsDwp({`_6gbXVWSEx z+qFI2oKM(X*V>%HYvjdH=aYEep7io`O(f@OWmgx5@ zI`K9j&_p|c1NhT3AX$beZ{EC@U@AnX8&Sb~rwP&oVzWj{fEb3|YpUTXHRL_;SIusY zJ5)xNeAWEN(O#(6L}3aEXrry9MM>;oin$TJ7t1K1R~^wOD??%kWC@0~=?4h3gR|H= znJpiNeD`b{^F|zGYqq5-mjUDG?eZyByk4&TO0Nf_9510)aE6H=80M47-e)tVF?sa| zvPWU6;z5Bl4a)?oJB&48xobpuKZpTsn^#q!llTt9va`JbLvjgHKFlpuC4XAmzKAxz=Oo^l=stAF7#$*dC2sZLpEj~96K6mgHTN3r(U|Dg)6 zKX5=^e%THzsh>Ia9(*xN1toeL`-^sD;$l*Tki6Y=L&Gj8ybvImi%o?kLZtIdwedu-|9%q8>>Jh*8#krkyo3 z`p#P8pRfk-PTu-BO7!<#Tp<{Sayx&^@uHcLlCR9`c$uUz)rBuDmfbar!tBkf4_Uy1 zfcMC)g7&4wO6^Q;o3=2B#BgCOB!b18HeIX90L)smlI@@rP|di5_|4P|U4SoQJY_oI z#e@I#md$L_V|HJ)<4=%o6t$ke0Hu7X=9EFB)v1F>RXUPn@tQ0wa#fC9Ux~Guktz*I z>;W|PKX&|a^As%E!Xu7kF8quKI)o?|A@=O+SNoKN1|F^u!j6409-(p#9jFhE@q|{5#}>FNs*kpqXrlWT1vT zZA=|K+JvLrB(nX;Y25pS^q_PU_L0DQ6>3$bsZF?qHR*;tWK~Y?qA(p&G%Jd-+&w2v z&by%${itd|`keFP$6(~LN63TIZ?p(+}?OB z>~}cWk$Z2vI<{Y|RBW#;X}+fR(;!r!IP(juF}Z2Z46sI9GHsfSQRHj_QBP)d%W4TY zjszxUZWiYFKF`lHnp^~?v)G+FV~u4aD!2ldJ=t2#4%h@L1yHZq8;7Chzvn0gs197J zv{_%$P9!I!;x~T0o+3_MxXG|DSH9+Ga6ce2S~1N>F+Im;d4e@(Vq`LUpj6uM>`n$L z{|2-3THwH)hI7V?$njCa^vg*o~nrqhLG>eZN5IFMWPfKJ+X}6XCNxZi4B1f-a8qB_S~3v+%Lg2ZhV$@ z-Z5f!`d=3dPDH$NfH)y}mNt)nM=`c5+f|3Pnj}ioMp;YXyx-2QcyYCIT+Dp9 zNE^pYJV0~!O(XN(l1Lit9dcuoBA|if!S=Y!;C#1r%x;VejH8>xjCSeu@7*kpcOc_( ziuD`$jR3-{2IdGTWa&)?U_msacP=q-R$DM4*;gJwhp>Ki`dOuz0M`)t{^qD{Qoc5R zPD_qj*_7$Le+YEdEXY4$)7?DxwYAt&7{>}F*6*wGbcFy1pos7>WHf>N@+|ODZHIC1 z2~>s65Bh;u@Z7wua zjL%HsQ&?vwwvAN;9F;~$u!HnjXGZ^+yDD(U+va#k%XYna=oCz#eUb4x%*Vowm$i>dbca+(Fj>}mkOHlCTCPL}h;KNtU+sA5yh4=jQ zV%vt_5eXtb-b+A?_H8ET+muwJUx1E(PhyBXvJsmgM4#W6dWx)G&y)`z9kh<%r}%)* z_I*1k@1PPl@QP4IxslfVwVitmAJ18ep2-dCVff)4XV#nQQ{8F?sOmmtr@ZZ08IR!) z9tZmBQtN>idd#s}R)$9kzeT@2KOO&}T_DAwt*}U+dRSO{tEoDBZsu3H6PwGRm9m6X zuXjwUlTCBH7WiZ5cVYY&_M-Z!@n6Nfpq_1~cKEQ`hdE=2I!-jXF$iP5+{Tf)$~v#l z%m;@+lxRfTJIihO(o6z0uC?ZaU$h9$lUJiBK`g92z}imBH!$NxFyPQVD@=8xvOj&~#!8EXC5-7-V$B(lbP20jbVim}oL7tl{ z=RLDY7-6qsa^yq3aG2t7%tM82Dx~GDt7`aSxvX&o@-Weim!KJr<6-Y8!Z|O%nN~_J z1C6z;6y(Q6IgQz@3v1*eH=`^tS>d90kO*gqN~6#uc3u9RRsOY~{*6oT z;%pPtBLwn;VD}NIdHs`!e!22T-?TD18N3dgOjYby4TrxD2E_71rHa%v7?~J4PSMT} zIy7EXcX7EU*Pbh2oIp1^C>$Z`=`NAiY-EY&{9a;6HXeP~c`@3nXO(V)+@7r(8p`H0 zeb$N>80)FZEZZ4N&&zdNY0((7O&#b|GAAPcfOUjCBUS}ES#>?cF6C(ebArz1`-{4+ zE3`&*gA$|hIaL9Rg5afEAe8$2B)$bNJ3OgYHiw-m;c0FRY(V6EmTE{<6#8pVM0oG6 zcolqsd?~V0p51`3hOat&jt#fw(kIqf!4$12m1^W>@5CujS(%CaXB>x(NsweJt(bK1$*jt&o@ z{^a?}()Fo19288J`X?Qw-!R=Jq)$vK_A(OOCa)y)9q-HDl}7!RXKHfr8}uNm0v=4M z&Y#EVroWq=XMB^>!3lCw#*hKlb9V5f zCVQu>g!r%yIp7F-L$6O;BCnt$W`+M;TYiXC0Kl5LdnUFT&yk{-70<1BrLww>1pDR5 z4(~pXYE0)9RtY)w9vO01_HF2^#*UWsXHru}rPlOCu#D6CG3&mgw1FYjZ){!f)VX&Y z%NMGnBbcd?C`6NAsN*D z`jiBvK!woMx4Pq-Bc7-ATj`fgqnb5q$aKYz@UgNwZxABLnz`TPb#tq_cEodl%`Z6* zxsqvZ9g11cvbJ-mxrRw45SX@IV>O}}I?c4v2y}H>R+u_c@mut#AGx((x)yk9R8xi# z-bnDVVahmitXCN2-THZ7_rI%@5+Ufno0R2^Yk+pvkoxO|*w3@Knwixu0KaPNcy^sx zTWEakAGfBN9fK7-7jg6g^jepjrgLu8u5*VUZ^y~P>J4wM-?dPGsFYNc!R(B;sGFDM z_rh&#@}#ck{R?(Ca+ouvh2ok_PiBdF?(g&1l!c27F}KAL*=|*L)wL2h$J(gTfgZ&L z*6y#+rbBttL)9K;^hA<8izp}hmE)YCkQn(H=|>xr?yWSrjvL@u{$)xqXpvi;!D;gM zsjP;77#03|*)0{^Ec%|ya!;rWTw5o>9`iTF)6neyd~dc_wgL2Kcrm@kFlV5YLxwdq zAo3L2Af9LqEY3xvPxIHgUr*Eb`qp;M{arW>lBxZ>vt)@4=#ByivU*%f6M#cveNOrN zj@V0UK@w_`l&_Vh*|Vjg@BaIc{(d;XIS`lg%^#@J0I2+*S5kdc>jxPZ^=VrV;1{$( z4Xq}v&QT&IV4jA!IC|KF+M^;6YWY8yFDycVMbhVZ0{QooRt=sUO#(nP!vFOLpi?C@ zU#OzNSh<1a-v0o?z)xz`{{11u|M?*nCE14;2k=X$K*=Hjz*Aa(O>0tw0lrFgZ7_B? zk_-6X3^A*Be{BuDPZzeX-9msJP>uuk>g1uJf?cVsH?DBq0@>F~DeEA-X#;N(_@s6B z@Ddape75TJN)26F>U_bfbShO0U?$eE;$mK%#RVSVg*EX!p0MpOT0fv(x(Ix>0H6He zYq=@b@+E-QxZ>tzo8pMy<*R~&h*O5WqaiiEr+f&M1?)ws<7uO0VPNtiun&LI1Ua9< z6D3isIDoEoybktc&ZT!O#aXlH1oFn)Qs2ruzm!|_GSdNz0<>#ws zIam)iS?H~Z5x`W*y(#KubvC4=2!6{WCUYqF^HF&}=Q7jtql&<6o4L%J^kC0>gj$Ge zK<3y`^!b2w8T8aP|6z|mxL_UnxU6z*`T%s-Q|zh*{4;V+_@6H^XMSV*6?J?Xb;?nJ z8s$s`Zd!)XmhBbTRjI#y>;AZ9+>bLu-R2egF)r@qWbPa~@h*MUL-_RLJrEYD2htNW z7kzcx<5*0wi&EFQ@LTKlFMjHM={nm%>Pdn%pLrn$_LQk^s&`t#qMSb3^|_xx_%*MI zz8ds^%p#b(T!Z_e{)GEotQXjk1oSn`g1~D#mcfmgwnw?=F#F2aaW#V4!W)FS&E-ej z=cfru=)e>BP5w(j*YdA=lTN|ze|*3&w##-AERB6Dir;B-?O{Lq^F$G3&UUs^z@ zHzHahG^$^7(4uhs_D=be3si@6IoPEj`%k#`Q}i$fEUjkGZiJ1=k=!!^8_IZxSs$bx zm`|4*({6z?MCw0E9jRCgDXINVj#7~6UMdELf7~Rw@h5YB4H>)RAy1EnX?>5>{Eyc7 zp1DW@RP^@LfJcsOD}*JO2Fr6}*}qePQ*3<3LuKkzyVsuSk*!3?4nC4LM-{-!-Qt)E ze*%0M79(*1hk00(N|fUyKxuf{N%FtM0in!-mxGzha#)&(HfRIRM?kjh&#D#B%sj~k zfT~Z0QW@V6(&ykEc9;GxHm?@M{~i{~M)WF+UY;M6f>q(Ci=!Z`#ZJc(5Xj1l{tmXY zTx%qq<{LBVRaK54@}P>}xe`CYd$a00?q_&{+pLdMpf3zeXp#u=U*UZLqx%!7wywjs zRoh_xXdfs!lG~C(w&w{Z`bVSolQ=pH7vRw{L)-&(=G@1FoKh{Kr2%E)|h?WD0e<5Q>W^#%mfFk51)}8Pfz@WLd0SlbG z9VWoy`ZI@yAFsttd-{VOEOS}vCw2Y+C|l!QJz3W0oLEetCs+x#+xlyYo>BR)hLC*p z7u3*=7i|B~MbU|OL)alz4Gq@WN@aLQ_6q|0Z!RVlz?`=j45_$$Y!u&~W1$}DaxeqD z9N+cYPxO-^Ws<1hq2f?`5&mbD8^ca7cmxixW>p|Z5IgXELv9KlhM&-slWEB|qG@)p zW=QqUQ{-u%%T&W_{l|Q@KjlV%kKGUpo5|j}M>J+&JEKp1Iy*mnzZ7IM=XxjjJ>__9 zV2Ir;y!yjoVgb+>{g95y|F1%}${%ckB4>O7QhLisC3agQZmzqeO3rV@3`&=m6t1j} zRGf_1E|<;vZf1@&xkyj>5ZGCjgR65CUK&0RWY2$x?>!!ypiOTY3V9tCIgd@w@vG3d z`E}7`P9zT>@-ax=9$OHySLvW3m%)a*iRySfFTr}|2oa`P7UijkiL2BF6FX;tH8OI? zZr2VbmPL}6R49-~-SG@CEVGqq>DV*`__vfkrU}2-G{9k+18R%!%#C^8psvRqC)W%8 z0?j8a1zyl{S>DEEcy{59VV!=^pca$4iuoR{LKNyoWT5G_#U5|2(Ni94`JC_ZgJB}^ z+p_P*-Xor@<%YIjq_4b^I+>m;0aHi{uPrS|`#E_8v0cx@EtwT*Vji`;HQH^EH8qxZ z*RlL`W7c!4yTyxHa%7MM9gKEVTa>TiV~xZqC5}E=!pc+{7?a9pP_X0RliAXP)L6{2 z!GP71NWbBRHPfMs1(uU_RPN=eqF{;qMJ7HYQl(XI5?0xd+z#Aq71of+(SqzK=#X!< zYw5yva%w*ebPsZ2hDf{de`6zM7yO*l7SbHod5~VzX+E%Nte<&OJbvf0_ zj~o)o-0I{5(We8kag45Q39=9l_GqmcXd{p>F^M01!?wWrIn~v7C>7F^H8N&8Sx;_; zQs8e>@di()P1DI5={0y6R^5`et0!a0cwi1gDIT-=?8yuFB32FBQfenomQuOj{Q)mX{Y>vtq2Ey)yrPL3AMBCIl&39ZtgzDlW5Zk z8XseL1F>R8dDKH}zc&BHqecogW>lzEtmTf_1>W1ZRq_s}r)SGhWcTsA2IV`63;yC3 z%C$|dvKPshF(O=tvnN)l7+d$UKCjN|;Uk7$^xpn)zh}+E=ai)E3Ej(F^=D8oKPGr7 zVq)m{b9LjEKPOsl91L6*)QmnLaOAc$d#8YW>?LLSy>2DLfBeYkJDqp@3UA#`+}gs} zX6k`fcN?tzHCaA9lKn7nRCEd41-6_zyBhNw-(~0{SHzEWxi1u(8djbjc=s1>!u{{| zBsh1D88vvRG}UjUvbO+cp%QwH;pmaT$G8gWbnJ16y%_hyvf0{Ffd?@~Z~OcoYAVUy zO0yl#*%Q<|aJF*LFytbF6nJUQk?4^MJg6a1rdSM};4Z!{=!DweM+NbM0}HlijJ0n0 zgK`9Jq%g)GBrhpV!S%_d4v3xbaR`D=LT29qrzQyYr3;E>>WQszZOe@y;#&f9Ne7ns z9yL)`K)*T*fbyR@o(NZcwMLvSN7*d{tPUM*{`C(WTG!e4Fdn+e%diOH_pGk`a(TFHkrE92vp^y1;t^^iemssqDD7MV@|3QXvd9PMPTsg= z`z#wUwX#6&uj9QcD(`-L68d2;TIvdU(_*69w{md!D?7z^2Yh{lImxP*+HDP;4}8BX zx{}3kdcLM*tA_>7O~V&)UYba?ny%f{VkGGvD=K3Aoq6`@tf1i%Ll`!PGjkX?c$fI< zKCNXEzpBATXAYhm+_%J}!&myTlzlP78864$n&|z_&Nt;#fBG8$foo37ZF&ie^QjUn zGk5NadPOx3NM`X&ET;9M+D7EjDuJeo%i{xJ5F<8bQ~~2 z+8yst`@2UAHHM|X^`mT?EyxbSs(yi?_K=hmUUUkv@_mhyTM>@xiTXbUZ4I#c_!wGXDCm)hCN5}?pdPW6B`0hOo zOwxbAtQvLiNO#8w`Y^m2WkNrpjQq{A+39j-HO{6&zbB+ZO*VjLt($Y_JTk8Xzyj;XvMmkL#v zO;!oL#M$Qqs=tVDx3J=jXqX3-tQKJ4{G%TSu` z1L)BgX|$%Hik7tCa?8=PmPW$#ul4P?&ZT`z93{6^Xae*rE{>!23^N@<8N>U*NVK2P zYRerg#@of9iyttzP2KS`J45M6^6l^MPQ21ib=jeFOj!Ynt4PraeE^<28V`ak+irgE zh~8=+G|KxLI;_N$JofqZOZME3=?E=pnFz*(2fLxGiZYr9c#eYiwd7M_Nq7SN8Sj!?^5@s7+ zwvvdqAnMt7bl=og_b7>w!N8?+>ZHBP=w$DP=fc=|8WWFr#O+@@C_&`AhP@~)W%-%u zv6#;4fPuZfgy~5o(Yo-RPlVNcH~vper)}7(pDI5<($)cD7;<2{_~WZ|drDJK37<3i ze+Hk^cDZ9f)QB12vLLzh{&~XlyEK9ON$@8?3FXwnhfC$nCj>Ywb^3IM624#8w3?XY z|JL^c4OoFM4uKJydPY-jVG3%WJw_dm6gM>pvTT1KvI~KXav_*uyB`3h_)>Axi%W*G zm6iNY(_D4~9=-};G2SC`&#+gJ=LuLi8c}@oRT*WRGNO2%IE?OnM=E=cfp|`zEb<^6 zR|54XnjO4Ja~Q80pt5~O%#IsTa4pF41m|AOHRVS9;r!QWI5aqx!6KbET1g-_K!g`{ zCOmT}BHN7fC}?+xfTzi2xM(9J?52R#AVg_jBqoSo8F}tc`1?^cm+T9(O#q*zh7*75 zzijj~Z472${|>-fJI(Hdz4L^y5I)EyPV^*6KTc)$%=!@ED&LzIOe9974QOpEbD2$w zAVu{$G;{F)_;~=Dtsrf`gWs4%UO8ct`C|o+@t80%be_aH!zR=9yC%qtiLRXcLc$^R z3}qQ%aw2~MM^%HkLS7nyr-gf0F5+#GCAZ8Lz%S4EkP^S)WQ|o@V9>pDj#Ft(l_tCq zhnmMjht=!d-?AM;imj6f;v6Zf zz{xp}+1JCo4?M&4iCoeI-TkT2Se7zO*tzb5$PpjFu~uQnx%FwPU+FPkT+x{WyS4 z=zqMde@%AV(Y+?fhfRxAyJHx?W8zLvU%x2XnwqF08^xhy?hD9H?{~(O_cc}cboVS$ z`~gT$FZ4`S3e%kDFU<}svJnVBdFV!EzW%r}O z@Y+vE%)l!D=`Q`2+N}Qx3;)Rye_pp!%#zeWjAy9Wsi|as!L{|&gG}VIxMM0{GO8xd zQJt=_s8|?s&@Dt%UHy>0v(lZl@Md56dq$M1{6pbE1lpoMbkqK#F#4!@_htBQujf0A z<1DaQ7clDe5$`8pHNz~Yi|2cbNm}i&Y^&6I<^3U{g9HhRSIYdk7|~~w&72iwEbQZS zeb0vJQt_Ju(lg6Z>B7sV`=fIxp39;8!bGUK@nWr!g`+>&3k{jMIcT}j;OykgI5njy z;judkAC3RJVvA`2Qip208NejCt1HN*4|4$Zn&gf4GF;^g}4&=;6Y7U%x*5=F{mFjQFTL zxFma!OSTFGU9CEv1WM2OnjPq8HpVZJUe)){z&y){aC1j1$P;8hYeSo>`1T_q-RCpXcq4j>Z{&0A zrWAQ+qU1Jlsh27=$c=)m->yq;OjqFKE-W$X*K0TIJUlN;Rg3j zRM77gU^eyfQoSx%sLhj2oXSY~041QdaT@M@=7H>TEz0 z^Y`FyXieO6-bvL)g^v89)Q1yPgZZW#J1TGXSP~wrcX}T@dWc}FR;CZy?N$t$2s)+q zFYs4q!{MoMN9PI)ys4&mAQ3mnaUQW#lTho#%I1`-F6!N=H8CQK&noTJr-*;aQJg@x zY?sN1a$WwzWav|SZ=Z-Q(Lz_1Df?-Xn^TO0JdK*3&-f2iq}7R(iIM^{&0G_0`bVw& zq6%*m{*b~{Wq~jI{kqaqNfn=OET<6xi+;}eS)Bk3ThyUrEpngyge(2Mk+K|x(op~E z?IBJ?V*Uz8JLI^txvr*pLRET63mvqRi8MB0$<|jg*pqd(*c@IS z+nLf7b~n#?q;O*3sHV2qhmAvsLiHTihkafuucIzI<{{Krp;{=8NK@PUNY~pMIOJCx zwlXrUqKk=WPHrP5oGBmYq?(p**lubgjS79ZevKUiM2pDny{c(t`CgxMUGdepb6$ln zo9yfKT(%(1dU+jzFNLwB=@a>y{+1yWVJNLH&T=mSGIaBz*9v0Zk8o z=%p&&p)BQip%%S1`ukiSsmYKz))&LOZWXF(nD|+hU5mvTlat4Cy9`->WmYYVf;y|&v|bVx_#~`Er%;}wGw-s z5r=S_b)s?UWO2y%bFv`RP`X>Kw^#<1A*AnhC{2M>R$-HdtJ};Hx{cY=3Swrj#7Xv& z6v0UYhlmBwR~mbck$8%3NItm`Hn$M}ea6y5mq=w?G_O0u;+0*SHa!iPPrwil$@}|3 zBZ_2$nvnP|if7C6+CH1Tw!#xuNe&lB8)D>3koC&Kh+qu9^6KYK&gVD+llqy)ul^|P zN$d_><_3|wZ-$^$$>Wg@j#9maC_I74K8U%lJj_}Ya+XUJA)INOlYtsi8t1-JoY6F} zm6P)>qC!_wv~EF~T?vZI8KhCB0jZmmKP#q+Pb7^xE_~In3l0ny_64PD-N1gNjy9}RIgs^J>t|0PCr*`f`Crf8fs;W zAA`6~q{w=9?@sPaW-H?qgm8jQY9u3c~7aPmX{maH46yv36MGTP1Qi5u^G7 z$}j5pTj_{jPFNq62j<3c+7(wg=q^;B37T4~y7kb-JKr|m(TA8X6xIuluGT2u)uwQs z?|GDJQ3Kj9C?d99R|$bTjfdY5x06Y^_v2mA7B#epN6`~PClWVdsdid$S;)g@+P%s? zcn5RR6tiz@iTNg7@O88sOoqgdyNYPpmChh7b$e<2TKMU$nt%sN88JazpdSn_BDPtSC@wJYh;tpdWQ8|@=NXFIJ1i>h!<*! z8x$P(G=wrLyT#BcrD*s;2${_Tx9v~ZwK!(GBm^foX7Y?5EJaq0Vqv#=A@ zep-oH>i%HlJRh~|9Mta&h**)|u2?g?W|D?9-cU)c0 z`c5FavMW1aWiX;Qk(}hRYnxj>$+VVyx+vKF!oNe$&b|MaUglsCI`iRr-nEk`kC;0e z)r>Dj&rL6f5Ki#0cPr7?RX!(*Wq2+Uw&`7no1s&sl5j=blFbb#+w^AYciKMx@!Lo_ zMm@Kh{Qq_vULfP;OwTgGk}%G=#S5Q;guBU6uvz*C?yJaK@6r~fwB@gQgl8fe(m}yP^Ja5)D?AqjcKvsP2!xejUR|VvYC?y|Qfb@gD!wbeY5P$!tp<2pA*zyv`#64K=RmZvMyVP9uZ!d%dB! z3~$p)iSibmlkFFwH$_xtAC!xuHF2Wx#v@WMK^)F)8D7@5%M0WsGya=HFjkyljci`U zV@)*;c>PN8iIyUaS8(WxnalQ5%?@dbphcian-`Sn+wtquL+jYCk_8`uALe&&oO=nKH}?sLd&OQC^H%tZ(i8X(0D0y*xr< zkPv+wwR6_UYf417l$SrZ+YH~7lYhuWNy`7-HYlU@htJA|N$tua{@#*)7kkAF4ylDa_37e^FP zO@uN%E0Ap;kMxz{RF=07-Kh=Gp~pWfPHEzwa#G_wx}CQZBkd#oEyO=X$a~hd1#~0u zJwM6OLCBMOLd+L$tgzs`%94^9AyUqUJ1*C3Kj_SwOA;o(RQa0e@=eoj3|8CtXzH%c_6} z!sG{;J}O8>hy7x%uAj-AFp6y_oy!-x8p;fF6Y5hc18D=&>kA3oi6vpzoga2a&>abm z6|q$LaHz;Tr|h(PCXbr5Aod-4jWy)1Q?z7~Y0U*<5Fr>zw|$MXtDJ+fLK=uxZ6bi>=7Pd5h|Tc=7*T|!gqE8G@hyCgrdY@Wj+QEccyt!29GlBzPM4T zcG`uks(~zrvOxmTIdXS1bhW4$$JLk%nDmc~>F3Xd+rJ)U_x4AGg6}~=X9D-p&NC+ogWVU;C`hufK0=uqupcgS zM2TVP0RLfJ?7LF)b+o<*JyE~5P!d(!*ynKCGK>`ZpauN{fkruvpa_vhEoWXA>|~6) z7MlvQ&7`lokTVE8f~8n@cHgxXY12Aj6KDD1B5M642&r5K8oAHd&C4o;M&>a@5qA{- zr4PFbch$*vOQE5uT#utj04lkoVdNh^sNq5X&yF8>vt*Fkb628%c! zGCGzZqHbeehLG@~XEa-DMPKm;f|Dl+2bYRUqeJlUMcpVU3eG3T4`d4KRh3Smnks4& zi>XVpZ@S!pgR?{!7&ZG}*e_ZMe(QWr`93s^I@q?b7T}E*=x|o{9aiACyT9(V7Q_uB zKj`48K$gKvDozAZD&pnHjSpn)a4VGj`4X$@#6&)zk0>Hzu~%GcIp4oi)iZaZOcxZ0 z7Gftc$9dIIgKSg-5PKH5rwlpwdVfW567gyc4moLL$X(f46^70c{CARfzFdiS%GMwVz^C3^@AP8~^vaUt`d99tjP)S>$C|%C)&FwwkJ}vV-%RV^r3<2d#uD;68&r$NtWSld&{(^Ts z#B^-g%?CB2zwiGdjrb*@sTpSAyd!U!HL%bVUAkr2mH(As^F_|@4v4#OFOkF&0Al8PF$%c1${75pPrCPd%$e-2r6mzI9K%xQ7Bk#zv;7#lWCFSCrO-Z)tkCt_J7og0o- zxyr7ak!e06A|n1-uSr#YLO>^$38i@OO5amRdY4z28M_VYllVb{E_&#eGbG%-WEi{urHfV!Y}fmQnj< z#xZi)J`UW8C@p(3O#DHLRO!Vbt@|@d{q{63sY!C}V)fBm?YfxIv~GmexwW`6cMD?_ z$_Aro^mb2%L1h4yn>`+GOw7QrQZ_SgBHd#dVSWu}!XhKaJ*a?smS~JN`Dja(XjetO zB+_m#f2zL5`I;Rs^?w|qiZBW){|P1m_k3bbFCgaoD@0^b+H<=m;S@rblzLL?%{8sv za)CY?nObQ1I4$>6BBM)p?co&K?z<#GZTe>{tUog{pW1|}s~JS8oBW-Bn%eU0 zJzdP>_#ai=TMt^9keQ$CU$_gsl9x3X$#gdloiU>-312CKv@rb{*=xmKv}BxMw|S_!w6+wiZ`LM zXb-A2jy0Pn=p-u3avu2vHF{^~>yt4LvnbfbzMP6X{o-I>s?~k5_&YAQZof8!orUGE zqQNNpgAxt4Rt|LiXl;omQ$eXpKWQT#8)#8z5ET|@kjJ|NPD1+bQ~e!Ykt7$ z4^$!OgM1;Cv$Nn57R3RdcWCJf`_MHN@iDdZW0_HP1#X999Ot2Jk{LuM#qBun zS^8F>707Ig|{Gm zfY+tQ#fm9o(sZut;YSBdI*!;aX%ESbNrYlezhDh1-ds#&&!03BEVfq?t7d>L``L_ZM|QSCzyb3m?f(&y+e-QaHA;!#fJBY= zG?D`YKUeeHd33PMFyd%gU)(NG3+)@_yLVw(AUL<_i3 z>tA(%_W3KXI73bx<8-q_jmv9|2(?N#mqV9`96;0^?E*e zbo7qFRC!aY=u}4A>mZ7R8TP(^J>jBa`jTNaNwJN%^el7jgHe*f^ZLbGqU7kDYF;AE zCE)*(ZW|Y%J52d;*UzzbY1DdhtvGq*Qh!a?I60PdJO$pefz%-Z6WYf~oeBSQvj#oL zPTIWL3;glOJy%#U?x=bt|FWKq0pPM?G+$cn8+v{-xl6)_3E0uF-OY@kxkHl+Lgp}` zckLM?;HRNakFbN}T+%>j#09-g4>k3Uh);M*n+Pwhz^?&0WNL)@;-pO~M8KW5d92dT zq46KKf~(>Um+>&~>!lL&L2L#8#--PbD$IJH9(gcp%@oYQ%wGuojbhdr(W*GaB%ES~ zpd~ZjAC7E)+ePSrD>=0=${>hHrWIWNLxE>5JqoweokZrl?j3?8(xjF8o?o;AHpS&M z&GI02yZaoUHU&#_bB|cb=9^Vz&UXO^XS$c7Vu+bnMCJ6DYJKwgjNipM2AmF-;FqBI zVuKF@%z?d^1QBpyF|0>}S+^1m&VAiFXQ5~ZwNnVW^nT(+9*xnC;t1FbH#wgpk9JW{MqOrQ&ij|RwX^Qw84-#J9|9+v@6o#!F1hC4yiP+CBX^Z z+wadW!(r(QGJAG81P6yaPrQJXX3N-4N?7@>q-2{8STq#GRNL*@PjYt+oy}OGE1ibg z_M~iPVf&9qz(?b@FU$His?r0V&{uikl+bk{9G>EPB-)V1*;B8ta4a-giS;|mN2<+w z7os#yCGaQTZ9xP&19};&Z#cpP_vx>=OA1V1sxdtz;TCDa4ruNwuLg$*)#UQ&%Zxak zUOAIPve4wu#y#vstd@FMWvD66CT7PGosBihnXD|s-6smGI+xrQeBuj=4M!EY|fg6m@B{S-5R+X)5&RA7?0^pQ< zW{Zzj8$3J@#4TiWft-xF0EDuoF)sbjN#q-C<3%SsI6i^kkLZ^4LI0l|CEjX>Q9Nq; ztD4WE$0l<3IEXF0sN~mPCtYT;&B@03Duv&PEF$bEn13E*^HyWpz#dxxNXN6b&RLpF zR~j-1pH~`J=lH#ww5TN)U5q4GM#E4kRw8kYM}u1+V+hUNNK~&6ozUAmwUoj;;i^7F zrJTSiwt*U40js@J_PfaKJ}cY4ebpq|8Ri29n=oSTwjlqyeQ@l1w`6>FaUt z-j)C@3q?8coy?qVk!>fOPxXf+tjLh{jFqJAhBC9mWMLz&YA6@n=psrP>xI_Gc}bv+ zK)1$Go15Zfm~7GRlXIX78b-Fq|1)Sm=S1<8Y{>j@K=QRZ-Xj9&6wo*M)?$5XLe>wu zh@}QTn{EY{xs;l~RJYNz0_cTz@%TgJMtN7ON*wJt`1o^<09?wgkaqW(ilp+{%edgL zeyWh3OVN7SdSPw<(AEFDh^Mdd(NcyDl5>;d5-R1dp}e8$eL>PZmid3g%~2)|hvOoJ z*AQW;0Vgkbj9|Mh1f&EpFwU3F(15W^l2P-Qid(5@Q`=t!o{>L~F9vq?z1EfdeF)oMA&5-{ ziY?Ir#h@*j4~dxqNNq*k%ar-&9di)c5^v(YX5u4x%UzStYy6{x_rbz*RaUZFM)u1(W4R@U6>zE3uB&@Q?#mTh zR2>A?cJNuXY{2g4Mf3NNEafsvB7UTjlmHOj`QJ1*pI_toAQbI;gwO>$%2@49b*nbe zZxRYplzG6Snxy7K#Du7`eMb_+0M13Y0?(4AvKTj(9g(!jb=%QqMw14?ef&FzDs+J9U zF34!%ktSxPzO1m}Scat(&Jms=FmuxNfx3#EkKqN|(WO9h_4u;)N@)OOhW6G-Wv%RNCeD-( zE)spIXwAk4;Qd#21Beedv?A0&u{6KX86XhczlrT7gQ18m)gd}vEiTpFDxp$;C>|!m zuslA;*SH{#dv~i0rWI?7YxIeb!hNS}oId7LZSjmgYTso)=QxeA8Xp#7-wC4PZ1upr8!F-&-GFWh zGOZH%VO5No`z-v1Ie8ln%2~o)d*>6XplYk*L6TgJ#gkb~ja9+Bd~Ht01AI@4Iu;eoNkDyk8GI5JZ;YoaNV)pJ=~hNs)NKJ!A$u=%-J_Yz^2Mb`=x zQ;YtPaP~&HiF4;xj=wnd;boh*Hqqm>)3o7I^!d?^j7iu&f#E(2+b(B@V~qPuDb!yy zSuqQ2SD#w;`o@fI|7@Q;vzvoQ-;7I74=HL>r^ea?T+~yLy)Z1k@WA~e6>dm_#+$D&U z=tlI{X)`dbdAotWp!Q`9oF^PPVL53V$OvcV?V1v%~=wLp`#~vR4>*5K%G#@`4 zj_9>@NzYZO!3eG&&G@8YE@O_kfe85U>2duC*gCzvAKM6qH;MxAbykwc%y!E%?Y>k7 zU}HAknMEMc7y_E_+kPj1ux75^dtC_1^eZw~A{)SL-0CXRcL~dfP*_B-M62-XKL#}+ zD(CP>mi9Xg@(T(<|8GZ(DiO8UDNV)_SW@2ER|xQu(u1IPz-ZR?)YQ(RRO&vM zd0$2fZLq#DJ3kzY*scaCMdd*};6l(qNRmBAr(j@O)J=B8B?sL#*Jyf>{Jm>QC0>6y zcLd1!w2gsHbtD0Gu!JFlelJFQuR9P*V8M29kkv$sqtD#JapT2#sc59i-3@l>DBB=G zmK$?jSJ~FDx{rgh&>w|C?94GrZ~Fb8uw5R?)j;VV(m^QjA4E3L%z4F9Ko4%==RQ9iPv9fYT8NyOf0pv(p;Ae_jRpQY54`-2z?OVBWD}ZTu)_lJzJ? z?e!(uq&r)f0Q4QRn#VL185!BR4TE02*t-Xao&=tkjdasM0NX;M0ZHQz9H(_F$K8|d zDkN_Z^8P+8<-PhXv8SX$$7>-O=8&e&zoG+Ax28cilt`|Em*~YFzZa_qWVh23)SeFG zb9CRk2K1nHOQaTaw1 z79@-o5Z;O~9ArC;xw8hmTk;HECQ>E(7b-;jfSt=JSHIr3_u{>cxDBU`wb7u|*||Rv zd1w$5mGHGW6)pOzz=Pq;t6EOM?yorf0J9Kh9mtNFB04Kdo8{gD24_=KJ>NIPw|#bL z7qoCxufIL6V)lEPVK!cBN`Ne9^pjYOlx1@o$W$_SFqrgCqkuV%U6vWQA!R{Ved8U# zg6}V#)ro*W-n3p`dFd{=M(8$+`N|0><0I}nQbB{BG`WZjnk0s*Y#r*)VA{yn$kc== z?q3v4I0)qGc8_qD_6WX*WUtjy#-lV=_gm?XSxn;;=_}J} zAGA@qe%f26YlnB*xE8uFxeX6!DhS71ss}#yiBKHzS<}bu})u6hB<9RLf|FM_biVPO25; z+DyWK83xrD_KA*@E`|-6N_KtElNnCeYf!Ym_*-H1p7zpB9YWAKQ4-lmkhx!Nb60l1Bn1hfA>f%Nqr`zW%=(7*SZdSbPc@V8V$?Dvl%B~EJf|sB^f8XY zoF0xEN-OcC5!i)0Xmfh83M_p-dr?l1K-%A#!{N&EL`{6j5$nzrqvbDB-%$v%*iQO- z<)W-nJ67q>%1J%fU3D6m1MWp?-Ie*^&v&}V&?N9A)(PyC$}J|Z>)1djd`YyslnBLl zBgD5(xYAGsPS&(hlxS6A2umCkiwA3VvZj73;tC1htIZY@z~4I4u2Fk$Rv5qtW0hwK zO3U)M{dbg!YiHHrSY1L7t3!)Sn~L8^n3oZZgbOenw^exc1bR4Fde<5NBTl^q$6ESe-^QQGO=D zy?R|{0e_br+RUT*Wr;GgY%oAjUwp=LISD-q&0B#7~U% zF9o;z?Z-+S!ZdL~)-3iJq>TxaOVdyM@byqUNCMHxO zOX%TyoyKOpPi44kGNbi(nXSm&7{Gy$lPHoRt@!z3!=H%cCVRrat#{mBNiW=vObx5L z!lIAe1N!Nlsl8Vu*ga(%yHfxS5Z`j+F;$4$0WWNg;NXhihWU@k+})=gf^HUy*zbyz zmaU?`1j>*_B)we9#Ef94X{bDnXD=PA!)&2!ys_k2pGda_$~6KX3~T5AG7XDIz(N}^ z{_o}OA61~1I3ZabZ4kVhXm2?GgY}1!aJ~k2(bg~@$>kJPt;Fhavo02clUE7~Dr3*y zlIuvzecIJcf>C4N0k_h!xLnEUcr=2cZ(O}0PTq$;?SRu)k@?AS0rgzt)*lhKAU>o# zo|^=!Nn>;0>}Y%;f}i4+-EGBzmt@M0zf6g2R{xCNRE8;2V6%VVcw7pY$hL#a4DWVE z{9*(CP0vgzdv;^uXsdh9R^2isJ0YN8u@ciW@>CVL=@Jz!7$@ixebOr^-7A@PecY;-6_K`?|Aas4wFun+0es6 zcGsjj6go2z57TfvaYxT=rk-eUB_BOO*bsr0pMBO~E55o{kmONBc`}2*OLhG6En=it z67@t>!<-x@FC{7ovnATrgyBX{(~+q}Q$YI_4=C0dH+!WpRyV9XZ1$ZN3b&)$QxlM7 zQ2JrPw$6JLSuHD5Z3$Ct%Y%h0UxK3+JJ9J@>6AynXQngX=Ty#B0w;_jW}QJW&gJg>55)tz()VWx-CtH=E&urj{)i>WVQ zBT`FO%tmzls2Vc~Pwcth4w+#=BlcV}=pXj~*+@i#NPHq|=C4;8zx;4PeCZ_p_qYg7 z2v{LZ4NcX-d@NU3`e8G~?>dMm&O}L>WNz;?op7Q_zkmMm2DbZv@Yw@M6>uAqQBBK5 zap04-yHD|tGM$ZaqvYWaUsR4yuZztQ5Cj@QnuFwyH*%64@4mem_0@Tn*Lkfb)iv)a z7r~Qr;8{-lE)EABr{*N$S85RZORhQ$#HPc{+nx+43|=jV{E<4$*U-7dLHhe-l_;JY zN?sg7(?ZIV1La%^d}BXL7U%!l!B?mSZ0PLLdON&T+imge;%ceWiHlJ8XvLD4sod}h zUP#d;pN;izB^hA|q<5U*pRbh!uMb7^Y}#&$Og(AWC0s0%Qi0G6v}q71D`_}rjLQam zf9kKpI-1mGt)@JkKt(bd?_ckz$jIat8-x%W{FGJ;J&$C>d;r)1MRvx>@SYK(wI}ei zfF1L>GOO%s{Z}IiR^@m(l*mpn3FZr)^pzfR%1y-991Uo zgQ?Eok5vXSm?rC6P&Ak>Q+Rb`U4{vS1{=Qi)&C_d#iFDJ+ivqtY>9 z@h~6PTH}LM4`{eNj!`CoaXi$|nqmyFm&TD)(RkiW}C^j07Hak-srE+72Nt+3;Fvj^vkL44nobjXqNx^yI1gjJX-O}7e^FtOmvIWCLnK)V$fsi zr%QQ^3zl~i;m7uT{Kw-1D1!hc8IX}!@*nLJ1u*naJPT-k{%TDeO?E$7GX{g0xn0vE zj2c71bo8@h8flkPr{2v;X~NJm-vi$Dw+*F*a^v1Hk+qxPF9TnLmU>8jj?#~)?@K#O znQwhm+y+B0lb2WT zvl7Hzio+iEsu9EK#UT#Vu%aP%?$M|_Oy79o_Q&6xhhBJ$Usg)lfTszyh0sKvngpN5 zI?Z{0tz|pEp0tSits~0B*s-`j-!b%u;$SoR^O>TGoqa1VM zLd-I+axvgE+;t99kd~h>3&BRK4Yvq^&P7TD+wAzHQ1dFVQBYy)_*VGVx%Y{g3xaDi z#oMk|P<6Ko4(=q3y5;$m{Jt1{m8zwPWvMsTa$n~{^@T5kneU)atlX>mgxdkGHMyR* zia`ISOOMFi>{h`+!{a6tItwI}$9LPjVI>G@+$vjCxKQOm7-CRQ+U8NA%TA2tL3|^< z&36Xx*p|A-T^Q`qi4oX$fR+CDeE5>xK|=}8n*Q|Z2E(WpScCtLlMm>aooA(G#;PWt zWYCb)49h$cCkRd%HW}ynw1L~R2Qbv=g2w4LvR`im%X8TfDcTzcA<*Pa6!?WJ10nW? zz7$+G(jwpg_Z0SDCMxM0WXN!pvWSc-^KKj!l=KPYZhx}`#m>Gl*dKjw2iR=nGT$H= z2$hBee+~{kwtU7?m-B5sT!grZP&HBt!b$k6%n|aGz!;L{6>D@eLzNx1sJvOKPy%5k zg`8h9Od*t*$mTJyqdWOVn$p4%?{596cF^6a#N1z1IAQqElhuABN$6$FN%DIU0k*Y(M9?8OCm-wCxoju zK5ni({2}%vp7G3ci4e6ooy6hEvIH5I02%QJ$43tCMDaGVn*{8#y#k(U- z)PBoOO`PI?H^)i75e^S08lU7!Kq}j5gu7t`1*C-O+{{aU{w7FPe?1wdWIIEMBVl&X z%_x3YF`#&Nc9@hHR%2&zL_s+lLc4CSVOM+W68v_aztg-G?f(+OO?i(#FE!{EyB7LI zBBeh>vrYOzzBkxW`tn#{zEBO8A9w(9+sNVRY}4)haXBgax1IIwXUHlcBZO5b-*jFVBXD~a$jH19R%^C(33sdCo01W8x?)mqIDBb0${PmK98KDIhsD0hgmIt zJRp3!PbNHyN);qu^JwY@x`~!I+zw5TcZT*c*3&n+R;V`~G54{F5-cZ%3y^xWdn*_CvEEM3b8n(}uGUO5M|M(g zo0Ymjr}RCxi+IubxR&sn}1Z?Se^hN3yz72WHMd7`93X2IEJ;8$4)-?&v_ zhn^+=U@32r22!TablXEJTgv1O#mjry4TBH{Bhs`v=Qh+z?N(P~{=*yedar59Tt-VS*RNxJuE z-IK*NKFFc*@0BNn!@=qTjogi^dBh?F=^5evtp4FQg~v+;SH`0F6x)&@HuJ~j5I+A1 zU<$@dvXVfgm1x6#u9h)w*6VR?_HD`hJN zoD9J&E%gCm;?-yy?n0lBeM;N|+Tjl>Ga2TJO6(t>Tb_Dd%@61&s^nS#8)CF}Y!=!g z?xuGlYUM9T?rxz-{ubHb4Y2l61)um>z@!C@&b675)s#7ZQr?u{|*9Wz#K7pbd2nz z-d&IA7>^s%BU~kXZ)5oGiVV$o!8w8b283t-v>YqW6?EIiGP#Dt znV{K~BID0Kue#U3C` zU=VTF&6rYJM#iSJc9rHBvX?izsmYhl8L5`&pTDZ>=tX#D(eHK=k0T3^cGY&*V^fPU>~hi8!3#+E1L-|Wp0$s>gYPjEE>#PiAZ`I z6DSq(Zg}OKw?K&blq-Sl#S+nWe}a9@d(}!*kw#)}kuRKLqhu1vyGJk%ITR${wcYG?%?sVVwe&n%F@Hmi)%rng< z((!0Rcuj8I8sFKgn{w&W0ojbd?28jcdEuZ9PxQtRT)_)`P1hhX?pgNwK?s{MH2kxo z_WzqNOgX&0^XZk0H^hJtL(Cw)!;xSYa8W5Y7ieJqU| zc(LpA(4TmgNZp?~=>UK5oS+t*|=cb(D|pgcDBeZ;iu z35V|~*8Fpofw+{opGuxX&7#BlnFar`6&9v|f5xGy-x}^K5U$*v*#oiP$YMl-zzQTR zu1C{2=pmIp=^d8r1AirlING<`!=J3E2r~`{GCRYGyBTLT9@(ldN*Qv{DHG^|DgBz? z9H*~u4T3Pk-%|O}#7`#@uk*|UzZ>OOn-ct;81K8kg4nkQi2W{-G6BMAmLpoOC7%I9 zG%tZK6`j9PI_=zz8jhQ9%}NxP9xy{_>$x=0%3}6>XtwjIbC`@VWR>-Ak#Eeq0TS5N zMZ&<3ADWEc=AY%^IUCs@W_2Y{xnUNQ0OQAlS%yk8Q%z2bi|zgGzYo+jHN$=9IRV(l z`Uin^dDMqWh9@-7HvOWcd&=6yNY;Knz86*iZI{IklO_JVr?gERJz;p;4nG=?-}G|V z^Mh1~VO&WmbAsoxWP^k9_2T>2m z^T`V7k6}6`IEzzc9vKtJm;*?{ZOYV4g|O>s+9XZfdx{2V64E61r>I(KHrqJkM0x0r z=wkA2=r+VaFyqnbXDfo?!9&04tB7WapRmsCaC?$|`5Oc-OWQN-9T={8-$FYC<4AtR zk!)ng=Z0bBw2{Uti+ZOVZzigb8|Hdhq%RU3!l*Wi&nbyu@}gKl7v9sLr&E0jTkvtK|sF`LUjzIPg>-dOj@CM;oe{>WrxEt@-EZOsLJ-S5|zru=N$Z zcebYxlUc-SL`B`|2mc3gNQKf;Odo0clVnAuDXIV<9+qc=eTUa9y0?e>PI^XbuPKU` z)e0CU(`h9iQm5p=J6n${{L0F1l45eh4v1#$$SnA;3TF;wIjIiJY{?RTBh@8ev$b=R z-Me2k8n33=M|~4_dd+ehnzkczFKP(NV$M8zm>c|#r#CQ{AYQs!XYDm z>+U?1X(O2KLy=AISSJF%ta;4Vm}Q6WlG^q&k8&FW+|t|l9D5w|#o+RU`ttUVl|yrQ zCUpE=WIzgTm29Jxrn5O=@r(UuMBZmFfzyx=CrQpK(5HBHs0n&$T;6 zHlZIzUu}ZL@nx<$1uG^S#Ai!jdf#TR`boR8G_B2MuvDW{ZVaD(&xK?%AJ68gm5>Dq z($RL3cSGm@clR6alI$N*ar5(^&cay=;p37EocY3wS{pvN=`mgm3XOogfbJE|qvd3R zREVKJQH&PfssG`!3anRc@OgU>>uv*9g00H9uiV3s=w$X#HS^k(P@3ygasCUP{Hcozx-n1%*^}tS(DYC|XOir#dUucxrdp+Lo zu&Q7zS1Tt6US>^&vy*eaMh7JD#(($#?vo!#+lyS62Y3uk?9jS(JuVi8hgP3+9!c-r z;lD=Oi}Ms-k9=!7q&|4uh#MZm{2CO2r5!IG_pI!rh(Kg7l0({ zo9;U|()Lr}pFU8oP2PO!F!pe$^E7GA!Yc`jgVs$b3Zz(yuUyRR+S}UCjHy+qCR#2h zVgbbcVAAHDlmi2@Yee;rP?OsvubP9@kQ!&atC^^T7W1~QqK@T+pvGdY5~r{&JZ(*= zYY?d@evxxjwUp(Xt>3DAZa;{_@^(dH-MOLlMO*wcY^#SAJWl&k$L|b!%Fiw~r#)gB zTdH{9U+m7*p^mXAE9m6dUo>&d=WD{>LzOrb&70&tUgJpo!5{R)Re|_phyG${yQt!o z$N;(4Ti01WK3MgDyZwsr_U;W;FtcR;iniMnvJ%tQ+eF{No!aWPxpEN_e*BYGcpRXB z?&&m_8Yp5$jQWjN8I0By=bWT?ga?mWi0>|SDrUE6x)5`p8VG9LPTiO^M<&)RdXf$& z^G6c4^DzO+LMc-yxbT%Zh;5L2X=jyW6^^EVjEAo$yvpNOqWhwO%#~Y&T!fL`t`Xrj zKFBav2+N`UgGQaTpSo3SijyjtiR$ZzIy%)E#zjrM)an)v?nM=M-na1T2I~=&q~>gN zjM|%oJ;gZ2(F)V*>s?iUI46K~E1Vgl*7k}sB<>j7-96fJE75P()L9>^O6l#+Fpg?- z@u9@(ABi~p8Gnc=pcloe=`B^XH=8Bco+c-iUm4W7{YGw12eIZDM?$5>no_uuqw|Lk z;J7NC50em?T7!)!mBF;6uf=j*NenpVT5f69$MpizXX=L3A1&2iOG+1adPKREsTKZ8 zmi;F6BWl5LG_@H0{y#69kB;ADcC+4SqU_D`#FHd=@g!nDsGwJ!&u+9xzy1Z$_(q=Y z_O@fyqnhIO+#;yPgiuCjmsoL=m6fgDCwoR6_=N*uhgmlK(9^@t5c~dD?pRxO&x)m` zgVSzDcfYh-9DLe+RQEJR^ATT)KGfJ46-+&ORKN8K`Dn7R!M%S1J#nagWKLBBZ9`O8 zrX<;NCZ2RA3E7Qqjcqrlr&X;KD9`6OE#dmO?>kK%e%U#V(~{ed4ID7YDT>sNqx z9{lB^|NX*B>dWb`^CR*9d>hBn?JPvJ<=XCi=Jr)I&k(11TR5LWsAsjeXPw2yxN}O9 zNj#5!`MZwj2P*&jMWTj6IE;@Pdiib9JxRd`r{H(<>+8zkHT%E*Ciu6ih41FM|9!Qf zg1d*0qf}wOk{!)@;JekyqJYl!U^DCg`+pb|WdF~%ZYXHnbeXpUnUt#DbLNph#fu;< z$29}@kqa>gVp-E&u#JW!B$x&9fP;_iPBE~vUFmNi<(TCO5?O`0w(dUlH2^dD?XpMU zU2s3~#MbaKK>lu0WX3b|-#_RCzn4cl2xjUV0c}$Yu)RW6QzdG>>#AUf7X0qxQY|Od zNUm0j&1vnnU$ttxOVZK>GZW@ZAlk^ig*r{e{8di?P}MtDKb$S^%8D6YrC>6;L{*IZ z(a(8)2}+&^$55Cz;*UB24$%lCp1E5>_)u?vQqr#FWOt!30p>8!i)h4uouBrocVv1G zY8o^|B;@wa-L8Cfs~N+M-CXof-pcG=c2Fq%GFv`<0l~I9Pk`Sn$Q`T&WKzkhweW8~ zph*yK2IQ?HT0`vi=grg_x)dJ4)=nVuo$~mS zreC~iBHeTT(l8@z22@x^{%n48pK845X#q*M!rVH!6DOZ-r2yj+5d4p=?!E2X;!i-K zOHut0d!^F*#0g#%VB$l4!yOsT6APNBUI(> zx!)r~O+K#qH-7a;#wp$ldUx*hTfbEC_bKT6xfZsyw0Z)#tJa6 zYimQf8oLY37d6iSJ+&a0;nOe<_)$%|rA`sw)I_F>oTrGrQ`|-$>l)ZB%>%l)%{>uM zhthwpwgC6~-8Sz!iKB7z7Vtx971hjtYjN_cf8z{-?LzHMg(Y!M?hxeDu^PeRMr8HEbBz^cz=kB#+1v}|ErQ|Q}_cd|>wEYF*(B&>jkG55R*Z2_U znFvyJHyPe4Sakv@vp$G~pgWk$Rns8nK8FTBg(uEgAIJYI|CKXKdH?&Gje1?{UAx<$ z@B-VdpM9(e+5`7_aHi6r;h>0BMsg`|ft+mXqJ9p##2O>RGds@YGI(4cwS! zKN*B!%K!r0(BNJgO)W@4PNs}$s7YJ^nKpt6-`WLcz*k1^|M{|Z3^x5!V)H5v$DqDU^=<0q&eA-ZttEVX-Xw--I8L8D@v!_ya- zPqL9>LeG)LaO|Bmkv-Em9o>h(;ZV}4W6ZW*Tn%>ygm-YoQf?Oj{ir8qQcMfzz=O;{ zM7V^>@ewtLDi@9ZyMe_J!Ox`=m$uzA{Ba{vTQHTN>z?scS!%)~%!>L^Lkt10g`n(CA(B#gnfNNOlVYCvPvtI(yj$ z82Gq=%YUqO+iuXwH}v8Xo-q%C#wA9g@~N=B4g+yfv7XIvG0ZB>VM;LK@*ckl-vnn9 z9@{f59LZ-7)sEC9cAGJ4Um+(DGQjsYE(5>F({P^&kb9862!R-K-W+V_C2kqM+LFp~ zxny?+yx6Q+-S8ez*RlD+;^GY2^N&huGVrYLA_2n$o1{J?usdRaI@>EGwg!LTwdP0V zYVs?231_GzfE_CQsW z#=Y;HMc#L!p^!Rx%k9#00rBGImX#9;o?54naNfW*ZK|Fdo>yvA)gZ(m#{GuSAS}VqVMLsMIH@;brq^6a=S z)=r`bg($1s@GNfK zsUPB0d`odryNxrw2V8p4!q07n#Zqp?b$b~z@p&NvZw`U6Ap2LTkN)_TQY^^qKHX!V z2GSw!o}npf5>j(BRq<}~ZE;-V^^-ge$m>OYlaq(DtblL(4DwBt1n!JUo12vklJ=6<>V5R{^P*_nBE>RFtHuQ~) z*an}bYox691rF}SJY%|Y@STt4rTl&j5;fUyLW*rR2J>AuWTggsGB_{JFaa}1t~>*y)Y;}@M46azJ%L;=5NLGF z=33_76h4t<{W1RgSsBR5<{sCq@?p-K{}e-%+OSG->5mv*6hl}7KNb~ty$s2b4(!k5HPeIPy|`{rt^O)^=e>BRX_iRoD4m| zo$UwPNG$3RJgQjZgYR|?=LR+`h&jAwNI}22Dbw=Hk<<_ftuBm+(^$6`LLmNQ`CS3iT^v0>ZKd|Xjm)=_UW;!%0W zCSiq6F171ieBc6qh^UV%u{8{B*zXo}GN3eI6hElPUE8eU;a@Hc5zv2hp`>#t<+T@k zC90K9)7svU$+gNC^%sXg6^yk2RRnM|C1+u&+!{V$V~()k{_%i$%n~c8qfz!b#4gPj zIfFMRf777nv$UYY5aWGuV6bVlk6ow?`hsMo#*W<&yoWZ6iWZXw{tyNS9VTqLsAZLt zp2E3JC9{{d$lwx%sg`S0lh})K{J~J4k_9W8&Tw_;&jnjC<*{PDRwIu2ICc^t48^CZ zRb6}^D|hfpErOkK?=@0T52Tw`ytN}}f&?iTh{~2nv z>H|&$l<4Me!gSpYBq!tPZr>o*Gjg^l16?iQ@LAB#Z2>KXC=Nf4r{| z3)qfM9VAen*a~|yw|jw~J*G}y72_5I19j?w^v!jpf zR1WFPv!;QKS+0OXF5^uuvjskCD*HDBqT|NG38>q`7?NeU2d?)%JO#S>ACQG3h^<^; zWkVCuKWA5UE7Ih-AdN@OXJ0qLIau-Sfz8B~G(Wf)Z8I&EDP+n*(5v!WZ0Q>CNW>`7 zTbL51%le`!q2J)IM4AR4?L7Ig+Ak;7ZYTGRNK-O5H91~$)v3Ex9)MHzx44@ri$5b1tDFe#{`a#7vuloV+&0Sw#~>J$}2mjY>N-;i|a799eUu#zdsK~%;yGf6zM<(H8%J| zYg*YH_yTPGUtboU%GOK#)Ea~Bu&m4kfJ3jNUg&i#-47sBRUd5wu(93CF)RpZqoU&5 zzXZi!pAUiJ0Wp<*NgR__dgqnNj1gJh-@eYDy&t8U#5O;tR9Y;N5t8m&tn*7DiXcz% z_L@l}GmBpt997*)$j>*&wxd7Xd)rg8vBu;u^U{u`9)_Q%ZB%mT7L8I3_re1=(iEh9 zq(h*KXCHwx$g0`-9WvE};64VL>M)>^Rc&}w^8BT%O>NP!qf4j2gXWR?n*S(iUQEVGnff?U>-}LcuQ+L6$ z-03}!D9O&E^JZ3=BXY`UinU`7wHGrKAf&>1C;}dA>`$Uu?hco^cQ7AFQaulVSxaJzC;~fcpd-@}feqvT>SSAuyN883RC%Kc{H2(mq_$W4Onip! zGCGX9Qu>t)UB$Y}5jcP~+ryjKg$UehU!*YPhov+;vj}+Dtq9#FRN)5=uDzmq1G#;s z4{wI(9_051v&(vKfIj)-*3X_!8(@L6N96UE?2rjN=Ni8M82$k?xN}@v<2?~$t@46< zDhrIW{CKX+7EXKw_Bo{;a%aiTzP*>ft>y^5{@NrG(|X7J8b9rIz>+SO*7gHSL3nw@ zCXp;SJgFbKiE!5QM!fo~|K2zWg#lrJUImzC~d4MIVGAb-+b-Lc8`0?Z9 zGxyU?4<%v5@fGnLpHK}<(&MT-4lhra5jaAxVW%=#opD7u_Q}=B$E7Qo8s(+9~ZMT$XcC(a(=>IK{xXLjRDI88|RWxTGf!%Ki$-GQ%u&xL!mUWhv)LD^v5UPrS#5|5tP2+S6DG^4alVU z_}u`fJZ8WV`j~L~g`Sm6Ke-t>W^3n~P(cz4Cp@czTjtqkl1BZ)WR3r> zsPl|pz~WliD<$W-7w3IlU`r5)WQnrebM27RDi)MN1fE^ZsG*4PY^&iIbx>|PXnyMz ziD*tyH)d_#qFLql#aSQUpxigQezj6HxN0Ya&R>=Yt^<}4MyhD|QAJvA!ehQ6nPto9 zU=ke}M$Vix5aLPR0K)sVhM)^M9TgaG6Tjt+j4 zzC954=Ql6i<@6_75Mm9bs~})*CQByslOG8yv4;bNH8~%3c2dvS#}teuv)vE#7!jx`39`< zH#2O4Ms`5h)w(>;n~>#^;3;8^KW&NWXGjj^SH*jpByJxAkk$kcLRq#X^6%wypU8yw zSZ`Xqr9~2xjw%-(5AOTw-KJuTzOUO*PQ)SzYo4OmG2ncEmHmCE)7u*^%EoMY!D_&XxC$p+YW;;{xIiuRg8i>@7?2;;^RaAKxV5p z$F~JNdmAaQ5B4)`3HgizzXumDK-i*kydKKvu#-S#jp|Zwc1}9hN(Z&Fph#VwA*%|< zE)K6fOQ2xsAz>c==wo4;2Ci)Wa|DV2DZ`6A?}Fl^-Cwb^Kbq?UZ%w>EEB2?qQ?r2K zItisgz{ReYuqiXkJ>~@7ex7#uP5GqzkYdO*q`}{lmCxVslSHV)MC7$W1=ZaN7D@@` z?@C4E?l%qhs5e>dvz^Kw7o1hxZ;FJJQbJf0#&0`nES!e?~mO z-FCJ49T9b{lVZv>t@2mlCD-C1bng~&=f)AorG%*Lc;+-~c1y1jM|4CITV4?qhHRER zBR9=)*oQU;Y{pa4!z7wsEHYD?%phmsr?-n&Ma;V0{AGS8%%A&jV-S_%P3b7whq5Ot zU~?}4zFtAFfu<+8PIM(3;aK@4sQ39T)|Qb9{+96DRK*Zaiur);wd(N~$;&Im>DB{m z&v{pKN&6Z2?`S^3eLji5>#uvkB*XLCaB@H-kJ;V)GmG&_sT341@AO53J9qahU->m3 z;&DgKpp}a1)5YS6csSbs_*&El;`>DumYj0xS-D}Q`R*p1rK2|EO*NmVT#Y7&6y*;d z8`|;7`L6z{qWaAQ9v&Wb8jR;!=esCnhJ~T!Q1} zRjC{ZeyWFZ;#82N^C6J^Dt?O(B z%Hhafo&+*7RFv?wBJ`wJoK35-y`+TbMS^cyn0)C!^anjb@9~0W&6D|k_dr93HP~#R z_}BeZvvr%0+j3IWzF*vnO4iRWR)Tj*`!mVIq2F02!er0X#f1{0IlT_?OsJ4wd#TLq zXKT4>sieH;u@dbOa9C%9+x(Y24=Jajgs*t9<=2O<9%t)Dh^K99N*|o`$M5oU@+~ts zP(hc8_xd6}*&~BKF|%Y)aXx2Cw9DgMibB>EkBw!;`Z+|i$gmm&H(t^FmX7?AB5Xt6 zA&j$!zx7dV%M)cDoNu9pzFf17*s#Pu{1*0R@$*Tv#wH&+;Q#6Bt)rrB`!C+1;X#D~ z2|;8CX&gzVYv>YbMnXbbM5KoS6k%XUMUY0Mkxq#Lm6DW_Qby?z=@{}{yuast*Ez>O zTuYa-aJ=s;zI%W6rrg59{~naP4rqDVK2KC_A6?2*m+Us(Y2EZ%ADEbD*^6kS5;e8v z6Me(HW83#oAy-I-{**PCA1k6wsL3clBYW|68Vp8XqXnzUU_C;uQ*qnq{mFtA7D*HJ z#gVP+JHlZ|N>&AcxqLmZ-FTZ;XxMJB>u}V=Jl=nE;YFe0*)^mT*5yvpYTT*(e7yW+ zx+EGtUXjPkt@EbS%$sn!zMXiBVO3ORo|6K>bYsSOVp2vo+W4p;Aa|FEAHG=76Vnjq z-{QfvT0)toYCfv6D~L9I{}+R<5T3<$zjoYGzy?O^B2HTlX9a8b#d!W5!0Q&bW;jQ} zZJ$&`eOGkmbi5~V=vMVL-&y&zjfBMF0seIdV#vM?(MD}|X=;z*Tkn_^t%+VpJigM~ z+WcZvr1Lfes>ocomqYT+bw49U2j%efRh~@lwXKCaWMxJQ(ehx0zejFn#xR|akrp7S zGGQmFYe6A@UbkU?a6cb^rkVix=^y9>1%FT)-OQ9-zOZt-mUj_Ci1jzh(i5gVmd)MuN#u z4eN;THfrq%4O6i&Zp|u|%|Q}h<;|r2@f#j80MUiQVT|j|U3o%I%^#&mED%jn z=Q5@+hP?M@RDKSxEI@4${SpXC2_nwDbFK;t$GnQtXcXNd8zr5fUq6?@6ZD8zifyp| zB3Vk|boOE}`7pMSypTI*2Q3??j-1I{Y{74+mpi|o2-mUPmmgQloT3x#9`m5Ip^S71 zR7g;3vpjvcDSFa;xWU=d= zt=M-PZ&N3RV>~!bK*!p-Ry%JNco*9}SGbgF;O-Z*JXeIEMVrliHW;vvLcImR*6X$z&= zvgA2)kh#0}o2^h_>I zscM_tzKOlw#Lh_qsT`-!H?3 zY1Py?at36p^%A8_d3<+Yb`lX7B>a5d~XrI_smYn+YV!yk9MH?bGy=!is1b9N zaJdxeUAZyycH^ryp~?JD#NqIuS%uNeb1BZ0(bq3;{9aq+=Iqr#EWW9~bk#dA9d-MU zV&7MUfp%PfZC9mTSMG7ijzP|q-XZ7iI^Halz)u?9c|)UMbKaCSJcnIZw`rgG0xx69 zroMdS2XTrhUpDBEN~iZi>rm@qrVd_dJ%!?Ru0LGQ(ciMK|En_3dXwwq$c%(h-Z zK8ShNNw?el80l#5BIP$Jp|^RZ^b^q=vP$;Zh50oboULE}Ef7h++ba0NLx?(Q;lBMi z&E$U%yor(J6N}%lDNKz5n1r&dE3>_;)*<@26o-=G5Q+qa%G42RmbPx{QYK4?9VFXC zlehpBXyJe`XA-V7lK0vHcQ3qrH^>^011`%wHbTOd?AMGm?76 zLBh%fC8yK3j}w`)CW3yK%%e6ml0gLp!! zP9Bz&2Jq5G$AmJunZ(uIAt!^-Eks%N0om84N^fH?EOwG5op`LI%0qoJ>wfTS3zt&uhba z@6uftq~>1uVHBl^4Q(26Y;k+?wsGX#BVv^J26v{goI5ehLt6+T(AIwav;W}J0aVQ8 z4?lcqnqZ2dZ#XEE%or?raDDD1eGf_Gv5Le;Bv#LbzMH5HxBug{vnJ~N;z2@ET}%jR zJCd1!l%JIo7q5c*`QlRaoAEZ5v#!7ER1oSAy?N>hYJ1{#0l4j(eme)LCmNRiWp)^) zgpjsv zuHV@F&{AOxsqYp$HK)zSNRYk}F-UV)Lv8tvzl10ycpJ~I^-h_6_{l*PEwb(KgZ$CX z2(zgw2L->@F7MQrQBvoS6K;s}M&A~W;@(~~4424WsMW`Q7@@(t?-*!zDXshcy78nL zD{W=LK)f#$I>1B-p-kJ))j>JW<*+G-Z2Y+#odq-_R=dojgBz}Ys`Gf8{BFG>bJmkq zMrn5W6~(Nzs`hMoU>(K`KgzZfvwHXIIkZ``ZKVLE>0=Up=Ii-HpCftrW!5A&rCR?} zzi8v!nRA7Vqu{x0^w@*4=1q<;{pE8@_UGVPlFupUKQAv=zu%K}B$OOcXh=$Y&X`vk zgTPzJ7Yqt#tzi-)SrSwiwThdgl&5Xl4bc zoaVL9AIpa~EG@;RO}8J0eGqt^)Lr+MV$(AS`ut|68IoEpm*b(rie|kYs^t}>pqFy+ zf*5gMih0*a0 z4A+D8%MC4z+S#V{ephbe>h%H>Hj-@7e3djF_ff|!4WDwSaa=}&AcO7nJ55Eie!A{l z;fS!_@!%dl-WXp(_CS!oDNyo0uCS?KMDBQGdBiLI1_e%Usx+5COgTo8YwDc{HV&&d zlFZ+vA??*Bu*Hyo4TG<+e_5K4I}z8o4{hwP6ZVKZe3d*w#MSDpNLOy1Cu@*-(dbEsM>E0&Fs0iF_%oxOs)bE-0u@ zA%8?q_u2=(X?1qAyYjj&o{wJY!iXVU_pw5Hdi9pg8>-4=X845h|8(*YB5gzlMB45A zJ{!Du7>pSDQZ&j;q^l&h7FcOb43V|eu!^bcW^tF#Yq%CiRIjDJ!x3_|Wg%BQ%r znhys>x~{L>obK^JsD`{-z11CBhe#aLM{kdiMpqsHc`&oh*{(^rKV+hZcbl3roRqGE zv-qEiUwJnQdl{|TLIdmYTf>)kNlwhj+^FkBf2lzHLp1O`l>*aVheUL$KOw^IzK@LW zeuHFFk=D>yk;lcBGXwOD={TzKO}_-Q8N94JtulXL`h*!tb};fFF9S#Qj#1> z4AI%+xt#5VGQ0#KVOC|=!(JyEGiU1#l2 zPW&lpON59l=BMW{*{{Yn?@;gDvXRR9=iMi30^P5f>r8y~^J(h^A-yI=B=-^D&2)7q z#388m_-<_!2q=LPe%llYD!o}px)!5lNe%pG<-r44|_$~or|k6hkXnX;WcJgw6(E4 ztfrI*5w|gu-m<*4PtgfITOh1Pm5+a|Jsl|y%q2ZffNv0kgUuHOgj9o7E0_DUbL17` zoUS{Kqop4+g>c_}#*tPm?ug!&S`k<0QW?9gEt}vsj`m9?rDa_hGnd?N^a+8{{ z{de&r=80kset0Akh&XYh`A|iS1L2`M_N2x+q!vfT%r>8o6AiHpBC}1*fFuG%QGInB z+BAi2T#6We)m13wBldX36spA;w)?QNQq+9?wCB)P$Idh?^C!mygzs;Qj(o`O6n&c+ z{zKO8#}O+6$+%v+H`yGg@EiVbZ+G>$=HwzrsOSs0ESmp6E{GfnvdGT{lK&K7}46 zQ@oS?9$1y3GP`H>L{Zkh$Dv)(lo&5!+dmbnIT7N-+-VCLEZLH~-Hu&Uo z&{U4klKGTyQ$>r{_PVSRpPG32R&P_hs8o6EgfT1M>#^9hrPMgiv>JF&F4`zi9km6Y zdnpMx#o1n4h+WLd>PStXeAoGILQYA)eh=vbOx-%G8^@v}qw2EhVWCvK` z1W4Th{~9P+Yms%*;ziNPa<+=KT2)7(#=jK(KdbAUl!gd+$Q^*Z&s<@Co#)deB!RD( zKeQAECRA2QwOzB#KG=TbApQKqq+}4V1NOf>4Y9DQm}GQXy_xerA@q#G2@)1rnlJ?l zF5ZqO^UJgEVrO?gRZ9$+V=K$gir9j-@5-p zhlnyKYa%)*#LvowDxMa@>oIm*I9KDg@5MBC4dc4ktr$*Xmr8v%-pE4AUr70r@+-2vVzOx-kT*3R zl4HK3?{iFChms-1oT3_*uw`&%K1KW@-@Fp`O!h?ir$?>vhS6hh2E|055L(mn9@9(` z66}KJ4DIbHHt1!J%U%ix9s9qw|HyG;G|{mYRH8_09t%-Lv2{reHDVZ6`uA(pb*Yyy zrRB@aw_zD22dpjh%;xJ%%0FL-e`j~@Vk8egjXYD5`Q?FmB>9Bh+F;|z8!tYh9u-!o za8PaUfvEQtIn{+!uKu(Qt*~>8Ec|oykXuYKjXknrPe>k~BZQ-0ZWPssX8z8!+%(2z zV|^Z180`)r%0j7_$5TT=&CTv_&bC5yDmz_UnXn%h@ydvPS3#qq21hNZEP2mpfW^Rk zW+U7fu(F2@K40pN?j(eSnBy0r?)Ml66V-hbZ*d|bok;e$2k*#t4y+DI!OFJy z>*U40n``96T;sKskwDPPY>GfC(Y&}ZhYzFmE9!F8(CY>YtIt6f#8imWm#4XyGZo~; zk%EQB_M}k#F+BO?LBQ52_X-CI!60aQK#mokGN0Uu1e+Cab=tnWEXKzqPTrDha>gs? z4Dr~kFIzZ`HLP5dJ%#rvOC;Nnd)qmNdq{mbaKZ@Rk3M>O>W(lAhfz{q^YW3x?+9)o`#n63YAN&d9Jif^6$DSukEk|zj$qJ=;cQAi`{rk~ z;R_lpVHN(?=JTJt4m16+PkSROx&9gtc0b>KAduVQ#m3+x4sQRYBXH)38iSs!x#lWo zFBGC>rQ|bH0<+Jbq3{~(xo1MV{BY4>v^&QdI;dEbb%QG>X`;t4P&xgrm^vQ%T!>2f zW^U%+lp_3h9)vR;Ld&hX)cU*6g2BfgFZ%Hbt?Fj#xW@WIMCz(!nW-t}W>>%5VWI!E zp~th+mWxlI#`!9T;18N#b*^QZ3MTeUwKa+e-0a@gW5k3 zFA(sUbV5DL@K3@?EDE93<$9W4x^Q+x5B>haDBh9&rXs8E9{h{$ivRXIydPn~blCGO z&x#hLJGK+c&sp{T?h{Fji*xo}1wP@2&TfTjKcW7$6C4P(;-$AVT+Q>y)3ot7zU_WX z83(SX$ID<;dtn{F_8l`_c8{FeIXiYZ?x<>^PDe}`GS5j(3`t8N?RM($+Y9^$x;9Uw~+xVm)7~ zLC#=hH^j^=xgxHwyW!t(yBn>FWEJtss_iGF}svDW; zGYxs=WN^~Cs6}?)hptwpsbHi^C@YJE8_)8w9RXt<92wxc^?%5~*yG97+r53@*GB>W zr?>w9-TEUJTkF4g0p+LusU7dWx1PWZ&V^(foDyLZd*sG>rKGFCR7R%d|HTyimogB` zvN%lt*NJL%8XFW=z3;XM9gI`BVSI`0$mhsXE~_@Ebb5omqv9c#bO$;k!|MNyCj9$l zQuKezGv+w{Yp*iBUcNU05uJIooY%q(;Jl;m*y?UCe%DIj+kjyJNk zI(eu2Nd8O2)Gigqsl~C$_)K+vqo%S;)nU-2v(;UMaTu@gE!jdpQ3^55uiMgRIHK_3$ff7Jw!e z{;CJjaVWU({%--RPTt%u@HYr|MqeBGEQxlUEpaDH#GRiVcL2xu{b+Ut+!&$pR64G3 z_0KM#h^D-Z*q?zqko>K0ccWIL;JeKOKl%)_rjS9(x19qnEO3+@B&%= zjEB#R-n4L$a$7gfMhQkKy&%{1%_usEU`IDxl%o%CtP*0K)OV1^`zzgIz))s-iTQRm zu$Z0=+0HDV0|#J>0r``Pg`E>gb+5AYuHt|s zT-%Sq>vv;AK2Rr4=;XzdMPY}52Q8PGtoY)_^*(70oB{}lS% z^nd#=QFFRj;;;PgDfhT`BofXh4I-z5uz(FIFawY_7PNTe`+=}stf&*wv%t+y5&wq` z0Gm)0$fc|`dz9jU1)m3;NX+Wbh2PBBR2_@jOVm)uBfB zgeghHh#IVzC9YktII(wUyTj#*{VyXq^rJn2aTr-;oZ^PvojrH4MBu&x+q8unj=;F$ zo5+Fc+^EVjIsGaI?7h>dL@ z$p8cR?1jtp0)0W&e9?ISAnW1Mp`7RJ)noNZfET4RI;%>b2xE305lR<}PCl%9I-FKs&z%E1S-l@v1CgV~dyNhCDETwfMGlp4@zTMkAGs9z zx0C;Ul-EDH$S3@dApmJG+V98es{2G;qY&Fq>pzhP^IaIjNYmex*K&T3GF=UZOoI!~ zgFxs>Gcc67VoU2*GfbMw!2ZntnVLWJ$46jKohD-ocvZCo;!2IK2RLPsiW3PerFop$ z{%Q+d3C`}O;!S^B#;R~OLONh%`kBRE49LZVc!pmZ!qSQfBg}XLe1hw+3Z*+%7Pz0�#@Z62Q>yf^LM5_h>KNVL8D3KY5 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 E11kJmVgLXD literal 0 HcmV?d00001 diff --git a/versions/55.0.0/_sources/contributor-guide/api-health.md.txt b/versions/55.0.0/_sources/contributor-guide/api-health.md.txt new file mode 100644 index 0000000000000..a20bc284cf362 --- /dev/null +++ b/versions/55.0.0/_sources/contributor-guide/api-health.md.txt @@ -0,0 +1,126 @@ + + +# API health policy + +DataFusion is used extensively as a library in other applications and has a +large public API. We try to keep the API well maintained and minimize breaking +changes to avoid issues for downstream users. + +## Breaking API Changes + +### What is the public Rust API and what is a breaking API change? + +An item is part of the public Rust API if it appears on the [docs.rs page]. + +Breaking changes _require_ users to modify their code for it to compile and +run, and are listed as "Major Changes" in the [SemVer Compatibility Section of +the Cargo Book]. Common examples include: + +- Adding new required parameters to a function (`foo(a: i32, b: i32)` -> `foo(a: i32, b: i32, c: i32)`) +- Removing a `pub` function +- Changing the return type of a function +- Adding a new function to a `trait` without a default implementation + +Examples of non-breaking changes include: + +- Marking a function as deprecated (`#[deprecated]`) +- Adding a new function to a `trait` with a default implementation + +### What is the public SQL API and what is a breaking SQL change? + +DataFusion is also used as a SQL engine, so changes to SQL semantics (the +results returned for a given query) are a form of breaking change. Even with +no Rust API change, altering the behavior of an existing SQL construct can +silently break downstream applications, dashboards, and tests. + +We apply the same caution to SQL semantics changes as to Rust API changes: +the benefit must be weighed against the cost of breaking downstream users. + +### When to make breaking API changes? + +When possible, we prefer to avoid making breaking API changes. One common way to +avoid such changes is to deprecate the old API, as described in the [Deprecation +Guidelines](#deprecation-guidelines) section below. + +If you do want to propose a breaking API change, we must weigh the benefits of the +change with the cost (impact on downstream users). It is often frustrating for +downstream users to change their applications, and it is even more so if they +do not gain improved capabilities. + +Examples of good reasons for a breaking API or SQL change: + +- It enables new use cases that were not possible before +- It significantly improves performance +- The previous behavior is clearly wrong (e.g. produces incorrect results) + +Examples of potentially weak reasons: + +- An internal refactor to make DataFusion more consistent +- Removing an API that is not widely used but has not been marked as deprecated +- Slightly improving compatibility with another database (for example, + PostgreSQL or DuckDB) + +### What to do when making breaking API changes? + +When making breaking Rust API changes, please: + +1. Add the `api-change` label so the change is highlighted in the release notes. +2. Document non-trivial changes in the version-specific [Upgrade Guide]. + +For breaking SQL changes, also describe the previous and new behavior in the PR +description, ideally including example queries and results where appropriate. +This makes review easier and helps downstream users discover the affected +semantics. + +[docs.rs page]: https://docs.rs/datafusion/latest/datafusion/index.html +[semver compatibility section of the cargo book]: https://doc.rust-lang.org/cargo/reference/semver.html#change-categories + +## Upgrade Guides + +When a change requires DataFusion users to modify their code as part of an +upgrade, please consider documenting it in the version-specific [Upgrade Guide]. + +[upgrade guide]: ../library-user-guide/upgrading/index.rst + +## Deprecation Guidelines + +When deprecating a method: + +- Mark the API as deprecated using `#[deprecated]` and specify the exact DataFusion version in which it was deprecated +- Concisely describe the preferred API to help the user transition + +The deprecated version is the next version that introduces the deprecation. For +example, if the current version listed in [`Cargo.toml`] is `43.0.0`, then the next +version will be `44.0.0`. + +[`cargo.toml`]: https://github.com/apache/datafusion/blob/main/Cargo.toml + +To mark the API as deprecated, use the `#[deprecated(since = "...", note = "...")]` attribute. + +For example: + +```rust +#[deprecated(since = "41.0.0", note = "Use new API instead")] +pub fn api_to_deprecated(a: usize, b: usize) {} +``` + +Deprecated methods will remain in the codebase for a period of 6 major versions or 6 months, whichever is longer, to provide users ample time to transition away from them. + +Please refer to [DataFusion releases](https://crates.io/crates/datafusion/versions) to plan API migration ahead of time. diff --git a/versions/55.0.0/_sources/contributor-guide/architecture.md.txt b/versions/55.0.0/_sources/contributor-guide/architecture.md.txt new file mode 100644 index 0000000000000..8197e0cd00a08 --- /dev/null +++ b/versions/55.0.0/_sources/contributor-guide/architecture.md.txt @@ -0,0 +1,91 @@ + + +# Architecture + +DataFusion's code structure and organization is described in the +[crates.io documentation], to keep it as close to the source as +possible. You can find the most up to date version in the [source code]. + +[crates.io documentation]: https://docs.rs/datafusion/latest/datafusion/index.html#architecture +[source code]: https://github.com/apache/datafusion/blob/main/datafusion/core/src/lib.rs + +## Forks vs Extension APIs + +DataFusion is a fast moving project, which results in frequent internal changes. +This benefits DataFusion by allowing it to evolve and respond quickly to +requests, but also means that maintaining a fork with major modifications +sometimes requires non trivial work. + +The public API (what is accessible if you use the DataFusion releases from +crates.io) is typically much more stable (though it does change from release to +release as well). + +Thus, rather than forks, we recommend using one of the many extension APIs (such +as `TableProvider`, `OptimizerRule`, or `ExecutionPlan`) to customize +DataFusion. If you can not do what you want with the existing APIs, we would +welcome you working with us to add new APIs to enable your use case, as +described in the next section. + +Please see the [Extensions] section to find out more about existing DataFusion +extensions and how to contribute your extension to the community. + +[extensions]: ../library-user-guide/extensions.md + +## Creating new Extension APIs + +DataFusion aims to be a general-purpose query engine, and thus the core crates +contain features that are useful for a wide range of use cases. Use case specific +functionality (such as very specific time series or stream processing features) +are typically implemented using the extension APIs. + +If you have a use case that is not covered by the existing APIs, we would love to +work with you to design a new general purpose API. There are often others who are +interested in similar extensions and the act of defining the API often improves +the code overall for everyone. + +Extension APIs that provide "safe" default behaviors are more likely to be +suitable for inclusion in DataFusion, while APIs that require major changes to +built-in operators are less likely. For example, it might make less sense +to add an API to support a stream processing feature if that would result in +slower performance for built-in operators. It may still make sense to add +extension APIs for such features, but leave implementation of such operators in +downstream projects. + +The process to create a new extension API is typically: + +- Look for an existing issue describing what you want to do, and file one if it + doesn't yet exist. +- Discuss what the API would look like. Feel free to ask contributors (via `@` + mentions) for feedback (you can find such people by looking at the most + recently changed PRs and issues) +- Prototype the new API, typically by adding an example (in + `datafusion-examples` or refactoring existing code) to show how it would work +- Create a PR with the new API, and work with the community to get it merged + +Some benefits of using an example based approach are + +- Any future API changes will also keep your example going ensuring no + regression in functionality +- There will be a blue print of any needed changes to your code if the APIs do change + (just look at what changed in your example) + +An example of this process was [creating a SQL Extension Planning API]. + +[creating a sql extension planning api]: https://github.com/apache/datafusion/issues/11207 diff --git a/versions/55.0.0/_sources/contributor-guide/architecture/dependency-graph.md.txt b/versions/55.0.0/_sources/contributor-guide/architecture/dependency-graph.md.txt new file mode 100644 index 0000000000000..be3502f48beda --- /dev/null +++ b/versions/55.0.0/_sources/contributor-guide/architecture/dependency-graph.md.txt @@ -0,0 +1,180 @@ + + +# Workspace Dependency Graph + +This page shows the dependency relationships between DataFusion's workspace +crates. This only includes internal dependencies, external crates like `Arrow` are not included + +The dependency graph is auto-generated by `docs/scripts/generate_dependency_graph.sh` to ensure it stays up-to-date, and the script now runs automatically as part of `docs/build.sh`. + +## Dependency Graph for Workspace Crates + + + +```{raw} html + + +``` + +### Legend + +- black lines: normal dependency +- blue lines: dev-dependency +- green lines: build-dependency +- dotted lines: optional dependency (could be removed by disabling a cargo feature) + +Transitive dependencies are intentionally ignored to keep the graph readable. + +The dependency graph is generated through `cargo depgraph` by `docs/scripts/generate_dependency_graph.sh`. diff --git a/versions/55.0.0/_sources/contributor-guide/communication.md.txt b/versions/55.0.0/_sources/contributor-guide/communication.md.txt new file mode 100644 index 0000000000000..b7d93d726a6bc --- /dev/null +++ b/versions/55.0.0/_sources/contributor-guide/communication.md.txt @@ -0,0 +1,106 @@ + + +# Community Communication + +We welcome participation from everyone and encourage you to join us, ask +questions, and get involved. +All participation in the Apache DataFusion project is governed by the +Apache Software Foundation's [code of +conduct](https://www.apache.org/foundation/policies/conduct.html). + +## GitHub + +The primary means of communication is the +[GitHub repository](https://github.com/apache/datafusion) in the form of issues, discussions, and Pull Requests. +Our repository is open to everyone. We encourage you to +participate by reporting issues, asking questions, and contributing code. + +## Chat + +We also use the Discord and Slack platforms for lower latency, informal discussions and coordination. +These are great places to +meet other members of the community, ask questions, and brainstorm ideas. +However, to ensure technical discussions are archived and accessible to everyone, +all technical designs are recorded and formalized in GitHub issues. + +### Discord + +Historically, the most active discussion forum has been the [Arrow Rust Discord +server][discord-link] which has specific channels for Arrow, DataFusion, and +DataFusion subprojects such as Ballista, Comet, Java, Python, etc. +DataFusion specific channels are prefixed with the `#datafusion-` tag. +We recommend new users join this server for real-time discussions with the community. + +### Slack + +Some of the community also uses the [ASF Slack workspace] for discussions. This +has historically been much less active than the Discord server. +Unfortunately, due to spammers, the ASF Slack workspace [requires an invitation] +to join. We are happy to invite any community member -- please ask for an +invitation in the Discord server. + +[asf slack workspace]: https://the-asf.slack.com/ +[requires an invitation]: https://s.apache.org/slack-invite + +In Slack, we use these channels: + +- `#arrow` +- `#arrow-rust` +- `#datafusion` +- `#datafusion-ballista` +- `#datafusion-comet` +- `#datafusion-python` + +## Weekly Video Call Syncs + +The DataFusion community also holds a weekly video call sync for real-time +discussion and coordination. You can join the meeting using the Google Meet +link below, and use the shared meeting document for agenda items and notes: + +- [Video call link](https://meet.google.com/nfg-eviu-qrm) +- [Meeting details and notes](https://docs.google.com/document/d/1NBpkIAuU7O9h8Br5CbFksDhX-L9TyO9wmGLPMe0Plc8) + +### Job Board + +There are plenty of opportunities to work with DataFusion advertised on the +#hiring channel on the [Arrow Rust Discord Server][discord-link]. +Please feel free to post links to DataFusion related jobs there. + +## Mailing Lists + +Like other Apache projects, we use [mailing lists] for certain purposes, most +importantly release coordination and announcing new committers and PMC members. +Other than these processes, most DataFusion mailing list traffic will link to a GitHub issue or PR where +the actual discussion occurs. The project mailing lists are: + +- [`dev@datafusion.apache.org`](mailto:dev@datafusion.apache.org): the main + mailing list for release coordination and other project-wide discussions. Links: + [archives](https://lists.apache.org/list.html?dev@datafusion.apache.org), + [subscribe](mailto:dev-subscribe@datafusion.apache.org), + [unsubscribe](mailto:dev-unsubscribe@datafusion.apache.org) +- `github@datafusion.apache.org`: read-only mailing list that receives all GitHub notifications for the project. Links: + [archives](https://lists.apache.org/list.html?github@datafusion.apache.org) +- `commits@datafusion.apache.org`: read-only mailing list that receives all GitHub commits for the project. Links: + [archives](https://lists.apache.org/list.html?commits@datafusion.apache.org) +- `private@datafusion.apache.org`: private mailing list for PMC members. This list has very little traffic, almost exclusively discussions on growing the committer and PMC membership. Links: + [archives](https://lists.apache.org/list.html?private@datafusion.apache.org) + +[mailing lists]: https://www.apache.org/foundation/mailinglists +[discord-link]: https://discord.gg/Qw5gKqHxUM diff --git a/versions/55.0.0/_sources/contributor-guide/development_environment.md.txt b/versions/55.0.0/_sources/contributor-guide/development_environment.md.txt new file mode 100644 index 0000000000000..2e4e00726580b --- /dev/null +++ b/versions/55.0.0/_sources/contributor-guide/development_environment.md.txt @@ -0,0 +1,132 @@ + + +# Development Environment + +This section describes how you can get started at developing DataFusion. + +## Quick Start + +For the fastest path to a working local environment, follow these steps +from the repository root: + +```shell +# 1. Install Rust (https://rust-lang.org/tools/install/) and verify the active toolchain with +rustup show + +# 2. Install protoc 3.15+ (see details below) +protoc --version + +# 3. Download test data used by examples and many tests +git submodule update --init --recursive + +# 4. Build the workspace +cargo build + +# 5. Verify that Rust integration tests can be run +cargo test -p datafusion --test parquet_integration + +# 6. Verify that sqllogictests can run +cargo test --profile=ci --test sqllogictests +``` + +Notes: + +- The pinned Rust version is defined in `rust-toolchain.toml`. +- `protoc` is required to compile DataFusion from source. +- Some tests and examples rely on git submodule data being present locally. + +## Windows Setup + +```shell +wget https://az792536.vo.msecnd.net/vms/VMBuild_20190311/VirtualBox/MSEdge/MSEdge.Win10.VirtualBox.zip +choco install -y git rustup.install visualcpp-build-tools +git-bash.exe +cargo build +``` + +## Dev Container setup + +DataFusion has support for [dev containers](https://containers.dev/) which may be used for +developing DataFusion in an isolated environment either locally or remote if desired. Using dev containers for developing +DataFusion is not a requirement but is available where doing local development could be tricky +such as with Windows and WSL2, those with older hardware, etc. + +For specific details on IDE support for dev containers see the documentation for [Visual Studio Code](https://code.visualstudio.com/docs/devcontainers/containers), +[IntelliJ IDEA](https://www.jetbrains.com/help/idea/connect-to-devcontainer.html), +[Rust Rover](https://www.jetbrains.com/help/rust/connect-to-devcontainer.html), and +[GitHub Codespaces](https://docs.github.com/en/codespaces/setting-up-your-project-for-codespaces/adding-a-dev-container-configuration/introduction-to-dev-containers). + +## `protoc` Installation + +Compiling DataFusion from sources requires an installed version of the protobuf compiler, `protoc`. + +On most platforms this can be installed from your system's package manager. For example: + +``` +# Ubuntu +$ sudo apt install -y protobuf-compiler + +# Fedora +$ dnf install -y protobuf-devel + +# Arch Linux +$ pacman -S protobuf + +# macOS +$ brew install protobuf +``` + +You will want to verify the version installed is `3.15` or greater, which has support for explicit [field presence](https://github.com/protocolbuffers/protobuf/blob/v3.15.0/docs/field_presence.md). Older versions may fail to compile. + +```shell +$ protoc --version +libprotoc 3.15.0 +``` + +Alternatively a binary release can be downloaded from the [Release Page](https://github.com/protocolbuffers/protobuf/releases) or [built from source](https://github.com/protocolbuffers/protobuf/blob/main/src/README.md). + +## Bootstrap Environment + +DataFusion is written in Rust and it uses a standard rust toolkit: + +- `rustup update stable` DataFusion generally uses the latest stable release of Rust, though it may lag when new Rust toolchains release + - See which toolchain is currently pinned in the [`rust-toolchain.toml`](https://github.com/apache/datafusion/blob/main/rust-toolchain.toml) file + - This can cause issues such as not having the rust-analyzer component installed for the specified toolchain, in which case just install it manually, e.g. `rustup component add --toolchain 1.97.0 rust-analyzer` +- `cargo build` +- `cargo fmt` to format the code +- etc. + +Testing setup: + +- `git submodule init` +- `git submodule update --init --remote --recursive` +- `cargo test` to run tests + +Note that running `cargo test` requires significant memory resources, due to cargo running many tests in parallel by default. If you run into issues with slow tests or system lock ups, you can significantly reduce the memory required by instead running `cargo test -- --test-threads=1`. For more information see [this issue](https://github.com/apache/datafusion/issues/5347). + +Formatting instructions: + +- [ci/scripts/rust_fmt.sh](../../../ci/scripts/rust_fmt.sh) +- [ci/scripts/rust_clippy.sh](../../../ci/scripts/rust_clippy.sh) +- [ci/scripts/rust_toml_fmt.sh](../../../ci/scripts/rust_toml_fmt.sh) + +or run them all at once: + +- [dev/rust_lint.sh](../../../dev/rust_lint.sh) diff --git a/versions/55.0.0/_sources/contributor-guide/governance.md.txt b/versions/55.0.0/_sources/contributor-guide/governance.md.txt new file mode 100644 index 0000000000000..c0208c9de1476 --- /dev/null +++ b/versions/55.0.0/_sources/contributor-guide/governance.md.txt @@ -0,0 +1,179 @@ + + +# Governance + +## Overview + +DataFusion is part of the [Apache Software Foundation] and is governed following +the [Apache Way] and [project management guidelines], [independently of +commercial interests]. + +[apache software foundation]: https://www.apache.org/ +[apache way]: https://www.apache.org/theapacheway/ +[project management guidelines]: https://www.apache.org/foundation/how-it-works.html#management +[independently of commercial interests]: https://community.apache.org/projectIndependence.html + +As much as practicable, we strive to make decisions by consensus, and anyone in +the community is encouraged to propose ideas, start discussions, and contribute +to the project. + +## People + +DataFusion is currently governed by the following individuals + + + + + +| Name | Apache ID | github | Affiliation | Role | +| ------------------------- | ---------------- | ------------------------------------------------------- | -------------- | --------- | +| Andrew Lamb | alamb | [alamb](https://github.com/alamb) | InfluxData | PMC Chair | +| Adrian Garcia Badaracco | adriangb | [adriangb](https://github.com/adriangb) | Pydantic | PMC | +| Andrew Grove | agrove | [andygrove](https://github.com/andygrove) | Apple | PMC | +| Mustafa Akur | akurmustafa | [akurmustafa](https://github.com/akurmustafa) | OHSU | PMC | +| Berkay Şahin | berkay | [berkaysynnada](https://github.com/berkaysynnada) | Synnada | PMC | +| Oleksandr Voievodin | comphead | [comphead](https://github.com/comphead) | Apple | PMC | +| Daniël Heres | dheres | [Dandandan](https://github.com/Dandandan) | | PMC | +| QP Hou | houqp | [houqp](https://github.com/houqp) | | PMC | +| Jie Wen | jakevin | [jackwener](https://github.com/jackwener) | | PMC | +| Jay Zhan | jayzhan | [jayzhan211](https://github.com/jayzhan211) | | PMC | +| Jeffrey Vo | jeffreyvo | [Jefffrey](https://github.com/Jefffrey) | | PMC | +| Jonah Gao | jonah | [jonahgao](https://github.com/jonahgao) | | PMC | +| Kun Liu | liukun | [liukun4515](https://github.com/liukun4515) | | PMC | +| Matt Butrovich | mbutrovich | [mbutrovich](https://github.com/mbutrovich) | Apple | PMC | +| Marko Milenković | milenkovicm | [milenkovicm](https://github.com/milenkovicm) | | PMC | +| Mehmet Ozan Kabak | ozankabak | [ozankabak](https://github.com/ozankabak) | Synnada, Inc | PMC | +| Tim Saucer | timsaucer | [timsaucer](https://github.com/timsaucer) | | PMC | +| L. C. Hsieh | viirya | [viirya](https://github.com/viirya) | Databricks | PMC | +| Ruihang Xia | wayne | [waynexia](https://github.com/waynexia) | Greptime | PMC | +| Wes McKinney | wesm | [wesm](https://github.com/wesm) | Posit | PMC | +| Will Jones | wjones127 | [wjones127](https://github.com/wjones127) | LanceDB | PMC | +| Xudong Wang | xudong963 | [xudong963](https://github.com/xudong963) | Polygon.io | PMC | +| Yongting You | ytyou | [2010YOUY01](https://github.com/2010YOUY01) | Independent | PMC | +| Brent Gardner | avantgardner | [avantgardnerio](https://github.com/avantgardnerio) | Coralogix | Committer | +| Bhargava Vadlamani | bhargava | [coderfender](https://github.com/coderfender) | | Committer | +| Dmitrii Blaginin | blaginin | [blaginin](https://github.com/blaginin) | SpiralDB | Committer | +| Piotr Findeisen | findepi | [findepi](https://github.com/findepi) | dbt Labs | Committer | +| Gabriel Musat | gabotechs | [gabotechs](https://github.com/gabotechs) | DataDog | Committer | +| Jax Liu | goldmedal | [goldmedal](https://github.com/goldmedal) | Canner | Committer | +| Huaxin Gao | huaxingao | [huaxingao](https://github.com/huaxingao) | | Committer | +| Ifeanyi Ubah | iffyio | [iffyio](https://github.com/iffyio) | Validio | Committer | +| Liu Jiayu | jiayuliu | [jimexist](https://github.com/jimexist) | | Committer | +| Ruiqiu Cao | kamille | [Rachelint](https://github.com/Rachelint) | Tencent | Committer | +| Kazuyuki Tanimura | kazuyukitanimura | [kazuyukitanimura](https://github.com/kazuyukitanimura) | | Committer | +| Eduard Karacharov | korowa | [korowa](https://github.com/korowa) | | Committer | +| Siew Kam Onn | kosiew | [kosiew](https://github.com/kosiew) | | Committer | +| Kumar Ujjawal | kumarujjawal | [kumarUjjawal](https://github.com/kumarUjjawal) | | Committer | +| Lewis Zhang | linwei | [lewiszlw](https://github.com/lewiszlw) | diit.cn | Committer | +| Metehan Yildirim | mete | [metegenez](https://github.com/metegenez) | | Committer | +| Martin Tzvetanov Grigorov | mgrigorov | [martin-g](https://github.com/martin-g) | | Committer | +| Wang Mingming | mingmwang | [mingmwang](https://github.com/mingmwang) | | Committer | +| Michael Ward | mjward | [Michael-J-Ward ](https://github.com/Michael-J-Ward) | | Committer | +| Marco Neumann | mneumann | [crepererum](https://github.com/crepererum) | InfluxData | Committer | +| Neil Conway | neilc | [neilconway](https://github.com/neilconway) | | Committer | +| Zhong Yanghong | nju_yaho | [yahoNanJing](https://github.com/yahoNanJing) | | Committer | +| Nuno Faria | nunofaria | [nuno-faria](https://github.com/nuno-faria) | | Committer | +| Paddy Horan | paddyhoran | [paddyhoran](https://github.com/paddyhoran) | Assured Allies | Committer | +| Parth Chandra | parthc | [parthchandra](https://github.com/parthchandra) | Apple | Committer | +| Rémi Dettai | rdettai | [rdettai](https://github.com/rdettai) | | Committer | +| Raz Luvaton | rluvaton | [rluvaton](https://github.com/rluvaton) | | Committer | +| Chao Sun | sunchao | [sunchao](https://github.com/sunchao) | OpenAI | Committer | +| Daniel Harris | thinkharderdev | [thinkharderdev](https://github.com/thinkharderdev) | Coralogix | Committer | +| Raphael Taylor-Davies | tustvold | [tustvold](https://github.com/tustvold) | | Committer | +| Zhen Wang | wangzhen | [wForget](https://github.com/wForget) | | Committer | +| Weijun Huang | weijun | [Weijun-H](https://github.com/Weijun-H) | OrbDB | Committer | +| Yang Jiang | yangjiang | [Ted-jiang](https://github.com/Ted-jiang) | Ebay | Committer | +| Yoav Cohen | ycohen | [yoavcloud](https://github.com/yoavcloud) | | Committer | +| Yijie Shen | yjshen | [yjshen](https://github.com/yjshen) | DataPelago | Committer | +| Qi Zhu | zhuqi | [zhuqi-lucas](https://github.com/zhuqi-lucas) | Polygon.io | Committer | + + + +Note that the authoritative list of PMC and committers is the [Apache Phonebook] + +[apache phonebook]: https://projects.apache.org/committee.html?datafusion + +## Roles + +- **Contributors**: Anyone who contributes to the project, whether it be code, + documentation, testing, issue reports, code, or some other forms. + +- **Committers**: Contributors who have been granted write access to the + project's source code repository. Committers are responsible for reviewing and + merging pull requests. Committers are chosen by the PMC. + +- **Project Management Committee (PMC)**: The PMC is responsible for the + oversight of the project. The PMC is responsible for making decisions about the + project, including the addition of new committers and PMC members. The PMC is + also responsible for [voting] on releases and ensuring that the project follows + the [Apache Way]. + +[voting]: https://www.apache.org/foundation/voting.html + +## Becoming a Committer + +Contributors with sustained, high-quality activity may be invited to become +committers by the PMC as a recognition of their contribution to the project and +their shared commitment. Committers have the significant responsibility of using +their status and access to improve the project for the entire community. + +When considering inviting someone to be a committer, the PMC looks for +contributors who are already doing the work and exercising the judgment expected +of a committer. After all, any contributor can do all of the things a committer +does except for merge a PR. While there is no set list of requirements, nor a +checklist that entitles one to commit privileges, typical behaviors include: + +- Contributions beyond pull requests, such as reviewing other pull requests, + fixing bugs and documentation, triaging issues, answering community questions, + improving usability, helping with CI, verifying releases, etc. + +- Contributions that are consistent in quality and sustained + over time, typically on the order of 6 months or more. + +- Assistance growing the size and health of the community via constructive, + respectful, and consensus driven interactions, as described in our [Code of + Conduct] and the [Apache Way]. + +If you feel you should be offered committer privileges, but have not been, you +can reach out to one of the PMC members or the private@datafusion.apache.org mailing +list. + +[code of conduct]: https://www.apache.org/foundation/policies/conduct.html + +## Becoming a PMC Member + +Committers with long term sustained contributions to the project may be invited +to join the PMC. This is a recognition of a significant contribution to growing +the community, improving the project, and helping to guide the project's +direction, typically over the course of a year or more. diff --git a/versions/55.0.0/_sources/contributor-guide/gsoc/gsoc_application_guidelines_2025.md.txt b/versions/55.0.0/_sources/contributor-guide/gsoc/gsoc_application_guidelines_2025.md.txt new file mode 100644 index 0000000000000..c127b4231b8e1 --- /dev/null +++ b/versions/55.0.0/_sources/contributor-guide/gsoc/gsoc_application_guidelines_2025.md.txt @@ -0,0 +1,105 @@ +# GSoC Application Guidelines (2025) + +## Introduction + +Welcome to the Apache DataFusion Google Summer of Code (GSoC) application guidelines. We are excited to support contributors who are passionate about open-source data processing technologies and eager to contribute to DataFusion. This document provides detailed instructions on how to apply, what we expect from applicants, and how you can increase your chances of selection. + +## Why Contribute to Apache DataFusion? + +Apache DataFusion is a high-performance, extensible query engine for data processing, written in Rust and designed for modern analytical workloads. GSoC offers a fantastic opportunity for students and early-career developers to work with experienced mentors, learn about open-source development, and make meaningful contributions. + +## Prerequisites + +Before applying, ensure you: + +- Have read and understood the [Apache DataFusion Contributor Guide](https://datafusion.apache.org/contributor-guide/index.html). +- Have basic familiarity with Rust programming and SQL-based data processing. +- Have explored DataFusion’s GitHub repository and tried running sample queries. +- Have introduced yourself on our mailing list or Discord to discuss project ideas with potential mentors. + +## Application Process + +To apply, follow these steps: + +1. **Choose a Project Idea** + - Review the list of proposed GSoC projects for Apache DataFusion. + - If you have your own project idea, discuss it with potential mentors before submitting your proposal. +2. **Engage with the Community** + - Join our [mailing list](mailto:dev@datafusion.apache.org) and [Discord](https://discord.gg/jHzkpK4em5) to introduce yourself and ask questions. + - Optional: Submit a small pull request (PR) for an issue marked with the **good first issue** tag to understand/test whether you enjoy working on Apache DataFusion, get comfortable with navigating the codebase and demonstrate your ability. +3. **Write a Clear Proposal** + - You can use the template below to structure your proposal. + - Ensure it is has sufficient details and is feasible. + - Seek feedback from mentors before submission. + +## Application Template + +``` +# Apache DataFusion GSoC Application + +## Personal Information + +- **Name:** +- **GitHub ID:** +- **Email:** +- **LinkedIn/Personal Website (if any):** +- **Time Zone & Available Hours Per Week:** + +## Project Proposal + +### Title + +Provide a concise and descriptive project title. + +### Synopsis + +Summarize the project in a few sentences. What problem does it solve? Why is it important? If you choose an idea proposed by us, this can simply be a summary of your research on the problem and/or your understanding of it. + +### Benefits to the Community + +Explain how this project will improve Apache DataFusion and its ecosystem. If you choose an idea proposed by us, this can simply be a summary of your understanding of potential benefits. + +### Deliverables & Milestones + +Consult with project mentors to come up with a rough roadmap for what you plan to accomplish, ensuring it aligns with GSoC’s timeline. + +### Technical Details + +Discuss the technologies, tools, and methodologies you plan to use. Mention any potential challenges and how you plan to address them. + +### Related Work & References + +List any relevant research, documentation, or prior work that informs your proposal. + +## Personal Experience + +### Relevant Skills & Background + +Describe your experience with Rust, databases, and open-source contributions. + +### Past Open-Source Contributions + +List any prior contributions (links to PRs, issues, repositories). + +### Learning Plan + +Explain how you will learn new skills required for this project. + +## Mentor & Communication + +- **Preferred Communication Channels:** (Email, Discord, etc.) +- **Weekly Progress Updates Plan:** Describe how you plan to remain in sync with your mentor(s). + +## Additional Information + +Add anything else you believe strengthens your application. + +``` + +## Final Steps + +- Review your proposal for clarity and completeness. +- Submit your proposal via the GSoC portal before the deadline. +- Stay active in the community and be ready to discuss your application with mentors. + +We look forward to your application and your contributions to Apache DataFusion! diff --git a/versions/55.0.0/_sources/contributor-guide/gsoc/gsoc_project_ideas_2025.md.txt b/versions/55.0.0/_sources/contributor-guide/gsoc/gsoc_project_ideas_2025.md.txt new file mode 100644 index 0000000000000..d81d9eb9adab5 --- /dev/null +++ b/versions/55.0.0/_sources/contributor-guide/gsoc/gsoc_project_ideas_2025.md.txt @@ -0,0 +1,112 @@ +# GSoC Project Ideas (2025) + +## Introduction + +Welcome to the Apache DataFusion Google Summer of Code (GSoC) 2025 project ideas list. Below you can find information about the projects. Please refer to [this page](https://datafusion.apache.org/contributor-guide/gsoc_application_guidelines.html) for application guidelines. + +## Projects + +### [Implement Continuous Monitoring of DataFusion Performance](https://github.com/apache/datafusion/issues/5504) + +- **Description and Outcomes:** DataFusion lacks continuous monitoring of how performance evolves over time -- we do this somewhat manually today. Even though performance has been one of our top priorities for a while now, we didn't build a continuous monitoring system yet. This linked issue contains a summary of all the previous efforts that made us inch closer to having such a system, but a functioning system needs to built on top of that progress. A student successfully completing this project would gain experience in building an end-to-end monitoring system that integrates with GitHub, scheduling/running benchmarks on some sort of a cloud infrastructure, and building a versatile web UI to expose the results. The outcome of this project will benefit Apache DataFusion on an ongoing basis in its quest for ever-more performance. +- **Category:** Tooling +- **Difficulty:** Medium +- **Possible Mentor(s) and/or Helper(s):** [alamb](https://github.com/alamb) and [mertak-synnada](https://github.com/mertak-synnada) +- **Skills:** DevOps, Cloud Computing, Web Development, Integrations +- **Expected Project Size:** 175 to 350 hours\* + +### [Supporting Correlated Subqueries](https://github.com/apache/datafusion/issues/5483) + +- **Description and Outcomes:** Correlated subqueries are an important SQL feature that enables some users to express their business logic more intuitively without thinking about "joins". Even though DataFusion has decent join support, it doesn't fully support correlated subqueries. The linked epic contains bite-size pieces of the steps necessary to achieve full support. For students interested in internals of data systems and databases, this project is a good opportunity to apply and/or improve their computer science knowledge. The experience of adding such a feature to a widely-used foundational query engine can also serve as a good opportunity to kickstart a career in the area of databases and data systems. +- **Category:** Core +- **Difficulty:** Advanced +- **Possible Mentor(s) and/or Helper(s):** [jayzhan-synnada](https://github.com/jayzhan-synnada) and [xudong963](https://github.com/xudong963) +- **Skills:** Databases, Algorithms, Data Structures, Testing Techniques +- **Expected Project Size:** 350 hours + +### Improving DataFusion DX (e.g. [1](https://github.com/apache/datafusion/issues/9371) and [2](https://github.com/apache/datafusion/issues/14429)) + +- **Description and Outcomes:** While performance, extensibility and customizability is DataFusion's strong aspects, we have much work to do in terms of user-friendliness and ease of debug-ability. This project aims to make strides in these areas by improving terminal visualizations of query plans and increasing the "deployment" of the newly-added diagnostics framework. This project is a potential high-impact project with high output visibility, and reduce the barrier to entry to new users. +- **Category:** DX +- **Difficulty:** Medium +- **Possible Mentor(s) and/or Helper(s):** [eliaperantoni](https://github.com/eliaperantoni) and [mkarbo](https://github.com/mkarbo) +- **Skills:** Software Engineering, Terminal Visualizations +- **Expected Project Size:** 175 to 350 hours\* + +### [Robust WASM Support](https://github.com/apache/datafusion/issues/13815) + +- **Description and Outcomes:** DataFusion can be compiled today to WASM with some care. However, it is somewhat tricky and brittle. Having robust WASM support improves the _embeddability_ aspect of DataFusion, and can enable many practical use cases. A good conclusion of this project would be the addition of a live demo sub-page to the DataFusion homepage. +- **Category:** Build +- **Difficulty:** Medium +- **Possible Mentor(s) and/or Helper(s):** [alamb](https://github.com/alamb) and [waynexia](https://github.com/waynexia) +- **Skills:** WASM, Advanced Rust, Web Development, Software Engineering +- **Expected Project Size:** 175 to 350 hours\* + +### [High Performance Aggregations](https://github.com/apache/datafusion/issues/7000) + +- **Description and Outcomes:** An aggregation is one of the most fundamental operations within a query engine. Practical performance in many use cases, and results in many well-known benchmarks (e.g. [ClickBench](https://benchmark.clickhouse.com/)), depend heavily on aggregation performance. DataFusion community has been working on improving aggregation performance for a while now, but there is still work to do. A student working on this project will get the chance to hone their skills on high-performance, low(ish) level coding, intricacies of measuring performance, data structures and others. +- **Category:** Core +- **Difficulty:** Advanced +- **Possible Mentor(s) and/or Helper(s):** [jayzhan-synnada](https://github.com/jayzhan-synnada) and [Rachelint](https://github.com/Rachelint) +- **Skills:** Algorithms, Data Structures, Advanced Rust, Databases, Benchmarking Techniques +- **Expected Project Size:** 350 hours + +### [Improving Python Bindings](https://github.com/apache/datafusion-python) + +- **Description and Outcomes:** DataFusion offers Python bindings that enable users to build data systems using Python. However, the Python bindings are still relatively low-level, and do not expose all APIs libraries like [Pandas](https://pandas.pydata.org/) and [Polars](https://pola.rs/) with a end-user focus offer. This project aims to improve DataFusion's Python bindings to make progress towards moving it closer to such libraries in terms of built-in APIs and functionality. +- **Category:** Python Bindings +- **Difficulty:** Medium +- **Possible Mentor(s) and/or Helper(s):** [timsaucer](https://github.com/timsaucer) +- **Skills:** APIs, FFIs, DataFrame Libraries +- **Expected Project Size:** 175 to 350 hours\* + +### [Optimizing DataFusion Binary Size](https://github.com/apache/datafusion/issues/13816) + +- **Description and Outcomes:** DataFusion is a foundational library with a large feature set. Even though we try to avoid adding too many dependencies and implement many low-level functionalities inside the codebase, the fast moving nature of the project results in an accumulation of dependencies over time. This inflates DataFusion's binary size over time, which reduces portability and embeddability. This project involves a study of the codebase, using compiler tooling, to understand where code bloat comes from, simplifying/reducing the number of dependencies by efficient in-house implementations, and avoiding code duplications. +- **Category:** Core/Build +- **Difficulty:** Medium +- **Possible Mentor(s) and/or Helper(s):** [comphead](https://github.com/comphead) and [alamb](https://github.com/alamb) +- **Skills:** Software Engineering, Refactoring, Dependency Management, Compilers +- **Expected Project Size:** 175 to 350 hours\* + +### [Ergonomic SQL Features](https://github.com/apache/datafusion/issues/14514) + +- **Description and Outcomes:** [DuckDB](https://duckdb.org/) has many innovative features that significantly improve the SQL UX. Even though some of those features are already implemented in DataFusion, there are many others we can implement (and get inspiration from). [This page](https://duckdb.org/docs/sql/dialect/friendly_sql.html) contains a good summary of such features. Each such feature will serve as a bite-size, achievable milestone for a cool GSoC project that will have user-facing impact improving the UX on a broad basis. The project will start with a survey of what is already implemented, what is missing, and kick off with a prioritization proposal/implementation plan. +- **Category:** SQL FE +- **Difficulty:** Medium +- **Possible Mentor(s) and/or Helper(s):** [berkaysynnada](https://github.com/berkaysynnada) +- **Skills:** SQL, Planning, Parsing, Software Engineering +- **Expected Project Size:** 350 hours + +### [Advanced Interval Analysis](https://github.com/apache/datafusion/issues/14515) + +- **Description and Outcomes:** DataFusion implements interval arithmetic and utilizes it for range estimations, which enables use cases in data pruning, optimizations and statistics. However, the current implementation only works efficiently for forward evaluation; i.e. calculating the output range of an expression given input ranges (ranges of columns). When propagating constraints using the same graph, the current approach requires multiple bottom-up and top-down traversals to narrow column bounds fully. This project aims to fix this deficiency by utilizing a better algorithmic approach. Note that this is a _very advanced_ project for students with a deep interest in computational methods, expression graphs, and constraint solvers. +- **Category:** Core +- **Difficulty:** Advanced +- **Possible Mentor(s) and/or Helper(s):** [ozankabak](https://github.com/ozankabak) and [berkaysynnada](https://github.com/berkaysynnada) +- **Skills:** Algorithms, Data Structures, Applied Mathematics, Software Engineering +- **Expected Project Size:** 350 hours + +### [Spark-Compatible Functions Crate](https://github.com/apache/datafusion/issues/5600) + +- **Description and Outcomes:** In general, DataFusion aims to be compatible with PostgreSQL in terms of functions and behaviors. However, there are many users (and downstream projects, such as [DataFusion Comet](https://datafusion.apache.org/comet/)) that desire compatibility with [Apache Spark](https://spark.apache.org/). This project aims to collect Spark-compatible functions into a separate crate to help such users and/or projects. The project will be an exercise in creating the right APIs, explaining how to use them, and then telling the world about them (e.g. via creating a compatibility-tracking page cataloging such functions, writing blog posts etc.). +- **Category:** Extensions +- **Difficulty:** Medium +- **Possible Mentor(s) and/or Helper(s):** [alamb](https://github.com/alamb) and [andygrove](https://github.com/andygrove) +- **Skills:** SQL, Spark, Software Engineering +- **Expected Project Size:** 175 to 350 hours\* + +### [SQL Fuzzing Framework in Rust](https://github.com/apache/datafusion/issues/14535) + +- **Description and Outcomes:** Fuzz testing is a very important technique we utilize often in DataFusion. Having SQL-level fuzz testing enables us to battle-test DataFusion in an end-to-end fashion. Initial version of our fuzzing framework is Java-based, but the time has come to migrate to Rust-native solution. This will simplify the overall implementation (by avoiding things like JDBC), enable us to implement more advanced algorithms for query generation, and attract more contributors over time. This project is a good blend of software engineering, algorithms and testing techniques (i.e. fuzzing techniques). +- **Category:** Extensions +- **Difficulty:** Advanced +- **Possible Mentor(s) and/or Helper(s):** [2010YOUY01](https://github.com/2010YOUY01) +- **Skills:** SQL, Testing Techniques, Advanced Rust, Software Engineering +- **Expected Project Size:** 175 to 350 hours\* + +\*_There is enough material to make this a 350-hour project, but it is granular enough to make it a 175-hour project as well._ + +## Contact Us + +You can join our [mailing list](mailto:dev%40datafusion.apache.org) and [Discord](https://discord.gg/jHzkpK4em5) to introduce yourself and ask questions. diff --git a/versions/55.0.0/_sources/contributor-guide/gsoc/index.rst.txt b/versions/55.0.0/_sources/contributor-guide/gsoc/index.rst.txt new file mode 100644 index 0000000000000..10b0013e9b169 --- /dev/null +++ b/versions/55.0.0/_sources/contributor-guide/gsoc/index.rst.txt @@ -0,0 +1,36 @@ +.. 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. + +Google Summer of Code (GSOC) +============================ + +DataFusion has participated in +`Google Summer of Code (GSOC) `_ +since 2025. GSOC is a global program that offers students stipends to +write code for open source projects. + +If you are a interested in contributing to DataFusion, we encourage you +to apply. You can find more information about the application process and +project ideas in the sections below. + + +.. toctree:: + :maxdepth: 1 + + gsoc_application_guidelines_2025 + gsoc_project_ideas_2025 + diff --git a/versions/55.0.0/_sources/contributor-guide/howtos.md.txt b/versions/55.0.0/_sources/contributor-guide/howtos.md.txt new file mode 100644 index 0000000000000..18d9391d24bbe --- /dev/null +++ b/versions/55.0.0/_sources/contributor-guide/howtos.md.txt @@ -0,0 +1,190 @@ + + +# HOWTOs + +## How to update the version of Rust used in CI tests + +Make a PR to update the [rust-toolchain] file in the root of the repository. + +[rust-toolchain]: https://github.com/apache/datafusion/blob/main/rust-toolchain.toml + +## Adding new functions + +**Implementation** + +| Function type | Location to implement | Trait to implement | Macros to use | Example | +| ------------- | ------------------------- | ---------------------------------------------- | ------------------------------------------------ | -------------------- | +| Scalar | [functions][df-functions] | [`ScalarUDFImpl`] | `make_udf_function!()` and `export_functions!()` | [`advanced_udf.rs`] | +| Nested | [functions-nested] | [`ScalarUDFImpl`] | `make_udf_expr_and_func!()` | | +| Aggregate | [functions-aggregate] | [`AggregateUDFImpl`] and an [`Accumulator`] | `make_udaf_expr_and_func!()` | [`advanced_udaf.rs`] | +| Window | [functions-window] | [`WindowUDFImpl`] and a [`PartitionEvaluator`] | `define_udwf_and_expr!()` | [`advanced_udwf.rs`] | +| Table | [functions-table] | [`TableFunctionImpl`] and a [`TableProvider`] | `create_udtf_function!()` | [`simple_udtf.rs`] | + +- The macros are to simplify some boilerplate such as ensuring a DataFrame API compatible function is also created +- Ensure new functions are properly exported through the subproject + `mod.rs` or `lib.rs`. +- Functions should preferably provide documentation via the `#[user_doc(...)]` attribute so their documentation + can be included in the SQL reference documentation (see below section) +- Scalar functions are further grouped into modules for families of functions (e.g. string, math, datetime). + Functions should be added to the relevant module; if a new module needs to be created then a new [Rust feature] + should also be added to allow DataFusion users to conditionally compile the modules as needed +- Aggregate functions can optionally implement a [`GroupsAccumulator`] for better performance + +Spark compatible functions are [located in separate crate][df-spark] but otherwise follow the same steps, though all +function types (e.g. scalar, nested, aggregate) are grouped together in the single location. + +[df-functions]: https://github.com/apache/datafusion/tree/main/datafusion/functions +[functions-nested]: https://github.com/apache/datafusion/tree/main/datafusion/functions-nested +[functions-aggregate]: https://github.com/apache/datafusion/tree/main/datafusion/functions-aggregate +[functions-window]: https://github.com/apache/datafusion/tree/main/datafusion/functions-window +[functions-table]: https://github.com/apache/datafusion/tree/main/datafusion/functions-table +[df-spark]: https://github.com/apache/datafusion/tree/main/datafusion/spark +[`scalarudfimpl`]: https://docs.rs/datafusion/latest/datafusion/logical_expr/trait.ScalarUDFImpl.html +[`aggregateudfimpl`]: https://docs.rs/datafusion/latest/datafusion/logical_expr/trait.AggregateUDFImpl.html +[`accumulator`]: https://docs.rs/datafusion/latest/datafusion/logical_expr/trait.Accumulator.html +[`groupsaccumulator`]: https://docs.rs/datafusion/latest/datafusion/logical_expr/trait.GroupsAccumulator.html +[`windowudfimpl`]: https://docs.rs/datafusion/latest/datafusion/logical_expr/trait.WindowUDFImpl.html +[`partitionevaluator`]: https://docs.rs/datafusion/latest/datafusion/logical_expr/trait.PartitionEvaluator.html +[`tablefunctionimpl`]: https://docs.rs/datafusion/latest/datafusion/catalog/trait.TableFunctionImpl.html +[`tableprovider`]: https://docs.rs/datafusion/latest/datafusion/catalog/trait.TableProvider.html +[`advanced_udf.rs`]: https://github.com/apache/datafusion/blob/main/datafusion-examples/examples/udf/advanced_udf.rs +[`advanced_udaf.rs`]: https://github.com/apache/datafusion/blob/main/datafusion-examples/examples/udf/advanced_udaf.rs +[`advanced_udwf.rs`]: https://github.com/apache/datafusion/blob/main/datafusion-examples/examples/udf/advanced_udwf.rs +[`simple_udtf.rs`]: https://github.com/apache/datafusion/blob/main/datafusion-examples/examples/udf/simple_udtf.rs +[rust feature]: https://doc.rust-lang.org/cargo/reference/features.html + +**Testing** + +Prefer adding `sqllogictest` integration tests where the function is called via SQL against +well known data and returns an expected result. See the existing [test files][slt-test-files] if +there is an appropriate file to add test cases to, otherwise create a new file. See the +[`sqllogictest` documentation][slt-readme] for details on how to construct these tests. +Ensure edge case, `null` input cases are considered in these tests. + +If a behaviour cannot be tested via `sqllogictest` (e.g. testing `simplify()`, needs to be +tested in isolation from the optimizer, difficult to construct exact input via `sqllogictest`) +then tests can be added as Rust unit tests in the implementation module, though these should be +kept minimal where possible + +[slt-test-files]: https://github.com/apache/datafusion/tree/main/datafusion/sqllogictest/test_files +[slt-readme]: https://github.com/apache/datafusion/blob/main/datafusion/sqllogictest/README.md + +**Documentation** + +Run documentation update script `./dev/update_function_docs.sh` which will update the relevant +markdown document [here][fn-doc-home] (see the documents for [scalar][fn-doc-scalar], +[aggregate][fn-doc-aggregate] and [window][fn-doc-window] functions) + +- You _should not_ manually update the markdown document after running the script as those manual + changes would be overwritten on next execution +- Reference [GitHub issue] which introduced this behaviour + +[fn-doc-home]: https://github.com/apache/datafusion/blob/main/docs/source/user-guide/sql +[fn-doc-scalar]: https://github.com/apache/datafusion/blob/main/docs/source/user-guide/sql/scalar_functions.md +[fn-doc-aggregate]: https://github.com/apache/datafusion/blob/main/docs/source/user-guide/sql/aggregate_functions.md +[fn-doc-window]: https://github.com/apache/datafusion/blob/main/docs/source/user-guide/sql/window_functions.md +[github issue]: https://github.com/apache/datafusion/issues/12740 + +## How to display plans graphically + +The query plans represented by `LogicalPlan` nodes can be graphically +rendered using [Graphviz](https://www.graphviz.org/). + +To do so, save the output of the `display_graphviz` function to a file.: + +```rust +// Create plan somehow... +let mut output = File::create("/tmp/plan.dot")?; +write!(output, "{}", plan.display_graphviz()); +``` + +Then, use the `dot` command line tool to render it into a file that +can be displayed. For example, the following command creates a +`/tmp/plan.pdf` file: + +```bash +dot -Tpdf < /tmp/plan.dot > /tmp/plan.pdf +``` + +## How to format `.md` documents + +We use [`prettier`] to format `.md` files. + +You can either use `npm i -g prettier` to install it globally or use `npx` to run it as a standalone binary. +Using `npx` requires a working node environment. Upgrading to the latest prettier is recommended (by adding +`--upgrade` to the `npm` command). + +```bash +$ prettier --version +2.3.0 +``` + +After you've confirmed your prettier version, you can format all the `.md` files: + +```bash +prettier -w {datafusion,datafusion-cli,datafusion-examples,dev,docs}/**/*.md +``` + +[`prettier`]: https://prettier.io/ + +## How to format `.toml` files + +We use [`taplo`] to format `.toml` files. + +To install via cargo: + +```sh +cargo install taplo-cli --locked +``` + +> Refer to the [taplo installation documentation][taplo-install] for other ways to install it. + +```bash +$ taplo --version +taplo 0.9.0 +``` + +After you've confirmed your `taplo` version, you can format all the `.toml` files: + +```bash +taplo fmt +``` + +[`taplo`]: https://taplo.tamasfe.dev/ +[taplo-install]: https://taplo.tamasfe.dev/cli/installation/binary.html + +## How to update protobuf/gen dependencies + +For the `proto` and `proto-common` crates, the prost/tonic code is generated by running their respective `./regen.sh` scripts, +which in turn invokes the Rust binary located in `./gen`. + +This is necessary after modifying the protobuf definitions or altering the dependencies of `./gen`, and requires a +valid installation of [protoc] (see [installation instructions] for details). + +```bash +# From repository root +# proto-common +./datafusion/proto-common/regen.sh +# proto +./datafusion/proto/regen.sh +``` + +[protoc]: https://github.com/protocolbuffers/protobuf#protocol-compiler-installation +[installation instructions]: https://datafusion.apache.org/contributor-guide/development_environment.html#protoc-installation diff --git a/versions/55.0.0/_sources/contributor-guide/index.md.txt b/versions/55.0.0/_sources/contributor-guide/index.md.txt new file mode 100644 index 0000000000000..6f1a0f1c19907 --- /dev/null +++ b/versions/55.0.0/_sources/contributor-guide/index.md.txt @@ -0,0 +1,244 @@ + + +# Introduction + +We welcome and encourage contributions of all kinds, from all levels, such as: + +1. Tickets with issue reports or feature requests +2. Discussions +3. Documentation improvements +4. Code, both PR and (especially) PR Review. + +In addition to submitting new PRs, we have a healthy tradition of community +members reviewing each other's PRs. Doing so is a great way to help the +community as well as get more familiar with Rust and the relevant codebases. + +## Development Environment + +Start with the [Development Environment Quick Start](development_environment.md#quick-start). + +For more detail, see the full [development environment guide](development_environment.md) +and the [testing guide](testing.md). + +## Finding and Creating Issues to Work On + +You can find a curated [good-first-issue] list to help you get started. +You can read about how we plan larger projects in the [Roadmap and Improvement Proposals](roadmap.md) section. + +[good-first-issue]: https://github.com/apache/datafusion/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22 + +### Open Contribution and Assigning tickets + +DataFusion is an open contribution project, and thus there is no particular +project imposed deadline for completing issues or restrictions on who can +work on an issue, nor limits to how many people can work on an issue at the same time. + +Contributors drive the project forward based on their own priorities and +interests and thus you are free to work on any issue that interests you. + +If someone is already working on an issue that you want or need but hasn't +been able to finish it yet, you should feel free to work on it as well. In +general it is both polite and will help avoid unnecessary duplication of work if +you leave a note on an issue when you start working on it. + +If you want to work on an issue which is not already assigned to someone else +and there are no comment indicating that someone is already working on that +issue then you can assign the issue to yourself by submitting a single word +comment `take`. This will assign the issue to yourself. However, if you are +unable to make progress you should unassign the issue by commenting a single +word `untake`. + +# Developer's guide + +## Pull Request Overview + +We welcome pull requests (PRs) from anyone in the community. + +DataFusion is a rapidly evolving project and we try to review and merge PRs quickly. + +Review bandwidth is currently our most limited resource, and we highly encourage reviews by the broader community. If you are waiting for your PR to be reviewed, consider helping review other PRs that are waiting. Such review both helps the reviewer to learn the codebase and become more expert, as well as helps identify issues in the PR (such as lack of test coverage), that can be addressed and make future reviews faster and more efficient. + +The lifecycle of a PR is: + +1. Create a PR targeting the `main` branch. +2. For new contributors a committer must first trigger the CI tasks. Please mention the members from committers list in the PR to help trigger the CI +3. Your PR will be reviewed. Please respond to all feedback on the PR: you don't have to change the code, but you should acknowledge the feedback. PRs waiting for the feedback for more than a few days will be marked as draft. +4. Once the PR is approved, one of the [committers] will merge your PR, typically within 24 hours. We leave approved "major" changes (see below) open for 24 hours prior to merging, and sometimes leave "minor" PRs open for the same time to permit additional feedback. + +Note that the above time frames are estimates. Due to limited committer +bandwidth, it may take longer to merge your PR. Please wait +patiently. If it has been several days you can friendly ping the +committer who approved your PR to help remind them to merge it. + +[committers]: https://people.apache.org/phonebook.html?unix=datafusion + +## Creating Pull Requests + +When possible, we recommend splitting your contributions into multiple smaller focused PRs rather than large PRs (500+ lines) because: + +1. The PR is more likely to be reviewed quickly -- our reviewers struggle to find the contiguous time needed to review large PRs. +2. The PR discussions tend to be more focused and less likely to get lost among several different threads. +3. It is often easier to accept and act on feedback when it comes early on in a small change, before a particular approach has been polished too much. + +If you are concerned that a larger design will be lost in a string of small PRs, creating a large draft PR that shows how they all work together can help. + +Note all commits in a PR are squashed when merged to the `main` branch so there is one commit per PR after merge. + +For larger PRs, it is often helpful to leave a review on your own PR with +comments calling out important changes or specific important choices. These +annotations can help reviewers quickly find areas they should focus on, thus +speeding up review. + +## Release Management and Backports + +Contributor-facing guidance for release branches, patch releases, and backports +is documented in the [Release Management](release_management.md) guide. + +## Before Submitting a PR + +Before submitting a PR, run the standard non-functional checks. PRs must pass +before merge. + +```bash +./dev/rust_lint.sh +# use `--write` to automatically fix some formatting and lint errors +# ./dev/rust_lint.sh --write --allow-dirty +``` + +You should also run any relevant commands from the [testing quick start](testing.md#testing-quick-start). + +## Conventional Commits & Labeling PRs + +We generate change logs for each release using an automated process that will categorize PRs based on the title +and/or the GitHub labels attached to the PR. + +We follow the [Conventional Commits] specification to categorize PRs based on the title. This most often simply means +looking for titles starting with prefixes such as `fix:`, `feat:`, `docs:`, or `chore:`. We do not enforce this +convention but encourage its use if you want your PR to feature in the correct section of the changelog. + +The change log generator will also look at GitHub labels such as `bug`, `enhancement`, or `api change`, and labels +do take priority over the conventional commit approach, allowing maintainers to re-categorize PRs after they have been merged. + +[conventional commits]: https://www.conventionalcommits.org/en/v1.0.0/ + +## Reviewing Pull Requests + +See the [Reviewing Pull Requests](pr_review.md) guide for what we look for +when reviewing PRs and how to prepare your own for review. + +## Performance Improvements + +Performance improvements are always welcome: performance is a key DataFusion +feature. + +In general, the performance improvement from a change should be "enough" to +justify any added code complexity. How much is "enough" is a judgement made by +the committers, but generally means that the improvement should be noticeable in +a real-world scenario and is greater than the noise of the benchmarking system. + +To help committers evaluate the potential improvement, performance PRs should +in general be accompanied by benchmark results that demonstrate the improvement. + +The best way to demonstrate a performance improvement is with the existing +benchmarks: + +- [System level SQL Benchmarks](https://github.com/apache/datafusion/tree/main/benchmarks) +- Microbenchmarks such as those in [functions/benches](https://github.com/apache/datafusion/tree/main/datafusion/functions/benches) + +If there is no suitable existing benchmark, you can create a new one. It helps +to isolate the effects of your change by creating a separate PR with the +benchmark, and then a PR with the code change that improves the benchmark. + +[system level sql benchmarks]: https://github.com/apache/datafusion/tree/main/benchmarks +[functions/benches]: https://github.com/apache/datafusion/tree/main/datafusion/functions/benches + +## "Major" and "Minor" PRs + +Since we are a worldwide community, we have contributors in many timezones who review and comment. To ensure anyone who wishes has an opportunity to review a PR, our committers try to ensure that at least 24 hours passes between when a "major" PR is approved and when it is merged. + +A "major" PR means there is a substantial change in design or a change in the API. Committers apply their best judgment to determine what constitutes a substantial change. A "minor" PR might be merged without a 24 hour delay, again subject to the judgment of the committer. Examples of potential "minor" PRs are: + +1. Documentation improvements/additions +2. Small bug fixes +3. Non-controversial build-related changes (clippy, version upgrades etc.) +4. Smaller non-controversial feature additions + +The good thing about open code and open development is that any issues in one change can almost always be fixed with a follow on PR. + +## Stale PRs + +Pull requests will be marked with a `stale` label after 60 days of inactivity and then closed 7 days after that. +Commenting on the PR will remove the `stale` label. + +## AI-Assisted contributions + +DataFusion has the following policy for AI-assisted PRs: + +- The PR author should **understand the core ideas** behind the implementation **end-to-end**, and be able to justify the design and code during review. +- **Calls out unknowns and assumptions**. It's okay to not fully understand some bits of AI generated code. You should comment on these cases and point them out to reviewers so that they can use their knowledge of the codebase to clear up any concerns. For example, you might comment "calling this function here seems to work but I'm not familiar with how it works internally, I wonder if there's a race condition if it is called concurrently". + +### Why fully AI-generated PRs without understanding are not helpful + +Today, AI tools cannot reliably make complex changes to DataFusion on their own, which is why we rely on pull requests and code review. + +The purposes of code review are: + +1. Finish the intended task. +2. Share knowledge between authors and reviewers, as a long-term investment in the project. For this reason, even if someone familiar with the codebase can finish a task quickly, we're still happy to help a new contributor work on it even if it takes longer. + +An AI dump for an issue doesn’t meet these purposes. Maintainers could finish the task faster by using AI directly, and the submitters gain little knowledge if they act only as a pass through AI proxy without understanding. + +Please understand the reviewing capacity is **very limited** for the project, so large PRs which appear to not have the requisite understanding might not get reviewed, and eventually closed or redirected. + +### Better ways to contribute than an “AI dump” + +It's recommended to write a high-quality issue with a clear problem statement and a minimal, reproducible example. This can make it easier for others to contribute. + +### CI Runners + +#### Runs-On + +We use [Runs-On](https://runs-on.com/) for some actions in the main repository, which run in the ASF AWS account to speed up CI. In forks, these actions run on the default GitHub runners since forks do not have access to ASF infrastructure. + +To configure them, we use the following format: + +`runs-on: ${{ github.repository_owner == 'apache' && format('runs-on={0},family=m8a,cpu=16,image=ubuntu24-full-x64,extras=s3-cache,disk=large,tag=datafusion', github.run_id) || 'ubuntu-latest' }}` + +This is a conditional expression that uses Runs-On custom runners for the main repository and falls back to the standard GitHub runners for forks. Runs-On configuration follows the [Runs-On pattern](https://runs-on.com/configuration/job-labels/). + +For those actions we also use the [Runs-On action](https://runs-on.com/caching/magic-cache/#how-to-use), which adds support for external caching and reports job metrics: + +`- uses: runs-on/action@cd2b598b0515d39d78c38a02d529db87d2196d1e` + +For the standard GitHub runners, this action will do nothing. + +##### Spot Instances + +By default, Runs-On actions run as [spot instances](https://runs-on.com/configuration/spot-instances/), which means they might occasionally be interrupted. In the CI you would see: + +``` +Error: The operation was canceled. +``` + +According to Runs-On, spot instance termination is extremely rare for instances running for less than 1h. Those actions will be restarted automatically. + +#### GitHub Runners + +We also use standard GitHub runners for some actions in the main repository; these are also runnable in forks. diff --git a/versions/55.0.0/_sources/contributor-guide/inviting.md.txt b/versions/55.0.0/_sources/contributor-guide/inviting.md.txt new file mode 100644 index 0000000000000..75ef7c1e98d6d --- /dev/null +++ b/versions/55.0.0/_sources/contributor-guide/inviting.md.txt @@ -0,0 +1,418 @@ + + +# Inviting New Committers and PMC Members + +This is a cookbook of the recommended DataFusion specific process for inviting +new committers and PMC members. It is intended to follow the [Apache Project +Management Committee guidelines], which takes precedence over this document if +there is a conflict. + +This process is intended for PMC members. While the process is open, the actual +discussions and invitations are not (one of the very few things that we do not +do in the open). + +When following this process, in doubt, check the `private@datafusion.apache.org` +[mailing list archive] for examples of previous emails. + +The general process is: + +1. A PMC member starts a discussion on `private@datafusion.apache.org` + about a candidate they feel should be considered who would use the additional + trust granted to a committer or PMC member to help the community grow and thrive. +2. When consensus is reached a formal [vote] occurs +3. Assuming the vote is successful, that person is granted the appropriate + access via ASF systems. + +[apache project management committee guidelines]: https://www.apache.org/dev/pmc.html +[mailing list archive]: https://lists.apache.org/list.html?dev@datafusion.apache.org +[vote]: https://www.apache.org/foundation/voting + +## New Committers + +### Step 1: Start a Discussion Thread + +The goal of this step is to allow free form discussion prior to calling a vote. +This helps the other PMC members understand why you are proposing to invite the +person to become a committer and makes the voting process easier. + +Best practice is to include some details about why you are proposing to invite +the person. Here is an example: + +``` +To: private@datafusion.apache.org +Subject: [DISCUSS] $PERSONS_NAME for Committer + +$PERSONS_NAME has been an active contributor to the DataFusion community for the +last 6 months[1][2], helping others, answering questions, and improving the +project's code. + +Are there any thoughts about inviting $PERSONS_NAME to become a committer? + +Thanks, +Your Name + +[1]: https://github.com/apache/datafusion/issues?q=commenter%3A +[2]: https://github.com/apache/datafusion/commits?author= +``` + +### Step 2: Formal Vote + +Assuming the discussion thread goes well, start a formal vote, with an email like: + +``` +To: private@datafusion.apache.org +Subject: [VOTE] $PERSONS_NAME for DataFusion Committer +I propose to invite $PERSONS_NAME to be a committer. See discussion here [1]. + +[ ] +1 : Invite $PERSONS_NAME to become a committer +[ ] +0: ... +[ ] -1: I disagree because ... + +The vote will be open for at least 7 days. + +My vote: +1 + +Thanks, +Your Name + +[1] LINK TO DISCUSSION THREAD (e.g. https://lists.apache.org/thread/7rocc026wckknrjt9j6bsqk3z4c0g5yf) +``` + +If the vote passes (requires 3 `+1`, and no `-1` votes) , send a result email +like the following (substitute `N` with the number of `+1` votes) + +``` +to: private@datafusion.apache.org +subject: [RESULT][VOTE] $PERSONS_NAME for DataFusion Committer + +The vote carries with N +1 votes. +``` + +### Step 3: Send Invitation to the Candidate + +Once the vote on `private@` has passed and the `[RESULT]` e-mail sent, send an +invitation to the new committer and cc: `private@datafusion.apache.org` + +In order to be given write access, the committer needs an apache.org account (e.g. `name@apache.org`). To get one they need: + +1. An [ICLA] on file. Note that Sending an ICLA to `secretary@apache.org` will trigger account creation. If they already have an ICLA on file, but no Apache account, see instructions below. +2. Add GitHub username to the account at [id.apache.org](http://id.apache.org/) +3. Connect [gitbox.apache.org] to their GitHub account +4. Follow the instructions at [gitbox.apache.org] to link your GitHub account with your + +[gitbox.apache.org]: https://gitbox.apache.org + +If the new committer is already a committer on another Apache project (so they +already had an Apache account), The PMC Chair (or an ASF member) simply needs to +explicitly add them to the roster on the [Whimsy Roster Tool]. + +### Step 4: Announce and Celebrate the New Committer + +Email to Send an email such as the following to +[dev@datafusion.apache.org](mailto:dev@datafusion.apache.org) to celebrate and +acknowledge the new committer to the community. + +``` +To: dev@datafusion.apache.org +Subject: [ANNOUNCE] New DataFusion committer: $NEW_COMMITTER + +On behalf of the DataFusion PMC, I'm happy to announce that $NEW_COMMITTER +has accepted an invitation to become a committer on Apache +DataFusion. Welcome, and thank you for your contributions! + + +``` + +[icla]: http://www.apache.org/licenses/#clas + +## Email Templates for Inviting New Committers + +### Committers WITHOUT an Apache account and WITHOUT ICLA on file + +You can check here to see if someone has an Apache account: http://people.apache.org/committer-index.html + +If you aren't sure whether someone has an ICLA on file, ask the DataFusion PMC +chair or an ASF Member to check [the ICLA file list]. + +[the icla file list]: https://whimsy.apache.org/officers/unlistedclas.cgi + +``` +To: $EMAIL +Cc: private@datafusion.apache.org +Subject: Invitation to become a DataFusion Committer + +Dear $NEW_COMMITTER, + +The DataFusion Project Management Committee (PMC) hereby offers you +committer privileges to the project. These privileges are offered on +the understanding that you'll use them reasonably and with common +sense. We like to work on trust rather than unnecessary constraints. + +Being a committer enables you to merge PRs to DataFusion git repositories. + +Being a committer does not require you to participate any more than +you already do. It does tend to make one even more committed. You will +probably find that you spend more time here. + +Of course, you can decline and instead remain as a contributor, +participating as you do now. + +A. This personal invitation is a chance for you to accept or decline +in private. Either way, please let us know in reply to this email, make sure to reply-all (it should send a copy to +private@datafusion.apache.org) for record keeping / log of the project. + +B. If you accept, the next step is to register an ICLA: + +Details of the iCLA and how to submit them are found through this link: +https://www.apache.org/licenses/contributor-agreements.html#clas + +When you transmit the completed ICLA, request to notify Apache +DataFusion and choose a unique Apache id. Look to see if your preferred id +is already taken at http://people.apache.org/committer-index.html This +will allow the Secretary to notify the PMC when your ICLA has been +recorded. + +Once you are notified that your Apache account has been created, you must then: + +1. Add your GitHub username to your account at id.apache.org + +2. Follow the instructions at gitbox.apache.org to link your GitHub account +with your Apache account. + +You will then have write (but not admin) access to the DataFusion repositories. +If you have questions or run into issues, please reply-all to this e-mail. +``` + +After the new account has been created, you can announce the new committer on `dev@` + +### Committers WITHOUT an Apache account but WITH an ICLA on file + +In this scenario, an officer (the project VP or an Apache Member) needs only to +request an account to be created for the new committer, since through the +ordinary process the ASF Secretary will do it automatically. This is done at +https://whimsy.apache.org/officers/acreq. Before requesting the new account, +send an e-mail to the new committer (cc'ing `private@`) like this: + +``` +To: $EMAIL +Cc: private@datafusion.apache.org +Subject: Invitation to become a DataFusion Committer + +Dear $NEW_COMMITTER, + +The DataFusion Project Management Committee (PMC) hereby offers you +committer privileges to the project. These privileges are offered on +the understanding that you'll use them reasonably and with common +sense. We like to work on trust rather than unnecessary constraints. + +Being a committer enables you to merge PRs to DataFusion git repositories. + +Being a committer does not require you to participate any more than +you already do. It does tend to make one even more committed. You will +probably find that you spend more time here. + +Of course, you can decline and instead remain as a contributor, +participating as you do now. + +This personal invitation is a chance for you to accept or decline +in private. Either way, please let us know in reply to this email, make sure to reply-all (it should send a copy to +private@datafusion.apache.org) for record keeping / log of the project. We will have to request an +Apache account be created for you, so please let us know what user id +you would prefer. + +Once you are notified that your Apache account has been created, you must then: + +1. Add your GitHub username to your account at id.apache.org + +2. Follow the instructions at gitbox.apache.org to link your GitHub account +with your Apache account. + +You will then have write (but not admin) access to the DataFusion repositories. +If you have questions or run into issues, please reply-all to this e-mail. +``` + +### Committers WITH an existing Apache account + +In this scenario, an officer (the project PMC Chair or an ASF Member) can simply add +the new committer on the [Whimsy Roster Tool]. Before doing this, e-mail the new +committer inviting them to be a committer like so (cc private@): + +``` +To: $EMAIL +Cc: private@datafusion.apache.org +Subject: Invitation to become a DataFusion Committer + +Dear $NEW_COMMITTER, + +The DataFusion Project Management Committee (PMC) hereby offers you +committer privileges to the project. These privileges are offered on +the understanding that you'll use them reasonably and with common +sense. We like to work on trust rather than unnecessary constraints. + +Being a committer enables you to merge PRs to DataFusion git repositories. + +Being a committer does not require you to participate any more than +you already do. It does tend to make one even more committed. You will +probably find that you spend more time here. + +Of course, you can decline and instead remain as a contributor, +participating as you do now. + +If you accept, please let us know in reply to this email, make sure to reply-all (it should send a copy to +private@datafusion.apache.org) for record keeping / log of the project. +``` + +## New PMC Members + +This is a DataFusion specific cookbook for the Apache Software Foundation +instructions on [how to add a PMC member]. + +[how to add a pmc member]: https://www.apache.org/dev/pmc.html#pmcmembers + +### Step 1: Start a Discussion Thread + +As for committers, start a discussion thread on the `private@` mailing list + +``` +To: private@datafusion.apache.org +Subject: [DISCUSS] $NEW_PMC_MEMBER for PMC + +I would like to propose adding $NEW_PMC_MEMBER[1] to the DataFusion PMC. + +$NEW_PMC_MEMBER has been a committer since $COMMITTER_MONTH [2], has a +strong and sustained contribution record for more than a year, and focused on +helping the community and the project grow[3]. + +Are there any thoughts about inviting $NEW_PMC_MEMBER to become a PMC member? + +[1] https://github.com/$NEW_PMC_MEMBERS_GITHUB_ACCOUNT +[2] LINK TO COMMITTER VOTE RESULT THREAD (e.g. https://lists.apache.org/thread/ovgp8z97l1vh0wzjkgn0ktktggomxq9t) +[3]: https://github.com/apache/datafusion/pulls?q=commenter%3A<$NEW_PMC_MEMBERS_GITHUB_ACCOUNT>+ + +Thanks, +YOUR NAME +``` + +### Step 2: Formal Vote + +Assuming the discussion thread goes well, start a formal vote with an email like: + +``` +To: private@datafusion.apache.org +Subject: [VOTE] $NEW_PMC_MEMBER for PMC + +I propose inviting $NEW_PMC_MEMBER to join the DataFusion PMC. We previously +discussed the merits of inviting $NEW_PMC_MEMBER to join the PMC [1]. + +The vote will be open for at least 7 days. + +[ ] +1 : Invite $NEW_PMC_MEMBER to become a PMC member +[ ] +0: ... +[ ] -1: I disagree because ... + +My vote: +1 + +[1] LINK TO DISCUSSION THREAD (e.g. https://lists.apache.org/thread/x2zno2hs1ormvfy13n7h82hmsxp3j66c) + +Thanks, +Your Name +``` + +If this vote succeeds, send a "RESULT" email to `private@` like this: + +``` +To: private@datafusion.apache.org +Subject: [RESULT][VOTE] $NEW_PMC_MEMBER for PMC + +The vote carries with N +1 votes and no -1 votes. I will send an invitation +``` + +### Step 3: Send invitation email + +Assuming the vote passes, the Chair sends an invitation e-mail to the new PMC +member (cc'ing `private@`) like this: + +``` +To: $EMAIL +Cc: private@datafusion.apache.org +Subject: Invitation to join the DataFusion PMC +Dear $NEW_PMC_MEMBER, + +In recognition of your demonstrated commitment to, and alignment with, the +goals of the Apache DataFusion project, the DataFusion PMC has voted to offer you +membership in the DataFusion PMC ("Project Management Committee"). + +Please let us know if you accept by subscribing to the private alias [by +sending mail to private-subscribe@datafusion.apache.org], and posting +a message to private@datafusion.apache.org. + +The PMC for every top-level project is tasked by the Apache Board of +Directors with official oversight and binding votes in that project. + +As a PMC member, you are responsible for continuing the general project, code, +and community oversight that you have exhibited so far. The votes of the PMC +are legally binding. + +All PMC members are subscribed to the project's private mail list, which is +used to discuss issues unsuitable for an open, public forum, such as people +issues (e.g. new committers, problematic community members, etc.), security +issues, and the like. It can't be emphasized enough that care should be taken +to minimize the use of the private list, discussing everything possible on the +appropriate public list. + +The private PMC list is *private* - it is strictly for the use of the +PMC. Messages are not to be forwarded to anyone else without the express +permission of the PMC. Also note that any Member of the Foundation has the +right to review and participate in any PMC list, as a PMC is acting on behalf +of the Membership. + +Finally, the PMC is not meant to create a hierarchy within the committership or +the community. Therefore, in our day-to-day interactions with the rest of the +community, we continue to interact as peers, where every reasonable opinion is +considered, and all community members are invited to participate in our public +voting. If there ever is a situation where the PMC's view differs significantly +from that of the rest of the community, this is a symptom of a problem that +needs to be addressed. + +With the expectation of your acceptance, welcome! + +The Apache DataFusion PMC +``` + +### Step 4: Chair Promotes the Committer to PMC + +The PMC chair adds the user to the PMC using the [Whimsy Roster Tool]. + +### Step 5: Announce and Celebrate the New PMC Member + +Send an email such as the following to `dev@datafusion.apache.org` to celebrate: + +``` +To: dev@datafusion.apache.org +Subject: [ANNOUNCE] New DataFusion PMC member: $NEW_PMC_MEMBER + +The Project Management Committee (PMC) for Apache DataFusion has invited +$NEW_PMC_MEMBER to become a PMC member and we are pleased to announce +that $NEW_PMC_MEMBER has accepted. + +Congratulations and welcome! +``` + +[whimsy roster tool]: https://whimsy.apache.org/roster/committee/datafusion diff --git a/versions/55.0.0/_sources/contributor-guide/pr_review.md.txt b/versions/55.0.0/_sources/contributor-guide/pr_review.md.txt new file mode 100644 index 0000000000000..1154ae0e96b04 --- /dev/null +++ b/versions/55.0.0/_sources/contributor-guide/pr_review.md.txt @@ -0,0 +1,240 @@ + + +# Reviewing Pull Requests + +When reviewing PRs, our primary goal is to improve DataFusion and its community +together. PR feedback should be constructive and help improve the code as well +as the understanding of the contributor. + +Review bandwidth is currently our most limited resource, and reviews from the +broader community are both welcomed and encouraged. Reviewing PRs is a great way +to learn the codebase, and you do not need to be a committer to leave valuable +review feedback. In fact, one of the best ways to become a committer is to +thoughtfully review other PRs. + +Please ensure any comments you leave contain a rationale and suggested +alternative -- it is frustrating to be told "don't do it this way" without any +clear reason or alternative provided. + +The criteria in this guide are also a useful checklist when preparing your own +PR for review. + +## PR Review Mechanics + +Some helpful links: + +- [PRs Waiting for Review] on GitHub +- [Approved PRs Waiting for Merge] on GitHub + +[prs waiting for review]: https://github.com/apache/datafusion/pulls?q=is%3Apr+is%3Aopen+-review%3Aapproved+-is%3Adraft+ +[approved prs waiting for merge]: https://github.com/apache/datafusion/pulls?q=is%3Apr+is%3Aopen+review%3Aapproved+-is%3Adraft + +The overall PR lifecycle (CI triggering, approval, the 24-hour rule for +"major" PRs, and merging) is described in the +[Pull Request Overview](index.md#pull-request-overview) section of the +contributor guide. + +Practical tips: + +1. Check out the changes locally to explore them in your IDE or with an + agent, e.g. `gh pr checkout ` using the [GitHub CLI]. +2. There is normally no need to rerun locally any tests that CI has already run. +3. Leave comments on specific lines of the diff where possible, so the + discussion has context. +4. If you review a PR but don't feel confident approving it, leaving comments + is still valuable: a partial review (e.g. "I reviewed the tests and they + look good") helps the next reviewer focus their time. +5. Anything that does not need to block the current PR can be noted as a + potential follow-up (ideally by filing an issue), keeping the PR focused + and quick to merge. + +[github cli]: https://cli.github.com/ + +## Review the PR Description + +The PR description is often what users and contributors will find when they have +a question about the intention behind a change, or when the code itself is not +clear. The PR description also becomes the extended commit message. + +Check that the description: + +1. Concisely describes the **problem being solved from the user's point of + view**. + +2. Follows the [PR template], and answers the template's questions. + +3. Accurately describes the content of the PR, including any relevant context or + background. + Great descriptions have a high signal-to-noise ratio, summarizing + important implementation changes without repeating technical minutiae that + are already present in the code itself. + +4. Explicitly calls out any user-facing or API changes (see + [Review the Code](#review-the-code) below). + +[pr template]: https://github.com/apache/datafusion/blob/main/.github/pull_request_template.md + +## Review the Code Comments + +The goal of code comments is to help future readers of the code understand what +is not obvious from reading the code itself. Great comments make the code easier +to reason about for readers with the expected background, and help future +maintainers. + +Some practical guidelines for reviewing comments: + +1. The code has adequate comments focused on the **rationale** for any + non-obvious change (the "why"), not a restatement of what the code does + (the "what"), which is typically clear from reading the code itself. +2. Comments do not narrate irrelevant internal implementation details or the + history of how the change was developed (this is common in LLM-assisted + code, e.g. "// changed to use a HashMap" or "// this handles the case + mentioned above"). Such comments become irrelevant as soon as the PR merges. +3. When comments refer to other structs, functions, or modules, they should use + [rustdoc intra-doc links] (e.g. `` [`SessionContext`] ``) rather than plain + text names, so that `cargo doc` link checking ensures the references stay + valid as the code evolves. +4. New public APIs have doc comments, including examples where appropriate + (doc examples are also tested by CI, so they double as test coverage). +5. When documenting modules, functions, or fields, start with simple examples + and intuitive explanations, and optionally add formal, math-like + definitions when necessary. This makes the implementation easier to reason + about. +6. When something is confusing on first read, treat that as a good + opportunity to improve the comments. + +[rustdoc intra-doc links]: https://doc.rust-lang.org/rustdoc/write-documentation/linking-to-items-by-name.html + +## Review the Test Coverage + +Check that the feature or fix is covered sufficiently with tests (see the +[Testing](testing.md) guide for more details): the PR should include tests for +any new functionality, and a bug fix should include a test that reproduces the +reported problem. + +Guidelines for evaluating tests: + +1. Prefer `sqllogictest` (`.slt`) tests or DataFrame API tests where + possible, as they exercise **user-visible behavior** and are less coupled + to internal implementation details than unit tests. +2. Verify tests cover edge cases and common failure scenarios, not just the + common successful path. However, it is NOT necessary to test every possible + error path, especially if it is difficult to trigger or unlikely to occur in + practice. +3. Verify test coverage of changed code using the `codecov` check on the PR, + or by running [`cargo llvm-cov`] locally for an HTML report. Use judgment + about any uncovered lines -- the goal is confidence in the change, not + slavishly hitting some coverage number. +4. Avoid tests with lots of repeated boilerplate: when many tests share + near-identical setup, it is hard to understand what is different + (and thus what is actually being tested) between them. Make the _difference_ + between cases obvious. +5. Check that tests assert on specific expected values or plans (e.g. via + `insta` snapshots or `.slt` expected output) rather than merely checking + "no error occurred". +6. Verify tests actually cover the bug ("Ablation Testing"): For bug fixes, revert + the fix locally and check that the new test fails without it (i.e. the test + actually reproduces the bug or covers the new feature). + +[`cargo llvm-cov`]: https://github.com/taiki-e/cargo-llvm-cov + +## Review the Code + +Check that: + +1. The code is clear and fits the style of the existing codebase. +2. New functions and tests are placed near similar functions and + tests. For example, helper functions should be defined close to where they are used, + and new tests should be placed in the same module as the code they test. + SLT tests should be placed in an existing .slt file with related functionality, + unless the new tests are large enough to justify their own file. +3. New APIs are consistent with existing public APIs and patterns; where a + similar mechanism already exists, the PR should extend it rather than + introduce a parallel one. +4. Any changes to the public API follow the [API health policy]. +5. The change is appropriately scoped: unrelated refactoring, formatting + churn, or drive-by changes make review longer and are better as separate + PRs. +6. New errors are actionable, mention the offending item, and use + the right error variant (e.g. `plan_err!` for user-triggerable errors vs + `internal_err!` for invariant violations). + +[api health policy]: api-health.md + +## Review the Performance + +Performance is a key feature of DataFusion. See [Performance Improvements](index.md#performance-improvements) +for the project policy: an improvement should be "enough" to justify any +added code complexity, and performance PRs should come with benchmark +results. + +When reviewing: + +1. Find any relevant existing benchmarks and run them against `main`: + the [system-level SQL benchmarks] are run with `bench.sh` (see the + [benchmarks README]), and microbenchmarks (e.g. in + `datafusion/functions/benches`) are run with `cargo bench`. +2. Be aware that benchmarking on a machine where other + work is being done will make results hard to reproduce. Prefer a quiet, + dedicated machine and repeated runs. +3. If the PR claims a performance improvement, check that the reported + results are reproducible and that the benchmark exercises the changed + code path. + +[system-level sql benchmarks]: https://github.com/apache/datafusion/tree/main/benchmarks +[benchmarks readme]: https://github.com/apache/datafusion/blob/main/benchmarks/README.md + +## Best Practices for Reviewers + +Here are some suggested best practices to follow when reviewing PRs. + +### Review Tone: Thank Contributors and Praise Good Work Specifically + +Open reviews by thanking the author by name, and when a PR is well done, say +specifically what makes it good -- positive feedback encourages people to keep +contributing and helps them understand what is valued in the project. + +### State Approval Conditions Explicitly + +If you are not ready to approve, list concretely what you would need to see +before approving (e.g. "benchmark results and an upgrade guide entry") so +the author has a clear path to merge. + +### Defer Non-Blocking Work to Follow-On Issues + +Explicitly defer non-critical suggestions to a follow-on PR and file +(or ask the author to file) issues for them, so good PRs merge quickly +without scope creep. + +Similarly, when a PR mixes refactoring with behavior changes or fixes a narrow +problem with a broad mechanism, ask for it to be split or scoped down rather +than reviewing it as-is. + +### Narrate What You Verified When Approving + +Rather than a bare "LGTM", say what you actually checked ("traced the state +transitions by hand", "confirmed the hasher change cannot affect ordering") +so it is clear what was verified and what was not. + +### Invite Additional Committers on Core Changes + +For changes to core, widely shared code, leave the PR open for other +committers to look at and cc those who know the area, even after you have +approved. diff --git a/versions/55.0.0/_sources/contributor-guide/release_management.md.txt b/versions/55.0.0/_sources/contributor-guide/release_management.md.txt new file mode 100644 index 0000000000000..7053d0f994559 --- /dev/null +++ b/versions/55.0.0/_sources/contributor-guide/release_management.md.txt @@ -0,0 +1,202 @@ + + +# Release Management + +This page describes DataFusion release branches and backports. For the +maintainer release guide, including release candidate artifacts, voting, and +publication, see [the release process README in `dev/release`]. + +## Overview + +DataFusion typically has a major release about once per month, including +breaking API changes. Patch releases are made on an ad hoc basis, but we try to +avoid them because major releases are frequent. + +New development happens on the [`main` branch]. Releases are made from release +branches named `branch-NN`, such as [`branch-50`] for the `50.x.y` release +series. + +In general: + +- New features land on [`main`] +- Patch releases are cut from the corresponding `branch-NN` +- Only targeted, low-risk fixes should be added to a release branch + +Changes reach a release branch in one of two ways: + +- (Most common) Fix the issue on `main` and then backport the merged change to the release branch +- Fix the issue on the release branch and then forward-port the change to `main` + +Releases are coordinated using GitHub issues. Each planned release is listed in +the [DataFusion Releases tracking issue], and each release is coordinated in a +dedicated issue, such as the [release issue for 50.3.0]. If you think a fix +should be included in a patch release, discuss it on the relevant tracking issue +or open a backport PR and link it there. + +To prepare for a new release series, maintainers: + +- Create a new branch from `main`, such as `branch-50`, in the Apache repository +- Continue merging new features to `main` +- Prepare the release branch for release by updating versions, changelog content, + and any additional release-specific fixes via the + [Backport Workflow](#backport-workflow) +- Create release candidate artifacts from the release branch +- After approval, publish to crates.io, ASF distribution servers, and Git tags + +## Backport Criteria + +A release branch is a stabilization branch for an imminent or recent patch +release. The bar for landing a change on a release branch is therefore +_higher_ than the bar for landing on `main`, not lower. These criteria define +what is eligible for backport; the [Backport Workflow](#backport-workflow) +below describes the mechanics. + +DataFusion follows Cargo SemVer, with breaking changes allowed at major +version boundaries — see the [API health policy] for the full framing of +public Rust and SQL API stability. Patch releases (`x.y.z`, `z ≥ 1`) carry +fixes only and never introduce new features or breaking changes. + +### Eligible for backport + +- **Security fixes.** Fixes for known or reported security issues should be + backported to every actively maintained release branch. +- **Correctness fixes.** Fixes for queries that produce incorrect results, + panics, data loss, or crashes. If the fix itself changes user-visible SQL + semantics to make a wrong result right, follow [Behavior changes] below. +- **Stability and regression fixes.** Fixes for regressions introduced in the + current release line, hangs, deadlocks, memory leaks, or other availability + issues. +- **Build, CI, and test fixes** required to keep the branch buildable and + releasable. +- **Documentation fixes** for behavior already in the release. Documentation + for behavior that exists only on `main` does not belong on a release branch. + +### Not recommended for backport + +- **New features**, including new SQL functions, new optimizer rules, new + configuration options, new public APIs, and new file-format support. Land + on `main` and ship in the next major release. +- **Breaking API changes** of any kind, Rust or SQL. DataFusion makes + breaking changes only at major version boundaries — see [API health policy]. +- **Refactors and cleanup** that do not fix a bug, even if they are correct. +- **Performance improvements** that are not also correctness or stability + fixes. Land on `main`. +- **Dependency upgrades**, except when the upgrade itself is the security or + correctness fix and there is no narrower alternative. + +### Behavior changes + +A "behavior change" is any fix that alters user-visible results: SQL +semantics (values, ordering, types, null handling), error messages that +downstream users may rely on, plan output, or default configuration values. + +Behavior-changing fixes need extra scrutiny on a release branch because +users upgrading between patch versions do not expect their queries to start +returning different results. When proposing one for backport, state on the +release tracking issue _why_ the change should ship in this patch release +rather than wait for the next major. The previous and new behavior should +already be documented on the original issue or PR — link to that rather +than restating it. + +If in doubt, default to "land on `main`, ship in the next major." + +### Who decides + +The release manager for the active release line is the final reviewer of +what goes into the patch release. They coordinate via the release tracking +issue (for example, the [release issue for 50.3.0]). Anyone may propose a +backport by opening a backport PR and linking it from the tracking issue; +inclusion is the release manager's call. + +### Active release branches + +DataFusion does not maintain Long-Term Support branches. In general only the +most recent `branch-NN` is actively maintained for backports, but if you need +fixes in older releases, we are open to discussion. + +Security fixes are an exception: a maintainer may choose to backport a +critical security fix to an older branch even after it would otherwise be +closed. Discuss on the dev list or in a tracking issue before doing so. + +## Backport Workflow + +The usual workflow is: + +1. Fix on `main` first, and merge the fix via a normal PR workflow. +2. Cherry-pick the merged commit onto the release branch. +3. Open a backport PR targeting the release branch (examples below). + +- [Example backport PR] +- [Additional backport PR example] + +### Inputs + +To backport a change, gather the following information: + +- Target branch, such as `apache/branch-52` +- The release tracking issue URL, such as https://github.com/apache/datafusion/issues/19692 +- The original PR URL, such as https://github.com/apache/datafusion/pull/20192 +- Optional explicit commit SHA to backport + +### Apply the Backport + +Start from the target release branch, create a dedicated backport branch, and +use `git cherry-pick`. For example, to backport PR #1234 to `branch-52` when +the commit SHA is `abc123`, run: + +```bash +git checkout apache/branch-52 +git checkout -b alamb/backport_1234 +git cherry-pick abc123 +``` + +### Test + +Run tests as described in the [testing documentation]. + +### Open the PR + +Create a PR against the release branch, not `main`, and prefix it with +`[branch-NN]` to show which release branch the backport targets. For example: + +- `[branch-52] fix: validate inter-file ordering in eq_properties() (#20329)` + +Use a PR description that links the tracking issue, original PR, and target +branch, for example: + +```markdown +- Part of +- Closes on + +This PR: + +- Backports from @ to the line +``` + +[`main` branch]: https://github.com/apache/datafusion/tree/main +[`branch-50`]: https://github.com/apache/datafusion/tree/branch-50 +[the release process readme in `dev/release`]: https://github.com/apache/datafusion/blob/main/dev/release/README.md +[datafusion releases tracking issue]: https://github.com/apache/datafusion/issues/19783 +[release issue for 50.3.0]: https://github.com/apache/datafusion/issues/18072 +[example backport pr]: https://github.com/apache/datafusion/pull/18131 +[additional backport pr example]: https://github.com/apache/datafusion/pull/20792 +[testing documentation]: testing.md +[api health policy]: api-health.md +[behavior changes]: #behavior-changes diff --git a/versions/55.0.0/_sources/contributor-guide/roadmap.md.txt b/versions/55.0.0/_sources/contributor-guide/roadmap.md.txt new file mode 100644 index 0000000000000..903b2c7b7ef38 --- /dev/null +++ b/versions/55.0.0/_sources/contributor-guide/roadmap.md.txt @@ -0,0 +1,156 @@ + + +# Roadmap and Improvement Proposals + +The [project introduction](../user-guide/introduction.md) explains the +overview and goals of DataFusion, and our development efforts largely +align to that vision. + +## Planning `EPIC`s + +DataFusion uses [GitHub issues] to track planned work. We collect related +tickets using tracking issues marked with the `EPIC` label, containing +discussion and links to more detailed items: + +[github issues]: https://github.com/apache/datafusion/issues + +- [The current list of `EPIC`s can be found here.](https://github.com/apache/datafusion/issues?q=is%3Aissue%20state%3Aopen%20label%3AEPIC) + +- [The current list of `PROPOSAL EPIC` (that are not yet underway) can be found here.](https://github.com/apache/datafusion/issues?q=is%3Aissue%20state%3Aopen%20label%3A%22PROPOSAL%20EPIC%22) + +Epics offer a high level roadmap of what the DataFusion community is thinking +about. The epics are not meant to restrict possibilities, but rather help +organize the community and make it easier to see where development is headed, +align our work, and inspire additional contributions. + +We also welcome contributions for items not covered by epics. However, before +submitting a large PR, we strongly suggest and request you start a conversation as described in [Discussing New Features](#discussing-new-features) below. + +[dev@arrow.apache.org]: mailto:dev@arrow.apache.org + +## Quarterly Roadmap + +The DataFusion roadmap is driven by the priorities of contributors rather than +any single organization or coordinating committee. We typically discuss our +roadmap using GitHub issues, approximately quarterly, and invite you to join the +discussion. + +The current roadmap discussion is +[DataFusion 2026 Q3-Q4 Roadmap Discussion](https://github.com/apache/datafusion/issues/22882). + +For more information: + +1. [Search for issues labeled `roadmap`](https://github.com/apache/datafusion/issues?q=is%3Aissue%20%20%20roadmap) +2. [DataFusion 2026 Q3-Q4 Roadmap Discussion](https://github.com/apache/datafusion/issues/22882) +3. [DataFusion Road Map: Q1 2026](https://github.com/apache/datafusion/issues/18494) +4. [DataFusion Road Map: Q3-Q4 2025](https://github.com/apache/datafusion/issues/15878) +5. [2024 Q4 / 2025 Q1 Roadmap](https://github.com/apache/datafusion/issues/13274) + +## Improvement Proposals + +### Discussing New Features + +If you plan to work on a new feature that doesn't have an existing ticket, it is +a good idea to open one for discussion. Advanced discussion helps avoid wasted +effort by determining if the feature is a good fit for DataFusion before too +much time is invested. Discussion on a ticket can help gather feedback from the +community and is likely easier to discuss than a 1000 line PR. + +Maintainers will mark major proposals as `PROPOSED EPIC` to make them more +visible, but we are very limited on review bandwidth. If you open a ticket and it +doesn't get any response, try `@`-mentioning recently active community members +in the ticket, or [posting to the mailing list or Discord](communication.md). + +### Supervising Maintainers + +We have found that most successful epics have one or more "supervising +maintainers", a committer ([see here for current list]) who take the lead on +reviewing and committing PRs, helps with design, and coordinates and +communicates with the community. If you want to ship a large feature, we +recommend finding such maintainer upfront; otherwise, your PRs may +remain unreviewed for a very long time. + +Supervising maintainers have no additional formal authority and there is +currently no formal process for appointing, approving or tracking who has that +role for a given epic. Instead, we rely on discussion on the ticket or PR. +Helping complete an epic is a significant time commitment, so maintainers are +more likely to help features they are particularly interested in or align with +their own project's use of DataFusion. + +If you are willing to be a supervising maintainer for a feature, please say so +explicitly. If you are unsure, we suggest asking directly who is willing to take +the role, as it can be hard to tell sometimes whether a committer is simply +participating and giving general feedback. + +[see here for current list]: governance.md + +### What Contributions are Good Fits? + +DataFusion is designed to be highly extensible, and many features can be +implemented as extensions without changes or additions to the core. Support for +new functions, data formats, and similar functionality can be added using those +extension APIs, and there are already many existing community supported +extensions listed in the [extensions list]. + +Query engines are complex pieces of software to develop and maintain. Given our +limited maintenance bandwidth, we try to keep the DataFusion core as simple and +focused as possible, while still satisfying the [design goal] of an easy to +start initial experience. + +With that in mind, contributions that meet the following criteria are more likely +to be accepted: + +1. Bug fixes for existing features +2. Test coverage for existing features +3. Documentation improvements / examples +4. Performance improvements to existing features (with benchmarks) +5. "Small" functional improvements to existing features (if they don't change existing behavior) +6. Additional APIs for extending DataFusion's capabilities +7. CI improvements + +Contributions that will likely involve more discussion (see Discussing New +Features above) prior to acceptance include: + +1. Major new functionality (even if it is part of the "standard SQL") +2. New functions, especially if they aren't part of "standard SQL" +3. New data sources (e.g. support for Apache ORC) + +[extensions list]: ../library-user-guide/extensions.md +[design goal]: https://docs.rs/datafusion/latest/datafusion/index.html#design-goals + +### Design Build vs. Big Up Front Design + +Typically, the DataFusion community attacks large problems by solving them bit +by bit and refining a solution iteratively on the `main` branch as a series of +Pull Requests. This is different from projects which front-load the effort +with a more comprehensive design process. + +By "advancing the front" the community always makes tangible progress, and the strategy is +especially effective in a project that relies on individual contributors who may +not have the time or resources to invest in a large upfront design effort. +However, this "bit by bit approach" doesn't always succeed, and sometimes we get +stuck or go down the wrong path and then change directions. + +Our process necessarily results in imperfect solutions being the "state of the +code" in some cases, and larger visions are not yet fully realized. However, the +community is good at driving things to completion in the long run. If you see +something that needs improvement or an area that is not yet fully realized, +please consider submitting an issue or PR to improve it. We are always looking +for more contributions. diff --git a/versions/55.0.0/_sources/contributor-guide/specification/index.rst.txt b/versions/55.0.0/_sources/contributor-guide/specification/index.rst.txt new file mode 100644 index 0000000000000..a34f0b19e4dea --- /dev/null +++ b/versions/55.0.0/_sources/contributor-guide/specification/index.rst.txt @@ -0,0 +1,35 @@ +.. 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. + +Specifications +============== + +We formalize some DataFusion semantics and behaviors through specification +documents. These specifications are useful to be used as references to help +resolve ambiguities during development or code reviews. + +You are also welcome to propose changes to existing specifications or create +new specifications as you see fit. All specifications are stored in the +`docs/source/specification` folder. Here is the list current active +specifications: + + +.. toctree:: + :maxdepth: 1 + + invariants + output-field-name-semantic diff --git a/versions/55.0.0/_sources/contributor-guide/specification/invariants.md.txt b/versions/55.0.0/_sources/contributor-guide/specification/invariants.md.txt new file mode 100644 index 0000000000000..c8de4e1d4e21d --- /dev/null +++ b/versions/55.0.0/_sources/contributor-guide/specification/invariants.md.txt @@ -0,0 +1,327 @@ + + +# Invariants + +This document enumerates invariants of DataFusion's logical and physical planes +(functions, and nodes). Some of these invariants are currently not enforced. +This document assumes that the reader is familiar with some of the codebase, +including rust arrow's RecordBatch and Array. + +## Rational + +DataFusion's computational model is built on top of a dynamically typed arrow +object, Array, that offers the interface `Array::as_any` to downcast itself to +its statically typed versions (e.g. `Int32Array`). DataFusion uses +`Array::data_type` to perform the respective downcasting on its physical +operations. DataFusion uses a dynamic type system because the queries being +executed are not always known at compile time: they are only known during the +runtime (or query time) of programs built with DataFusion. This document is +built on top of this principle. + +In dynamically typed interfaces, it is up to developers to enforce type +invariances. This document declares some of these invariants, so that users +know what they can expect from a query in DataFusion, and DataFusion developers +know what they need to enforce at the coding level. + +## Notation + +- Field or physical field: the tuple name, `arrow::DataType` and nullability flag (a bool whether values can be null), represented in this document by `PF(name, type, nullable)` +- Logical field: Field with a relation name. Represented in this document by `LF(relation, name, type, nullable)` +- Projected plan: plan with projection as the root node. +- Logical schema: a vector of logical fields, used by logical plan. +- Physical schema: a vector of physical fields, used by both physical plan and Arrow record batch. + +### Logical + +#### Function + +An object that knows its valid incoming logical fields and how to derive its +output logical field from its arguments' logical fields. A functions' output +field is itself a function of its input fields: + +``` +logical_field(lf1: LF, lf2: LF, ...) -> LF +``` + +Examples: + +- `plus(a,b) -> LF(None, "{a} Plus {b}", d(a.type,b.type), a.nullable | b.nullable)` where d is the function mapping input types to output type (`get_supertype` in our current implementation). +- `length(a) -> LF(None, "length({a})", u32, a.nullable)` + +#### Plan + +A tree composed of other plans and functions (e.g. `Projection c1 + c2, c1 - c2 AS sum12; Scan c1 as u32, c2 as u64`) +that knows how to derive its schema. + +Certain plans have a frozen schema (e.g. Scan), while others derive their +schema from their child nodes. + +#### Column + +An identifier in a logical plan consists of field name and relation name. + +### Physical + +#### Function + +An object that knows how to derive its physical field from its arguments' +physical fields, and also how to actually perform the computation on data. A +functions' output physical field is a function of its input physical fields: + +``` +physical_field(PF1, PF2, ...) -> PF +``` + +Examples: + +- `plus(a,b) -> PF("{a} Plus {b}", d(a.type,b.type), a.nullable | b.nullable)` where d is a complex function (`get_supertype` in our current implementation) whose computation is for each element in the columns, sum the two entries together and return it in the same type as the smallest type of both columns. +- `length(&str) -> PF("length({a})", u32, a.nullable)` whose computation is "count number of bytes in the string". + +#### Plan + +A tree (e.g. `Projection c1 + c2, c1 - c2 AS sum12; Scan c1 as u32, c2 as u64`) +that knows how to derive its metadata and compute itself. + +Note how the physical plane does not know how to derive field names: field +names are solely a property of the logical plane, as they are not needed in the +physical plane. + +#### Column + +A type of physical node in a physical plan consists of a field name and unique index. + +### Data Sources' registry + +A map of source name/relation -> Schema plus associated properties necessary to read data from it (e.g. file path). + +### Functions' registry + +A map of function name -> logical + physical function. + +### Physical Planner + +A function that knows how to derive a physical plan from a logical plan: + +``` +plan(LogicalPlan) -> PhysicalPlan +``` + +### Logical Optimizer + +A function that accepts a logical plan and returns an (optimized) logical plan +which computes the same results, but in a more efficient manner: + +``` +optimize(LogicalPlan) -> LogicalPlan +``` + +### Physical Optimizer + +A function that accepts a physical plan and returns an (optimized) physical +plan which computes the same results, but may differ based on the actual +hardware or execution environment being run: + +``` +optimize(PhysicalPlan) -> PhysicalPlan +``` + +### Builder + +A function that knows how to build a new logical plan from an existing logical +plan and some extra parameters. + +``` +build(logical_plan, params...) -> logical_plan +``` + +## Invariants + +The following subsections describe invariants. Since functions' output schema +depends on its arguments' schema (e.g. min, plus), the resulting schema can +only be derived based on a known set of input schemas (TableProvider). +Likewise, schemas of functions depend on the specific registry of functions +registered (e.g. does `my_op` return u32 or u64?). Thus, in this section, the +wording "same schema" is understood to mean "same schema under a given registry +of data sources and functions". + +### (relation, name) tuples in logical fields and logical columns are unique + +Every logical field's (relation, name) tuple in a logical schema MUST be unique. +Every logical column's (relation, name) tuple in a logical plan MUST be unique. + +This invariant guarantees that `SELECT t1.id, t2.id FROM t1 JOIN t2...` +unambiguously selects the field `t1.id` and `t2.id` in a logical schema in the +logical plane. + +#### Responsibility + +It is the logical builder and optimizer's responsibility to guarantee this +invariant. + +#### Validation + +Builder and optimizer MUST error if this invariant is violated on any logical +node that creates a new schema (e.g. scan, projection, aggregation, join, etc.). + +### Physical schema is consistent with data + +The contents of every Array in every RecordBatch in every partition returned by +a physical plan MUST be consistent with RecordBatch's schema, in that every +Array in the RecordBatch must be downcastable to its corresponding type +declared in the RecordBatch. + +#### Responsibility + +Physical functions MUST guarantee this invariant. This is particularly +important in aggregate functions, whose aggregating type may be different from +the intermediary types during calculations (e.g. sum(i32) -> i64). + +#### Validation + +Since the validation of this invariant is computationally expensive, execution +contexts CAN validate this invariant. It is acceptable for physical nodes to +`panic!` if their input does not satisfy this invariant. + +### Physical schema is consistent in physical functions + +The schema of every Array returned by a physical function MUST match the +DataType reported by the physical function itself. + +This ensures that when a physical function claims that it returns a type +(e.g. Int32), users can safely downcast its resulting Array to the +corresponding type (e.g. Int32Array), as well as to write data to formats that +have a schema with nullability flag (e.g. parquet). + +#### Responsibility + +It is the responsibility of the developer that writes a physical function to +guarantee this invariant. + +In particular: + +- The derived DataType matches the code it uses to build the array for every branch of valid input type combinations. +- The nullability flag matches how the values are built. + +#### Validation + +Since the validation of this invariant is computationally expensive, execution +contexts CAN validate this invariant. + +### The physical schema is invariant under planning + +The physical schema derived by a physical plan returned by the planner MUST be +equivalent to the physical schema derived by the logical plan passed to the +planner. Specifically: + +``` +plan(logical_plan).schema === logical_plan.physical_schema +``` + +Logical plan's physical schema is defined as logical schema with relation +qualifiers stripped for all logical fields: + +``` +logical_plan.physical_schema = vector[ strip_relation(f) for f in logical_plan.logical_fields ] +``` + +This is used to ensure that the physical schema of its (logical) plan is what +it gets in record batches, so that users can rely on the optimized logical plan +to know the resulting physical schema. + +Note that since a logical plan can be as simple as a single projection with a +single function, `Projection f(c1,c2)`, a corollary of this is that the +physical schema of every `logical function -> physical function` must be +invariant under planning. + +#### Responsibility + +Developers of physical and logical plans and planners MUST guarantee this +invariant for every triplet (logical plan, physical plan, conversion rule). + +#### Validation + +Planners MUST validate this invariant. In particular they MUST return an error +when, during planning, a physical function's derived schema does not match the +logical functions' derived schema. + +### The output schema equals the physical plan schema + +The schema of every RecordBatch in every partition outputted by a physical plan +MUST be equal to the schema of the physical plan. Specifically: + +``` +physical_plan.evaluate(batch).schema = physical_plan.schema +``` + +Together with other invariants, this ensures that the consumers of record +batches do not need to know the output schema of the physical plan; they can +safely rely on the record batch's schema to perform downscaling and naming. + +#### Responsibility + +Physical nodes MUST guarantee this invariant. + +#### Validation + +Execution Contexts CAN validate this invariant. + +### Logical schema is invariant under logical optimization + +The logical schema derived by a projected logical plan returned by the logical +optimizer MUST be equivalent to the logical schema derived by the logical plan +passed to the planner: + +``` +optimize(logical_plan).schema === logical_plan.schema +``` + +This is used to ensure that plans can be optimized without jeopardizing future +referencing logical columns (name and index) or assumptions about their +schemas. + +#### Responsibility + +Logical optimizers MUST guarantee this invariant. + +#### Validation + +Users of logical optimizers SHOULD validate this invariant. + +### Physical schema is invariant under physical optimization + +The physical schema derived by a projected physical plan returned by the +physical optimizer MUST match the physical schema derived by the physical plan +passed to the planner: + +``` +optimize(physical_plan).schema === physical_plan.schema +``` + +This is used to ensure that plans can be optimized without jeopardizing future +references of logical columns (name and index) or assumptions about their +schemas. + +#### Responsibility + +Optimizers MUST guarantee this invariant. + +#### Validation + +Users of optimizers SHOULD validate this invariant. diff --git a/versions/55.0.0/_sources/contributor-guide/specification/output-field-name-semantic.md.txt b/versions/55.0.0/_sources/contributor-guide/specification/output-field-name-semantic.md.txt new file mode 100644 index 0000000000000..1a3c373c9bbd5 --- /dev/null +++ b/versions/55.0.0/_sources/contributor-guide/specification/output-field-name-semantic.md.txt @@ -0,0 +1,212 @@ + + +# Output field name semantics + +This specification documents how field names in output record batches should be +generated based on given user queries. The field name rules apply to +DataFusion queries planned from both SQL queries and Dataframe APIs. + +## Field name rules + +- All bare column field names MUST not contain relation/table qualifier. + - Both `SELECT t1.id`, `SELECT id` and `df.select_columns(&["id"])` SHOULD result in field name: `id` +- All compound column field names MUST contain relation/table qualifier. + - `SELECT foo + bar` SHOULD result in field name: `table.foo PLUS table.bar` +- Function names MUST be converted to lowercase. + - `SELECT AVG(c1)` SHOULD result in field name: `avg(table.c1)` +- Literal string MUST not be wrapped with quotes or double quotes. + - `SELECT 'foo'` SHOULD result in field name: `foo` +- Operator expressions MUST be wrapped with parentheses. + - `SELECT -2` SHOULD result in field name: `(- 2)` +- Operator and operand MUST be separated by spaces. + - `SELECT 1+2` SHOULD result in field name: `(1 + 2)` +- Function arguments MUST be separated by a comma `,` and a space. + - `SELECT f(c1,c2)` and `df.select(vec![f.udf("f")?.call(vec![col("c1"), col("c2")])])` SHOULD result in field name: `f(table.c1, table.c2)` + +## Appendices + +### Examples and comparison with other systems + +Data schema for test sample queries: + +``` +CREATE TABLE t1 (id INT, a VARCHAR(5)); +INSERT INTO t1 (id, a) VALUES (1, 'foo'); +INSERT INTO t1 (id, a) VALUES (2, 'bar'); + +CREATE TABLE t2 (id INT, b VARCHAR(5)); +INSERT INTO t2 (id, b) VALUES (1, 'hello'); +INSERT INTO t2 (id, b) VALUES (2, 'world'); +``` + +#### Projected columns + +Query: + +``` +SELECT t1.id, a, t2.id, b +FROM t1 +JOIN t2 ON t1.id = t2.id +``` + +DataFusion Arrow record batches output: + +| id | a | id | b | +| --- | --- | --- | ----- | +| 1 | foo | 1 | hello | +| 2 | bar | 2 | world | + +Spark, MySQL 8 and PostgreSQL 13 output: + +| id | a | id | b | +| --- | --- | --- | ----- | +| 1 | foo | 1 | hello | +| 2 | bar | 2 | world | + +SQLite 3 output: + +| id | a | b | +| --- | --- | ----- | +| 1 | foo | hello | +| 2 | bar | world | + +#### Function transformed columns + +Query: + +``` +SELECT ABS(t1.id), abs(-id) FROM t1; +``` + +DataFusion Arrow record batches output: + +| abs(t1.id) | abs((- t1.id)) | +| ---------- | -------------- | +| 1 | 1 | +| 2 | 2 | + +Spark output: + +| abs(id) | abs((- id)) | +| ------- | ----------- | +| 1 | 1 | +| 2 | 2 | + +MySQL 8 output: + +| ABS(t1.id) | abs(-id) | +| ---------- | -------- | +| 1 | 1 | +| 2 | 2 | + +PostgreSQL 13 output: + +| abs | abs | +| --- | --- | +| 1 | 1 | +| 2 | 2 | + +SQlite 3 output: + +| ABS(t1.id) | abs(-id) | +| ---------- | -------- | +| 1 | 1 | +| 2 | 2 | + +#### Function with operators + +Query: + +``` +SELECT t1.id + ABS(id), ABS(id * t1.id) FROM t1; +``` + +DataFusion Arrow record batches output: + +| t1.id + abs(t1.id) | abs(t1.id \* t1.id) | +| ------------------ | ------------------- | +| 2 | 1 | +| 4 | 4 | + +Spark output: + +| id + abs(id) | abs(id \* id) | +| ------------ | ------------- | +| 2 | 1 | +| 4 | 4 | + +MySQL 8 output: + +| t1.id + ABS(id) | ABS(id \* t1.id) | +| --------------- | ---------------- | +| 2 | 1 | +| 4 | 4 | + +PostgreSQL output: + +| ?column? | abs | +| -------- | --- | +| 2 | 1 | +| 4 | 4 | + +SQLite output: + +| t1.id + ABS(id) | ABS(id \* t1.id) | +| --------------- | ---------------- | +| 2 | 1 | +| 4 | 4 | + +#### Project literals + +Query: + +``` +SELECT 1, 2+5, 'foo_bar'; +``` + +DataFusion Arrow record batches output: + +| 1 | (2 + 5) | foo_bar | +| --- | ------- | ------- | +| 1 | 7 | foo_bar | + +Spark output: + +| 1 | (2 + 5) | foo_bar | +| --- | ------- | ------- | +| 1 | 7 | foo_bar | + +MySQL output: + +| 1 | 2+5 | foo_bar | +| --- | --- | ------- | +| 1 | 7 | foo_bar | + +PostgreSQL output: + +| ?column? | ?column? | ?column? | +| -------- | -------- | -------- | +| 1 | 7 | foo_bar | + +SQLite 3 output: + +| 1 | 2+5 | 'foo_bar' | +| --- | --- | --------- | +| 1 | 7 | foo_bar | diff --git a/versions/55.0.0/_sources/contributor-guide/testing.md.txt b/versions/55.0.0/_sources/contributor-guide/testing.md.txt new file mode 100644 index 0000000000000..3b644f610b90e --- /dev/null +++ b/versions/55.0.0/_sources/contributor-guide/testing.md.txt @@ -0,0 +1,270 @@ + + +# Testing + +Tests are critical to ensure that DataFusion is working properly and +is not accidentally broken during refactorings. All new features +should have test coverage and the entire test suite is run as part of CI. + +## Testing Quick Start + +While developing a feature or bug fix, best practice is to run the smallest set +of tests that gives confidence for your change, then expand as needed. + +Initially, run the tests in the crates you changed. For example, if you made changes +to files in `datafusion-optimizer/src`, run the corresponding crate tests: + +```shell +cargo test -p datafusion-optimizer +``` + +Then, run the `sqllogictest` suite, which provides a strong speed–coverage tradeoff for development: it runs quickly while offering broad regression coverage across most SQL behavior in DataFusion. + +```shell +cargo test --profile=ci --test sqllogictests +``` + +Finally, before submitting a PR, run the tests for the core `datafusion` and +`datafusion-cli` crates: + +```shell +cargo test -p datafusion +cargo test -p datafusion-cli +``` + +Some integration tests require optional external services such as Docker-backed +containers and may skip when unavailable. + +## Testing Overview + +DataFusion has several levels of tests in its [Test Pyramid] and tries to follow +the Rust standard [Testing Organization] described in [The Book]. + +Run tests using `cargo`: + +```shell +cargo test +``` + +You can also use other runners such as [cargo-nextest]. + +```shell +cargo nextest run +``` + +[test pyramid]: https://martinfowler.com/articles/practical-test-pyramid.html +[testing organization]: https://doc.rust-lang.org/book/ch11-03-test-organization.html +[the book]: https://doc.rust-lang.org/book/ +[cargo-nextest]: https://nexte.st/ + +## Unit tests + +Tests for code in an individual module are defined in the same source file with a `test` module, following Rust convention. + +For example, to run tests in the `datafusion` crate: + +```shell +cargo test -p datafusion +``` + +The [test_util] module provides useful macros to write unit tests effectively, such as [`assert_batches_sorted_eq`] and [`assert_batches_eq`] for RecordBatches and [`assert_contains`] / [`assert_not_contains`] which are used extensively in the codebase. + +[test_util]: https://github.com/apache/datafusion/tree/main/datafusion/common/src/test_util.rs +[`assert_batches_sorted_eq`]: https://docs.rs/datafusion/latest/datafusion/macro.assert_batches_sorted_eq.html +[`assert_batches_eq`]: https://docs.rs/datafusion/latest/datafusion/macro.assert_batches_eq.html +[`assert_contains`]: https://docs.rs/datafusion/latest/datafusion/common/macro.assert_contains.html +[`assert_not_contains`]: https://docs.rs/datafusion/latest/datafusion/common/macro.assert_not_contains.html + +## sqllogictests Tests + +DataFusion's SQL implementation is tested using [sqllogictest](https://github.com/apache/datafusion/tree/main/datafusion/sqllogictest). You can run these tests with commands like: + +```shell +# Run all tests +cargo test --profile=ci --test sqllogictests +# Run a specific test file +cargo test --profile=ci --test sqllogictests -- aggregate.slt +# Run a specific test file and update expected outputs +cargo test --profile=ci --test sqllogictests -- aggregate.slt --complete +# Run and update expected outputs for all test files +cargo test --profile=ci --test sqllogictests -- --complete +``` + +`sqllogictests` may be less convenient for new contributors who are familiar with writing `.rs` tests as they require learning another tool. However, `sqllogictest` based tests are much easier to develop and maintain as they 1) do not require a slow recompile/link cycle and 2) can be automatically updated. + +Like similar systems such as [DuckDB](https://duckdb.org/dev/testing), DataFusion has chosen to trade off a slightly higher barrier to contribution for longer term maintainability. + +DataFusion has integrated [sqlite's test suite](https://sqlite.org/sqllogictest/doc/trunk/about.wiki) as a supplemental test suite that is run whenever a PR is merged into DataFusion. To run it manually please refer to the [README](https://github.com/apache/datafusion/blob/main/datafusion/sqllogictest/README.md#running-tests-sqlite) file for instructions. + +## Snapshot testing (`cargo insta`) + +[Insta](https://github.com/mitsuhiko/insta) is used for snapshot testing. Snapshots are generated +and compared on each test run. If the output changes, tests will fail. + +To review the changes, you can use Insta CLI: + +```shell +cargo install cargo-insta +cargo insta review +``` + +## Extended Tests + +In addition to the standard CI test suite that is run on all PRs prior to merge, +DataFusion has "extended" tests (defined in [extended.yml]) that are run on each +commit to `main`. These tests rarely fail but take significantly longer to run +than the standard test suite and add important test coverage such as ensuring +correctness when there are hash collisions and running the relevant portions of +the entire [sqlite test suite]. You can run the extended tests +locally by following the [instructions in the documentation]. + +[sqlite test suite]: https://www.sqlite.org/sqllogictest/dir?ci=tip +[instructions in the documentation]: https://github.com/apache/datafusion/tree/main/datafusion/sqllogictest#running-tests-sqlite +[extended.yml]: https://github.com/apache/datafusion/blob/main/.github/workflows/extended.yml + +## Rust Integration Tests + +There are several public interface tests for the DataFusion library in the [tests](https://github.com/apache/datafusion/tree/main/datafusion/core/tests) directory. + +You can run these tests individually using `cargo` as normal command such as + +```shell +cargo test -p datafusion --test parquet_integration +``` + +## SQL "Fuzz" testing + +DataFusion uses the [SQLancer] for "fuzz" testing: it generates random SQL +queries and execute them against DataFusion to find bugs. + +The code is in the [datafusion-sqllancer] repository, and we welcome further +contributions. Kudos to [@2010YOUY01] for the initial implementation. + +[sqlancer]: https://github.com/sqlancer/sqlancer +[datafusion-sqllancer]: https://github.com/datafusion-contrib/datafusion-sqllancer +[@2010youy01]: https://github.com/2010YOUY01 + +## Documentation Examples + +We use Rust [doctest] to verify examples from the documentation are correct and +up-to-date. These tests are run as part of our CI and you can run them them +locally with the following command: + +```shell +cargo test --doc +``` + +### API Documentation Examples + +As with other Rust projects, examples in doc comments in `.rs` files are +automatically checked to ensure they work and evolve along with the code. + +### User Guide Documentation + +Rust example code from the user guide (anything marked with \`\`\`rust) is also +tested in the same way using the [doc_comment] crate. See the end of +[core/src/lib.rs] for more details. + +[doctest]: https://doc.rust-lang.org/rust-by-example/testing/doc_testing.html +[doc_comment]: https://docs.rs/doc-comment/latest/doc_comment +[core/src/lib.rs]: https://github.com/apache/datafusion/blob/main/datafusion/core/src/lib.rs#L583 + +## Documentation Link Checks + +Run the internal markdown link check locally: + +```shell +source ci/scripts/utils/tool_versions.sh +cargo install lychee --locked --version "${LYCHEE_VERSION}" +bash ci/scripts/markdown_link_check.sh +``` + +Notes: + +- The script is run with `bash` and is compatible with the default Bash on macOS (no `mapfile` dependency). +- The CI configuration currently checks internal markdown links only. External `http(s)` and `mailto` links are excluded to avoid flaky failures. + +When a link is broken, lychee prints the file and URL/path that failed. For example: + +```text +[docs/source/user-guide/cli/overview.md]: + [ERROR] file:///.../docs/source/user-guide/cli/missing-page.md | Cannot find file: File not found. Check if file exists and path is correct +``` + +Rust doc comments are validated by rustdoc in CI and can be checked locally with: + +```shell +bash ci/scripts/rust_docs.sh +``` + +## Benchmarks + +### Criterion Benchmarks + +[Criterion](https://docs.rs/criterion/latest/criterion/index.html) is a statistics-driven micro-benchmarking framework used by DataFusion for evaluating the performance of specific code-paths. In particular, the criterion benchmarks help to both guide optimisation efforts, and prevent performance regressions within DataFusion. + +Criterion integrates with Cargo's built-in [benchmark support](https://doc.rust-lang.org/cargo/commands/cargo-bench.html) and a given benchmark can be run with + +``` +cargo bench --bench BENCHMARK_NAME +``` + +A full list of benchmarks can be found [here](https://github.com/apache/datafusion/tree/main/datafusion/core/benches). + +_[cargo-criterion](https://github.com/bheisler/cargo-criterion) may also be used for more advanced reporting._ + +### Parquet SQL Benchmarks + +The parquet SQL benchmarks can be run with + +``` + cargo bench --bench parquet_query_sql +``` + +These randomly generate a parquet file, and then benchmark queries sourced from [parquet_query_sql.sql](../../../datafusion/core/benches/parquet_query_sql.sql) against it. This can therefore be a quick way to add coverage of particular query and/or data paths. + +If the environment variable `PARQUET_FILE` is set, the benchmark will run queries against this file instead of a randomly generated one. This can be useful for performing multiple runs, potentially with different code, against the same source data, or for testing against a custom dataset. + +The benchmark will automatically remove any generated parquet file on exit, however, if interrupted (e.g. by CTRL+C) it will not. This can be useful for analysing the particular file after the fact, or preserving it to use with `PARQUET_FILE` in subsequent runs. + +### Comparing Baselines + +By default, Criterion.rs will compare the measurements against the previous run (if any). Sometimes it's useful to keep a set of measurements around for several runs. For example, you might want to make multiple changes to the code while comparing against the master branch. For this situation, Criterion.rs supports custom baselines. + +``` + git checkout main + cargo bench --bench sql_planner -- --save-baseline main + git checkout YOUR_BRANCH + cargo bench --bench sql_planner -- --baseline main +``` + +Note: For MacOS it may be required to run `cargo bench` with `sudo` + +``` +sudo cargo bench ... +``` + +More information on [Baselines](https://bheisler.github.io/criterion.rs/book/user_guide/command_line_options.html#baselines) + +### Upstream Benchmark Suites + +Instructions and tooling for running upstream benchmark suites against DataFusion can be found in [benchmarks](https://github.com/apache/datafusion/tree/main/benchmarks). + +These are valuable for comparative evaluation against alternative Arrow implementations and query engines. diff --git a/versions/55.0.0/_sources/download.md.txt b/versions/55.0.0/_sources/download.md.txt new file mode 100644 index 0000000000000..34296262071c8 --- /dev/null +++ b/versions/55.0.0/_sources/download.md.txt @@ -0,0 +1,77 @@ + + +# Download + +Most users use DataFusion as a library in their Rust projects by adding it as a dependency +in their `Cargo.toml` file and downloading it from the Rust [crates.io] package registry. + +For example: + +```toml +[dependencies] +datafusion = "55.0.0" +``` + +While DataFusion is distributed via [crates.io] as a convenience, the +official Apache DataFusion releases are provided as source artifacts. + +[crates.io]: https://crates.io/crates/datafusion + +## Releases + +You can find the latest releases, signatures and checksums on +the [ASF Release Page](https://dist.apache.org/repos/dist/release/datafusion) + +For previous releases, please check the [archive](https://archive.apache.org/dist/datafusion/). + +For releases earlier than 37.0.0, please check [Arrow's archive](https://archive.apache.org/dist/arrow/). + +## Notes + +- When downloading a release, please verify the OpenPGP compatible signature (or failing that, check the SHA-512); these should be fetched from the main Apache site. +- The [KEYS] file contains the public keys used for signing release. It is recommended that (when possible) a web of trust is used to confirm the identity of these keys. +- Please download the [KEYS] file as well as the .asc signature files. + +[keys]: https://downloads.apache.org/datafusion/KEYS + +### To verify the signature of the release artifact + +You will need to download both the release artifact and the .asc signature file for that artifact. Then verify the signature by: + +- Download the KEYS file and the .asc signature files for the relevant release artifacts. +- Import the KEYS file to your GPG keyring: + + ```shell + gpg --import KEYS + ``` + +- Verify the signature of the release artifact using the following command: + + ```shell + gpg --verify .asc + ``` + +### To verify the checksum of the release artifact + +You will need to download both the release artifact and the .sha512 checksum file for that artifact. Then verify the checksum by: + +```shell +shasum -a 512 -c .sha512 +``` diff --git a/versions/55.0.0/_sources/index.rst.txt b/versions/55.0.0/_sources/index.rst.txt new file mode 100644 index 0000000000000..ea6ebb74c08b1 --- /dev/null +++ b/versions/55.0.0/_sources/index.rst.txt @@ -0,0 +1,186 @@ +.. 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. + +.. image:: _static/images/original.svg + :alt: DataFusion Logo + :class: light-logo + +.. image:: _static/images/original_dark.svg + :alt: DataFusion Logo + :class: dark-logo + +================= +Apache DataFusion +================= + +.. raw:: html + + + + + +DataFusion is an extensible query engine written in `Rust `_ that +uses `Apache Arrow `_ as its in-memory format. + +The documentation on this site is for the `core DataFusion project `_, which contains +libraries and binaries for developers building fast and feature rich database and analytic systems, +customized to particular workloads. See `use cases `_ for examples. + +The following related subprojects target end users and have separate documentation. + +- `DataFusion Python `_ offers a Python interface for SQL and DataFrame + queries. +- `DataFusion Java `_ offers a Java interface for SQL and DataFrame + queries. +- `DataFusion Comet `_ is an accelerator for Apache Spark based on + DataFusion. +- `DataFusion Ballista `_ is distributed processing extension for DataFusion. + +"Out of the box," DataFusion offers `SQL `_ +and `Dataframe `_ APIs, +excellent `performance `_, built-in support for CSV, Parquet, JSON, and Avro, +extensive customization, and a great `community`_. +`Python Bindings `_ are also available. +`Ballista `_ is Apache DataFusion extension enabling the parallelized execution of workloads across multiple nodes in a distributed environment. + +DataFusion features a full query planner, a columnar, streaming, multi-threaded, +vectorized execution engine, and partitioned data sources. You can +customize DataFusion at almost all points including additional data sources, +query languages, functions, custom operators and more. +See the `Architecture `_ section for more details. + +To get started, see + +* The `example usage`_ section of the user guide and the `datafusion-examples`_ directory. +* The `library user guide`_ for examples of using DataFusion's extension APIs +* The `developer’s guide`_ for contributing and `communication`_ for getting in touch with us. + +.. _example usage: user-guide/example-usage.html +.. _datafusion-examples: https://github.com/apache/datafusion/tree/main/datafusion-examples +.. _developer’s guide: contributor-guide/index.html#developer-s-guide +.. _library user guide: library-user-guide/index.html +.. _community: contributor-guide/communication.html +.. _communication: contributor-guide/communication.html + +.. _toc.asf-links: +.. toctree:: + :maxdepth: 1 + :caption: ASF Links + + Apache Software Foundation + License + Donate + Thanks + Security + +.. _toc.links: +.. toctree:: + :maxdepth: 1 + :caption: Links + + GitHub and Issue Tracker + crates.io + API Docs + Blog + Code of conduct + Download + +.. _toc.guide: +.. toctree:: + :maxdepth: 1 + :caption: User Guide + + user-guide/introduction + user-guide/example-usage + user-guide/features + user-guide/concepts-readings-events + user-guide/crate-configuration + user-guide/cli/index + user-guide/dataframe + user-guide/arrow-introduction + user-guide/expressions + user-guide/sql/index + user-guide/configs + user-guide/explain-usage + user-guide/parquet-content-defined-chunking + user-guide/metrics + user-guide/faq + +.. _toc.library-user-guide: + +.. toctree:: + :maxdepth: 1 + :caption: Library User Guide + + library-user-guide/index + library-user-guide/upgrading/index + library-user-guide/extensions + library-user-guide/using-the-sql-api + library-user-guide/extending-sql + library-user-guide/working-with-exprs + library-user-guide/using-the-dataframe-api + library-user-guide/building-logical-plans + library-user-guide/catalogs + library-user-guide/functions/index + library-user-guide/custom-table-providers + library-user-guide/table-constraints + library-user-guide/extending-operators + library-user-guide/profiling + library-user-guide/query-optimizer + +.. .. _toc.contributor-guide: + +.. toctree:: + :maxdepth: 1 + :caption: Contributor Guide + + contributor-guide/index + contributor-guide/pr_review + contributor-guide/communication + contributor-guide/development_environment + contributor-guide/architecture + contributor-guide/architecture/dependency-graph + contributor-guide/testing + contributor-guide/api-health + contributor-guide/howtos + contributor-guide/release_management + contributor-guide/roadmap + contributor-guide/governance + contributor-guide/inviting + contributor-guide/specification/index + contributor-guide/gsoc/index + +.. _toc.subprojects: + +.. toctree:: + :maxdepth: 1 + :caption: DataFusion Subprojects + + DataFusion Ballista + DataFusion Comet + DataFusion Java + DataFusion Python diff --git a/versions/55.0.0/_sources/library-user-guide/building-logical-plans.md.txt b/versions/55.0.0/_sources/library-user-guide/building-logical-plans.md.txt new file mode 100644 index 0000000000000..6efd97879ac4d --- /dev/null +++ b/versions/55.0.0/_sources/library-user-guide/building-logical-plans.md.txt @@ -0,0 +1,226 @@ + + +# Building Logical Plans + +A logical plan is a structured representation of a database query that describes the high-level operations and +transformations needed to retrieve data from a database or data source. It abstracts away specific implementation +details and focuses on the logical flow of the query, including operations like filtering, sorting, and joining tables. + +This logical plan serves as an intermediate step before generating an optimized physical execution plan. This is +explained in more detail in the [Query Planning and Execution Overview] section of the [Architecture Guide]. + +## Building Logical Plans Manually + +DataFusion's [LogicalPlan] is an enum containing variants representing all the supported operators, and also +contains an `Extension` variant that allows projects building on DataFusion to add custom logical operators. + +It is possible to create logical plans by directly creating instances of the [LogicalPlan] enum as shown, but it is +much easier to use the [LogicalPlanBuilder], which is described in the next section. + +Here is an example of building a logical plan directly: + +```rust +use datafusion::common::DataFusionError; +use datafusion::arrow::datatypes::{DataType, Field, Schema, SchemaRef}; +use datafusion::logical_expr::{Filter, LogicalPlan, TableScan, LogicalTableSource}; +use datafusion::prelude::*; +use std::sync::Arc; + +fn main() -> Result<(), DataFusionError> { + // create a logical table source + let schema = Schema::new(vec![ + Field::new("id", DataType::Int32, true), + Field::new("name", DataType::Utf8, true), + ]); + let table_source = LogicalTableSource::new(SchemaRef::new(schema)); + + // create a TableScan plan + let projection = None; // optional projection + let filters = vec![]; // optional filters to push down + let fetch = None; // optional LIMIT + let table_scan = LogicalPlan::TableScan(TableScan::try_new( + "person", + Arc::new(table_source), + projection, + filters, + fetch, + )? + ); + + // create a Filter plan that evaluates `id > 500` that wraps the TableScan + let filter_expr = col("id").gt(lit(500)); + let plan = LogicalPlan::Filter(Filter::try_new(filter_expr, Arc::new(table_scan)) ? ); + + // print the plan + println!("{}", plan.display_indent_schema()); + Ok(()) +} +``` + +This example produces the following plan: + +```text +Filter: person.id > Int32(500) [id:Int32;N, name:Utf8;N] + TableScan: person [id:Int32;N, name:Utf8;N] +``` + +## Building Logical Plans with LogicalPlanBuilder + +DataFusion logical plans can be created using the [LogicalPlanBuilder] struct. There is also a [DataFrame] API which is +a higher-level API that delegates to [LogicalPlanBuilder]. + +There are several functions that can be used to create a new builder, such as + +- `empty` - create an empty plan with no fields +- `values` - create a plan from a set of literal values +- `scan` - create a plan representing a table scan +- `scan_with_filters` - create a plan representing a table scan with filters + +Once the builder is created, transformation methods can be called to declare that further operations should be +performed on the plan. Note that all we are doing at this stage is building up the logical plan structure. No query +execution will be performed. + +Here are some examples of transformation methods, but for a full list, refer to the [LogicalPlanBuilder] API documentation. + +- `filter` +- `limit` +- `sort` +- `distinct` +- `join` + +The following example demonstrates building the same simple query plan as the previous example, with a table scan followed by a filter. + + + +```rust +use datafusion::common::DataFusionError; +use datafusion::arrow::datatypes::{DataType, Field, Schema, SchemaRef}; +use datafusion::logical_expr::{LogicalPlanBuilder, LogicalTableSource}; +use datafusion::prelude::*; +use std::sync::Arc; + +fn main() -> Result<(), DataFusionError> { + // create a logical table source + let schema = Schema::new(vec![ + Field::new("id", DataType::Int32, true), + Field::new("name", DataType::Utf8, true), + ]); + let table_source = LogicalTableSource::new(SchemaRef::new(schema)); + + // optional projection + let projection = None; + + // create a LogicalPlanBuilder for a table scan + let builder = LogicalPlanBuilder::scan("person", Arc::new(table_source), projection)?; + + // perform a filter operation and build the plan + let plan = builder + .filter(col("id").gt(lit(500)))? // WHERE id > 500 + .build()?; + + // print the plan + println!("{}", plan.display_indent_schema()); + Ok(()) +} +``` + +This example produces the following plan: + +```text +Filter: person.id > Int32(500) [id:Int32;N, name:Utf8;N] + TableScan: person [id:Int32;N, name:Utf8;N] +``` + +## Translating Logical Plan to Physical Plan + +Logical plans can not be directly executed. They must be "compiled" into an +[`ExecutionPlan`], which is often referred to as a "physical plan". + +Compared to `LogicalPlan`s, `ExecutionPlan`s have many more details such as +specific algorithms and detailed optimizations. Given a +`LogicalPlan`, the easiest way to create an `ExecutionPlan` is using +[`SessionState::create_physical_plan`] as shown below + +```rust +use datafusion::datasource::{provider_as_source, MemTable}; +use datafusion::common::DataFusionError; +use datafusion::physical_plan::display::DisplayableExecutionPlan; +use datafusion::arrow::datatypes::{DataType, Field, Schema, SchemaRef}; +use datafusion::logical_expr::{LogicalPlanBuilder, LogicalTableSource}; +use datafusion::prelude::*; +use std::sync::Arc; + +// Creating physical plans may access remote catalogs and data sources +// thus it must be run with an async runtime. +#[tokio::main] +async fn main() -> Result<(), DataFusionError> { + + // create a default table source + let schema = Schema::new(vec![ + Field::new("id", DataType::Int32, true), + Field::new("name", DataType::Utf8, true), + ]); + // To create an ExecutionPlan we must provide an actual + // TableProvider. For this example, we don't provide any data + // but in production code, this would have `RecordBatch`es with + // in memory data + let table_provider = Arc::new(MemTable::try_new(Arc::new(schema), vec![vec![]])?); + // Use the provider_as_source function to convert the TableProvider to a table source + let table_source = provider_as_source(table_provider); + + // create a LogicalPlanBuilder for a table scan without projection or filters + let logical_plan = LogicalPlanBuilder::scan("person", table_source, None)?.build()?; + + // Now create the physical plan by calling `create_physical_plan` + let ctx = SessionContext::new(); + let physical_plan = ctx.state().create_physical_plan(&logical_plan).await?; + + // print the plan + println!("{}", DisplayableExecutionPlan::new(physical_plan.as_ref()).indent(true)); + Ok(()) +} +``` + +This example produces the following physical plan: + +```text +DataSourceExec: partitions=0, partition_sizes=[] +``` + +## Table Sources + +The previous examples use a [LogicalTableSource], which is used for tests and documentation in DataFusion, and is also +suitable if you are using DataFusion to build logical plans but do not use DataFusion's physical planner. + +However, it is more common to use a [TableProvider]. To get a [TableSource] from a +[TableProvider], use [provider_as_source] or [DefaultTableSource]. + +[query planning and execution overview]: https://docs.rs/datafusion/latest/datafusion/index.html#query-planning-and-execution-overview +[architecture guide]: https://docs.rs/datafusion/latest/datafusion/index.html#architecture +[logicalplan]: https://docs.rs/datafusion-expr/latest/datafusion_expr/logical_plan/enum.LogicalPlan.html +[logicalplanbuilder]: https://docs.rs/datafusion-expr/latest/datafusion_expr/logical_plan/builder/struct.LogicalPlanBuilder.html +[dataframe]: using-the-dataframe-api.md +[logicaltablesource]: https://docs.rs/datafusion-expr/latest/datafusion_expr/logical_plan/builder/struct.LogicalTableSource.html +[defaulttablesource]: https://docs.rs/datafusion/latest/datafusion/datasource/default_table_source/struct.DefaultTableSource.html +[provider_as_source]: https://docs.rs/datafusion/latest/datafusion/datasource/default_table_source/fn.provider_as_source.html +[tableprovider]: https://docs.rs/datafusion/latest/datafusion/datasource/trait.TableProvider.html +[tablesource]: https://docs.rs/datafusion-expr/latest/datafusion_expr/trait.TableSource.html +[`executionplan`]: https://docs.rs/datafusion/latest/datafusion/physical_plan/trait.ExecutionPlan.html +[`sessionstate::create_physical_plan`]: https://docs.rs/datafusion/latest/datafusion/execution/session_state/struct.SessionState.html#method.create_physical_plan diff --git a/versions/55.0.0/_sources/library-user-guide/catalogs.md.txt b/versions/55.0.0/_sources/library-user-guide/catalogs.md.txt new file mode 100644 index 0000000000000..fc1d0abb7823a --- /dev/null +++ b/versions/55.0.0/_sources/library-user-guide/catalogs.md.txt @@ -0,0 +1,315 @@ + + +# Catalogs, Schemas, and Tables + +This section describes how to create and manage catalogs, schemas, and tables in DataFusion. For those wanting to dive into the code quickly please see the [example](https://github.com/apache/datafusion/blob/main/datafusion-examples/examples/data_io/catalog.rs). + +## General Concepts + +Catalog providers, catalogs, schemas, and tables are organized in a hierarchy. A `CatalogProviderList` contains `CatalogProvider`s, a `CatalogProvider` contains `SchemaProviders` and a `SchemaProvider` contains `TableProvider`s. + +DataFusion comes with a basic in memory catalog functionality in the [`catalog` module]. You can use these in memory implementations as is, or extend DataFusion with your own catalog implementations, for example based on local files or files on remote object storage. + +DataFusion supports DDL queries (e.g. `CREATE TABLE`) using the catalog API described in this section. See the [TableProvider] section for information on DML queries (e.g. `INSERT INTO`). + +[`catalog` module]: https://docs.rs/datafusion/latest/datafusion/catalog/index.html +[tableprovider]: ./custom-table-providers.md + +Similarly to other concepts in DataFusion, you'll implement various traits to create your own catalogs, schemas, and tables. The following sections describe the traits you'll need to implement. + +The `CatalogProviderList` trait has methods to register new catalogs, get a catalog by name and list all catalogs .The `CatalogProvider` trait has methods to set a schema to a name, get a schema by name, and list all schemas. The `SchemaProvider`, which can be registered with a `CatalogProvider`, has methods to set a table to a name, get a table by name, list all tables, deregister a table, and check for a table's existence. The `TableProvider` trait has methods to scan underlying data and use it in DataFusion. The `TableProvider` trait is covered in more detail [here](./custom-table-providers.md). + +In the following example, we'll implement an in memory catalog, starting with the `SchemaProvider` trait as we need one to register with the `CatalogProvider`. Finally we will implement `CatalogProviderList` to register the `CatalogProvider`. + +## Implementing `MemorySchemaProvider` + +The `MemorySchemaProvider` is a simple implementation of the `SchemaProvider` trait. It stores state (i.e. tables) in a `DashMap`, which then underlies the `SchemaProvider` trait. + +```rust +use std::sync::Arc; +use dashmap::DashMap; +use datafusion::catalog::{TableProvider, SchemaProvider}; + +#[derive(Debug)] +pub struct MemorySchemaProvider { + tables: DashMap>, +} +``` + +`tables` is the key-value pair described above. The underlying state could also be another data structure or other storage mechanism such as a file or transactional database. + +Then we implement the `SchemaProvider` trait for `MemorySchemaProvider`. + +```rust +# use std::sync::Arc; +# use dashmap::DashMap; +# use datafusion::catalog::TableProvider; +# +# #[derive(Debug)] +# pub struct MemorySchemaProvider { +# tables: DashMap>, +# } + +use std::any::Any; +use datafusion::catalog::SchemaProvider; +use async_trait::async_trait; +use datafusion::common::{Result, exec_err}; + +#[async_trait] +impl SchemaProvider for MemorySchemaProvider { + fn table_names(&self) -> Vec { + self.tables + .iter() + .map(|table| table.key().clone()) + .collect() + } + + async fn table(&self, name: &str) -> Result>> { + Ok(self.tables.get(name).map(|table| table.value().clone())) + } + + fn register_table( + &self, + name: String, + table: Arc, + ) -> Result>> { + if self.table_exist(name.as_str()) { + return exec_err!( + "The table {name} already exists" + ); + } + Ok(self.tables.insert(name, table)) + } + + fn deregister_table(&self, name: &str) -> Result>> { + Ok(self.tables.remove(name).map(|(_, table)| table)) + } + + fn table_exist(&self, name: &str) -> bool { + self.tables.contains_key(name) + } +} +``` + +Without getting into a `CatalogProvider` implementation, we can create a `MemorySchemaProvider` and register `TableProvider`s with it. + +```rust +# use std::sync::Arc; +# use dashmap::DashMap; +# use datafusion::catalog::TableProvider; +# +# #[derive(Debug)] +# pub struct MemorySchemaProvider { +# tables: DashMap>, +# } +# +# use std::any::Any; +# use datafusion::catalog::SchemaProvider; +# use async_trait::async_trait; +# use datafusion::common::{Result, exec_err}; +# +# #[async_trait] +# impl SchemaProvider for MemorySchemaProvider { +# fn table_names(&self) -> Vec { +# self.tables +# .iter() +# .map(|table| table.key().clone()) +# .collect() +# } +# +# async fn table(&self, name: &str) -> Result>> { +# Ok(self.tables.get(name).map(|table| table.value().clone())) +# } +# +# fn register_table( +# &self, +# name: String, +# table: Arc, +# ) -> Result>> { +# if self.table_exist(name.as_str()) { +# return exec_err!( +# "The table {name} already exists" +# ); +# } +# Ok(self.tables.insert(name, table)) +# } +# +# fn deregister_table(&self, name: &str) -> Result>> { +# Ok(self.tables.remove(name).map(|(_, table)| table)) +# } +# +# fn table_exist(&self, name: &str) -> bool { +# self.tables.contains_key(name) +# } +# } + +use arrow::datatypes::{DataType, Field, Schema, SchemaRef}; +use arrow::record_batch::RecordBatch; +use datafusion::datasource::MemTable; +use arrow::array::{self, Array, ArrayRef, Int32Array}; + +impl MemorySchemaProvider { + /// Instantiates a new MemorySchemaProvider with an empty collection of tables. + pub fn new() -> Self { + Self { + tables: DashMap::new(), + } + } +} + +let schema_provider = Arc::new(MemorySchemaProvider::new()); + +let table_provider = { + let schema = Arc::new(Schema::new(vec![Field::new("i", DataType::Int32, true)])); + let arr = Arc::new(Int32Array::from((1..=1).collect::>())); + let partitions = vec![vec![RecordBatch::try_new(schema.clone(), vec![arr as ArrayRef]).unwrap()]]; + Arc::new(MemTable::try_new(schema, partitions).unwrap()) +}; + +schema_provider.register_table("users".to_string(), table_provider); + +let table = schema_provider.table("users"); +``` + +### Asynchronous `SchemaProvider` + +It's often useful to fetch metadata about which tables are in a schema, from a remote source. For example, a schema provider could fetch metadata from a remote database. To support this, the `SchemaProvider` trait has an asynchronous `table` method. + +The trait is roughly the same except for the `table` method, and the addition of the `#[async_trait]` attribute. + +```rust +# use async_trait::async_trait; +# use std::sync::Arc; +# use datafusion::catalog::{TableProvider, SchemaProvider}; +# use datafusion::common::Result; +# +# type OriginSchema = arrow::datatypes::Schema; +# +# #[derive(Debug)] +# struct Schema(OriginSchema); + +#[async_trait] +impl SchemaProvider for Schema { + async fn table(&self, name: &str) -> Result>> { +# todo!(); + } + +# fn table_names(&self) -> Vec { todo!() } +# fn table_exist(&self, _: &str) -> bool { todo!() } +} +``` + +## Implementing `MemoryCatalogProvider` + +As mentioned, the `CatalogProvider` can manage the schemas in a catalog, and the `MemoryCatalogProvider` is a simple implementation of the `CatalogProvider` trait. It stores schemas in a `DashMap`. With that the `CatalogProvider` trait can be implemented. + +```rust +use std::any::Any; +use std::sync::Arc; +use dashmap::DashMap; +use datafusion::catalog::{CatalogProvider, SchemaProvider}; +use datafusion::common::Result; + +#[derive(Debug)] +pub struct MemoryCatalogProvider { + schemas: DashMap>, +} + +impl CatalogProvider for MemoryCatalogProvider { + fn schema_names(&self) -> Vec { + self.schemas.iter().map(|s| s.key().clone()).collect() + } + + fn schema(&self, name: &str) -> Option> { + self.schemas.get(name).map(|s| s.value().clone()) + } + + fn register_schema( + &self, + name: &str, + schema: Arc, + ) -> Result>> { + Ok(self.schemas.insert(name.into(), schema)) + } + + fn deregister_schema( + &self, + name: &str, + cascade: bool, + ) -> Result>> { + /// `cascade` is not used here, but can be used to control whether + /// to delete all tables in the schema or not. + if let Some(schema) = self.schema(name) { + let (_, removed) = self.schemas.remove(name).unwrap(); + Ok(Some(removed)) + } else { + Ok(None) + } + } +} +``` + +Again, this is fairly straightforward, as there's an underlying data structure to store the state, via key-value pairs. With that the `CatalogProviderList` trait can be implemented. + +## Implementing `MemoryCatalogProviderList` + +```rust + +use std::any::Any; +use std::sync::Arc; +use dashmap::DashMap; +use datafusion::catalog::{CatalogProviderList, CatalogProvider}; +use datafusion::common::Result; + +#[derive(Debug)] +pub struct MemoryCatalogProviderList { + /// Collection of catalogs containing schemas and ultimately TableProviders + pub catalogs: DashMap>, +} + +impl CatalogProviderList for MemoryCatalogProviderList { + fn register_catalog( + &self, + name: String, + catalog: Arc, + ) -> Option> { + self.catalogs.insert(name, catalog) + } + + fn catalog_names(&self) -> Vec { + self.catalogs.iter().map(|c| c.key().clone()).collect() + } + + fn catalog(&self, name: &str) -> Option> { + self.catalogs.get(name).map(|c| c.value().clone()) + } +} +``` + +Like other traits, it also maintains the mapping of the Catalog's name to the CatalogProvider. + +## Recap + +To recap, you need to: + +1. Implement the `TableProvider` trait to create a table provider, or use an existing one. +2. Implement the `SchemaProvider` trait to create a schema provider, or use an existing one. +3. Implement the `CatalogProvider` trait to create a catalog provider, or use an existing one. +4. Implement the `CatalogProviderList` trait to create a CatalogProviderList, or use an existing one. diff --git a/versions/55.0.0/_sources/library-user-guide/custom-table-providers.md.txt b/versions/55.0.0/_sources/library-user-guide/custom-table-providers.md.txt new file mode 100644 index 0000000000000..c094f8bf7eb1b --- /dev/null +++ b/versions/55.0.0/_sources/library-user-guide/custom-table-providers.md.txt @@ -0,0 +1,973 @@ + + +# Custom Table Provider + +One of DataFusion's greatest strengths is its extensibility. If your data lives +in a custom format, behind an API, or in a system that DataFusion does not +natively support, you can teach DataFusion to read it by implementing a +**custom table provider**. This post walks through the three layers you need to +understand to design a table provider and where planning and execution work should happen. + +For details on how table constraints such as primary keys or unique +constraints are handled, see [Table Constraint Enforcement](table-constraints.md). + +The majority of this content was originally posted in the blog +[Writing Custom Table Providers in Apache DataFusion](https://datafusion.apache.org/blog/2026/03/31/writing-table-providers/). + +## The Three Layers + +When DataFusion executes a query against a table, three abstractions collaborate +to produce results: + +1. **[TableProvider]** -- Describes the table (schema, capabilities) and + produces an execution plan when queried. This is part of the **Logical Plan**. +2. **[ExecutionPlan]** -- Describes _how_ to compute the result: partitioning, + ordering, and child plan relationships. This is part of the **Physical Plan**. +3. **[SendableRecordBatchStream]** -- The async stream that _actually does the + work_, yielding `RecordBatch`es one at a time. + +Think of these as a funnel: `TableProvider::scan()` is called once during +planning to create an `ExecutionPlan`, then `ExecutionPlan::execute()` is called +once per partition to create a stream, and those streams are where rows are +actually produced during execution. + +[tableprovider]: https://docs.rs/datafusion/latest/datafusion/catalog/trait.TableProvider.html +[executionplan]: https://docs.rs/datafusion/latest/datafusion/physical_plan/trait.ExecutionPlan.html +[sendablerecordbatchstream]: https://docs.rs/datafusion/latest/datafusion/execution/type.SendableRecordBatchStream.html +[memtable]: https://docs.rs/datafusion/latest/datafusion/datasource/memory/struct.MemTable.html +[streamtable]: https://docs.rs/datafusion/latest/datafusion/datasource/stream/struct.StreamTable.html +[listingtable]: https://docs.rs/datafusion/latest/datafusion/datasource/listing/struct.ListingTable.html +[viewtable]: https://docs.rs/datafusion/latest/datafusion/datasource/view/struct.ViewTable.html +[planproperties]: https://docs.rs/datafusion/latest/datafusion/physical_plan/struct.PlanProperties.html +[streamingtableexec]: https://docs.rs/datafusion/latest/datafusion/physical_plan/streaming/struct.StreamingTableExec.html +[datasourceexec]: https://docs.rs/datafusion/latest/datafusion/datasource/source/struct.DataSourceExec.html + +## Background: Logical and Physical Planning + +Before diving into the three layers, it helps to understand how DataFusion +processes a query. There are several phases between a SQL string (or DataFrame +call) and streaming results: + +```text +SQL / DataFrame API + → Logical Plan (abstract: what to compute) + → Logical Optimization (rewrite rules that preserve semantics) + → Physical Plan (concrete: how to compute it) + → Physical Optimization (hardware- and data-aware rewrites) + → Execution (streaming RecordBatches) +``` + +### Logical Planning + +A **logical plan** describes _what_ the query computes without specifying _how_. +It is a tree of relational operators -- `TableScan`, `Filter`, `Projection`, +`Aggregate`, `Join`, `Sort`, `Limit`, and so on. The logical optimizer rewrites +this tree to reduce work while preserving the query's meaning. Some logical +optimizations include: + +- **Predicate pushdown** -- moves filters as close to the data source as + possible, so fewer rows flow through the rest of the plan. +- **Projection pruning** -- eliminates columns that are never referenced + downstream, reducing memory and I/O. +- **Expression simplification** -- rewrites expressions like `1 = 1` or + `x AND true` into simpler forms. +- **Subquery decorrelation** -- converts correlated `IN` / `EXISTS` subqueries + into more efficient semi-joins. +- **Limit pushdown** -- pushes `LIMIT` earlier in the plan so operators + produce less data. + +### Physical Planning + +The **physical planner** converts the optimized logical plan into an +`ExecutionPlan` tree -- the concrete plan that will actually run. This is where +decisions like "use a hash join vs. a sort-merge join" or "how many partitions +to scan" are made. The physical optimizer then refines this tree further with rewrites such as: + +- **Distribution enforcement** -- inserts `RepartitionExec` nodes so that data + is partitioned correctly for joins and aggregations. +- **Sort enforcement** -- inserts `SortExec` nodes where ordering is required, + and removes them where the data is already sorted. +- **Join selection** -- picks the most efficient join strategy based on + statistics and table sizes. +- **Aggregate optimization** -- combines partial and final aggregation stages, + and can use exact statistics to skip scanning entirely. + +### Why This Matters for Table Providers + +Your `TableProvider` sits at the boundary between logical and physical planning. +During logical optimization, DataFusion determines which filters and projections +_could_ be pushed down to the source. When `scan()` is called during physical +planning, those hints are passed to you. By implementing capabilities like +`supports_filters_pushdown`, you influence what the optimizer can do -- and the +metadata you declare in your `ExecutionPlan` (partitioning, ordering) directly +affects which physical optimizations apply. + +## Choosing the Right Starting Point + +Not every custom data source requires implementing all three layers from +scratch. DataFusion provides building blocks that let you plug in at whatever +level makes sense: + +| If your data is... | Start with | You implement | +| -------------------------------------------------- | ------------------------------------------------------------------------- | ------------------------------ | +| Already in `RecordBatch`es in memory | [MemTable] | Nothing -- just construct it | +| An async stream of batches | [StreamTable] | A stream factory | +| A logical transformation of other tables | [ViewTable] wrapping a logical plan | The logical plan | +| A variant of an existing file format | [ListingTable] with a custom [FileFormat] wrapping an existing one | A thin `FileFormat` wrapper | +| Files in a custom format on disk or object storage | [ListingTable] with a custom [FileFormat], [FileSource], and [FileOpener] | The format, source, and opener | +| A custom source needing full control | `TableProvider` + `ExecutionPlan` + stream | All three layers | + +[fileformat]: https://docs.rs/datafusion/latest/datafusion/datasource/file_format/trait.FileFormat.html +[filesource]: https://docs.rs/datafusion-datasource/latest/datafusion_datasource/file/trait.FileSource.html +[fileopener]: https://docs.rs/datafusion-datasource/latest/datafusion_datasource/file_stream/trait.FileOpener.html + +If your data is file-based, `ListingTable` handles file discovery, partition +column inference, and plan construction -- you only need to implement +`FileFormat`, `FileSource`, and `FileOpener` to describe how to read your +files. See the [custom_file_format example] for a minimal wrapping approach, +or [ParquetSource] and [ParquetOpener] for a full custom implementation to +use as a reference. + +[custom_file_format example]: https://github.com/apache/datafusion/blob/main/datafusion-examples/examples/custom_data_source/custom_file_format.rs +[parquetsource]: https://docs.rs/datafusion/latest/datafusion/datasource/physical_plan/struct.ParquetSource.html +[parquetopener]: https://github.com/apache/datafusion/blob/main/datafusion/datasource-parquet/src/opener.rs + +The rest of this post focuses on the full `TableProvider` + `ExecutionPlan` + +stream path, which gives you complete control and applies to any data source. + +## Layer 1: TableProvider + +A [TableProvider] represents a queryable data source. For a minimal read-only +table, you need three methods: + +```rust,ignore +impl TableProvider for MyTable { + fn schema(&self) -> SchemaRef { + Arc::clone(&self.schema) + } + + fn table_type(&self) -> TableType { + TableType::Base + } + + async fn scan( + &self, + state: &dyn Session, + projection: Option<&Vec>, + filters: &[Expr], + limit: Option, + ) -> Result> { + // Build and return an ExecutionPlan -- don't do any execution work here -- keep lightweight! + Ok(Arc::new(MyExecPlan::new( + Arc::clone(&self.schema), + projection, + limit, + ))) + } +} +``` + +The `scan` method is the heart of `TableProvider`. It receives three pushdown +hints from the optimizer, each reducing the amount of data your source needs +to produce: + +- **`projection`** -- Which columns are needed. This reduces the **width** of + the output. If your source supports it, read only these columns rather than + the full schema. +- **`filters`** -- Predicates the engine would like you to apply during the + scan. This reduces the **number of rows** by skipping data that does not + match. Implement `supports_filters_pushdown` to advertise which filters you + can handle. +- **`limit`** -- A row count cap. This also reduces the **number of rows** -- + if you can stop reading early once you have produced enough rows, this avoids + unnecessary work. + +You can also use the [scan_with_args()](https://docs.rs/datafusion/latest/datafusion/catalog/trait.TableProvider.html#method.scan_with_args) +variant that provides additional pushdown information for other advanced use cases. + +### Keep `scan()` Lightweight + +This is a critical point: **`scan()` runs during planning, not execution.** It +should return quickly. Best practice is to avoid performing I/O, network +calls, or heavy computation here. The `scan` method's job is to _describe_ how +the data will be produced, not to produce it. All the real work belongs in the +stream (Layer 3). + +A common pitfall is to fetch data or open connections in `scan()`. This blocks +the planning thread and can cause timeouts or deadlocks, especially if the query +involves multiple tables or subqueries that all need to be planned before +execution begins. + +### Existing Implementations to Learn From + +DataFusion ships several `TableProvider` implementations that are excellent +references: + +- **[MemTable]** -- Holds data in memory as `Vec`. The simplest + possible provider; great for tests and small datasets. +- **[StreamTable]** -- Wraps a user-provided stream factory. Useful when your + data arrives as a continuous stream (e.g., from Kafka or a socket). +- **[ListingTable]** -- The file-based data source behind DataFusion's + built-in Parquet, CSV, and JSON support. Demonstrates sophisticated filter + and projection pushdown, file pruning, and schema inference. +- **[ViewTable]** -- Wraps a logical plan, representing a SQL view. Useful + if your provider is best expressed as a transformation of other tables. + +## Layer 2: ExecutionPlan + +An [ExecutionPlan] is a node in the physical query plan tree. Your table +provider's `scan()` method returns one. The required methods are: + +```rust,ignore +impl ExecutionPlan for MyExecPlan { + fn name(&self) -> &str { "MyExecPlan" } + + fn properties(&self) -> &Arc { + &self.properties + } + + fn children(&self) -> Vec<&Arc> { + vec![] // Leaf node -- no children + } + + fn replace_children( + self: Arc, + children: Vec>, + _: ReplaceChildrenOptions, + ) -> Result> { + assert!(children.is_empty()); + Ok(self) + } + + fn with_new_children( + self: Arc, + children: Vec>, + ) -> Result> { + self.replace_children(children, ReplaceChildrenOptions::new(ChildrenPropertiesMode::Recompute)) + } + + fn execute( + &self, + partition: usize, + context: Arc, + ) -> Result { + // This is where you build and return your stream + // ... + } +} +``` + +The key properties to set correctly in [PlanProperties] are **output +partitioning** and **output ordering**. + +**Output partitioning** tells the engine how many partitions your data has, +which determines parallelism. If your source naturally partitions data (e.g., +by file or by shard), expose that here. + +**Output ordering** declares whether your data is naturally sorted. This +enables the optimizer to avoid inserting a `SortExec` when a query requires +ordered data. Getting this right can be a significant performance win. + +### Partitioning Strategies + +Since `execute()` is called once per partition, partitioning directly controls +the parallelism of your table scan. Each partition produces an independent +stream that DataFusion schedules as a **task** on the tokio runtime. It is +important to distinguish tasks from threads: tasks are lightweight units of +async work that are multiplexed onto a thread pool. You can have many more +tasks (partitions) than physical threads -- the runtime will interleave them +efficiently as they await I/O or yield. + +**Start simple: match your data's natural layout.** If you have 4 files, expose +4 partitions. If your source has 8 shards, expose 8 partitions. DataFusion will +insert a `RepartitionExec` above your scan when downstream operators need a +different distribution. You can also implement the +[`repartitioned`](https://docs.rs/datafusion/latest/datafusion/physical_plan/trait.ExecutionPlan.html#method.repartitioned) +method on your `ExecutionPlan` to let DataFusion request a different partition +count directly from your source, avoiding the extra operator entirely. + +Consider how your data source naturally divides its data: + +- **By file or object:** If you are reading from S3, each file can be a + partition. DataFusion will read them in parallel. +- **By shard or region:** If your source is a sharded database, each shard + maps naturally to a partition. +- **By key range:** If your data is keyed (e.g., by timestamp or customer ID), + you can split it into ranges. + +**Advanced: aligning with `target_partitions`.** Once you have something +working, you can tune further. Having _too many_ partitions is not free: each +partition adds scheduling overhead, and downstream operators may need to +repartition the data anyway. The session configuration exposes a +**target partition count** that reflects how many partitions the optimizer +expects to work with: + +```rust,ignore +async fn scan( + &self, + state: &dyn Session, + projection: Option<&Vec>, + filters: &[Expr], + limit: Option, +) -> Result> { + let target_partitions = state.config().target_partitions(); + // Optionally coalesce or split partitions to match target_partitions. + // ... +} +``` + +If your source produces data in exactly `target_partitions` partitions, the +optimizer is less likely to insert a `RepartitionExec` above your scan. +For small datasets, `target_partitions` may be set to 1, which avoids any +repartitioning overhead entirely. + +**Advanced: declaring hash partitioning.** If your source stores data +pre-partitioned by a specific key (e.g., `customer_id`), you can declare this +in your output partitioning. For a query like: + +```sql +SELECT customer_id, SUM(amount) +FROM my_table +GROUP BY customer_id; +``` + +If you declare your output partitioning as `Hash([customer_id], N)`, the +optimizer recognizes that the data is already distributed correctly for the +aggregation and eliminates the `RepartitionExec` that would otherwise appear +in the plan. You can verify this with `EXPLAIN` (more on this below). + +Conversely, if you report `UnknownPartitioning`, DataFusion must assume the +worst case and will always insert repartitioning operators as needed. + +### Keep `execute()` Lightweight Too + +Like `scan()`, the `execute()` method should construct and return a stream +without doing heavy work. The actual data production happens when the stream +is polled. Do not block on async operations here -- build the stream and let +the runtime drive it. + +### Existing Implementations to Learn From + +- **[StreamingTableExec]** -- Executes a streaming table scan. It takes a + stream factory (a closure that produces streams) and handles partitioning. + Good reference for wrapping external streams. +- **[DataSourceExec]** -- The execution plan behind DataFusion's built-in file + scanning (Parquet, CSV, JSON). It demonstrates sophisticated partitioning, + filter pushdown, and projection pushdown. + +## Layer 3: SendableRecordBatchStream + +[SendableRecordBatchStream] is where the real work happens. It is defined as: + +```rust,ignore +type SendableRecordBatchStream = + Pin> + Send>>; +``` + +This is an async stream of `RecordBatch`es that can be sent across threads. When +the DataFusion runtime polls this stream, your code runs: reading files, calling +APIs, transforming data, etc. + +### Using RecordBatchStreamAdapter + +The easiest way to create a `SendableRecordBatchStream` is with +[RecordBatchStreamAdapter]. It bridges any `futures::Stream>` into the `SendableRecordBatchStream` type: + +```rust,ignore +use datafusion::physical_plan::stream::RecordBatchStreamAdapter; + +fn execute( + &self, + partition: usize, + context: Arc, +) -> Result { + let schema = self.schema(); + let config = self.config.clone(); + + let stream = futures::stream::once(async move { + // ALL the heavy work happens here, inside the stream: + // - Open connections + // - Read data from external sources + // - Transform and batch the results + let batches = fetch_data_from_source(&config).await?; + Ok(batches) + }) + .flat_map(|result| match result { + Ok(batch) => futures::stream::iter(vec![Ok(batch)]), + Err(e) => futures::stream::iter(vec![Err(e)]), + }); + + Ok(Box::pin(RecordBatchStreamAdapter::new(schema, stream))) +} +``` + +[recordbatchstreamadapter]: https://docs.rs/datafusion/latest/datafusion/physical_plan/stream/struct.RecordBatchStreamAdapter.html + +### Blocking Work: Use a Separate Thread Pool + +If your stream performs **blocking** work -- such as blocking I/O, or CPU work +that runs for hundreds of milliseconds without yielding -- you must avoid +blocking the tokio async runtime. Short CPU work (e.g., parsing a batch in a +few milliseconds) is fine to do inline as long as your code yields back to the +runtime frequently. But for long-running synchronous work that cannot yield, +offload to a dedicated thread pool and send results back through a channel: + +```rust,ignore +fn execute( + &self, + partition: usize, + context: Arc, +) -> Result { + let schema = self.schema(); + let config = self.config.clone(); + + let (tx, rx) = tokio::sync::mpsc::channel(2); + + // Spawn blocking work on a dedicated thread pool + tokio::task::spawn_blocking(move || { + let batches = generate_data(&config); + for batch in batches { + if tx.blocking_send(Ok(batch)).is_err() { + break; // Receiver dropped, query was cancelled + } + } + }); + + let stream = tokio_stream::wrappers::ReceiverStream::new(rx); + Ok(Box::pin(RecordBatchStreamAdapter::new(schema, stream))) +} +``` + +This pattern keeps the async runtime responsive while long-running synchronous +work runs on its own threads. For a working example that shows how to configure +separate thread pools for I/O and CPU work, see the +[thread_pools example](https://github.com/apache/datafusion/blob/main/datafusion-examples/examples/query_planning/thread_pools.rs) +in the DataFusion repository. + +## Where Should the Work Happen? + +This table summarizes what belongs at each layer: + +| Layer | Runs During | Should Do | Should NOT Do | +| ----------------------------- | ------------------------------ | -------------------------------------- | ------------------------------------- | +| `TableProvider::scan()` | Planning | Build an `ExecutionPlan` with metadata | I/O, network calls, heavy computation | +| `ExecutionPlan::execute()` | Execution (once per partition) | Construct a stream, set up channels | Block on async work, read data | +| `RecordBatchStream` (polling) | Execution | All I/O, computation, data production | -- | + +The guiding principle: **push work as late as possible.** Planning should be +fast so the optimizer can do its job. Execution setup should be fast so all +partitions can start promptly. The stream is where you spend time producing +data. + +### Why This Matters + +When `scan()` does heavy work, several problems arise: + +1. **Planning becomes slow.** If a query touches 10 tables and each `scan()` + takes 500ms, planning alone takes 5 seconds before any data flows. +2. **Execution is single-threaded.** `scan()` runs on a single thread during + planning, so any work done there cannot benefit from the parallel execution + that DataFusion provides across partitions. +3. **The optimizer cannot help.** The optimizer runs between planning and + execution. If you have already fetched data during planning, optimizations + like predicate pushdown or partition pruning cannot reduce the work. +4. **Resource management breaks down.** DataFusion manages concurrency and + memory during execution. Work done during planning bypasses these controls. + +## Filter Pushdown: Doing Less Work + +One of the most impactful optimizations you can add to a custom table provider +is **filter pushdown** -- letting the source skip data that the query does not +need, rather than reading everything and filtering it afterward. + +### How Filter Pushdown Works + +When DataFusion plans a query with a `WHERE` clause, it passes the filter +predicates to your `scan()` method as the `filters` parameter. By default, +DataFusion assumes your provider cannot handle any filters and inserts a +`FilterExec` node above your scan to apply them. But if your source _can_ +evaluate some predicates during scanning -- for example, by skipping files, +partitions, or row groups that cannot match -- you can eliminate a huge amount +of unnecessary I/O. + +To opt in, implement `supports_filters_pushdown`: + +```rust +# use std::any::Any; +# use std::sync::Arc; +# use arrow::datatypes::SchemaRef; +# use datafusion::catalog::{TableProvider, Session}; +# use datafusion::common::Result; +# use datafusion::datasource::TableType; +# use datafusion::logical_expr::{Expr, BinaryExpr, Operator, TableProviderFilterPushDown}; +# use datafusion::physical_plan::ExecutionPlan; +# +# fn is_partition_column(_expr: &Expr) -> bool { false } +# +# #[derive(Debug)] +# struct MyFilterTable; +# +# #[async_trait::async_trait] +# impl TableProvider for MyFilterTable { +# fn schema(&self) -> SchemaRef { todo!() } +# fn table_type(&self) -> TableType { TableType::Base } +# async fn scan(&self, _: &dyn Session, _: Option<&Vec>, _: &[Expr], _: Option) -> Result> { todo!() } +# +fn supports_filters_pushdown( + &self, + filters: &[&Expr], +) -> Result> { + Ok(filters.iter().map(|f| { + match f { + // We can fully evaluate equality filters on + // the partition column at the source + Expr::BinaryExpr(BinaryExpr { + left, op: Operator::Eq, right + }) if is_partition_column(left) || is_partition_column(right) => { + TableProviderFilterPushDown::Exact + } + // All other filters: let DataFusion handle them + _ => TableProviderFilterPushDown::Unsupported, + } + }).collect()) +} +# } +``` + +The three possible responses for each filter are: + +- **`Exact`** -- Your source guarantees that no output rows will have a false + value for this predicate. Because the filter is fully evaluated at the source, + DataFusion will **not** add a `FilterExec` for it. +- **`Inexact`** -- Your source has the ability to reduce the data produced, but + the output may still include rows that do not satisfy the predicate. For + example, you might skip entire files based on metadata statistics but not + filter individual rows within a file. DataFusion will still add a `FilterExec` + above your scan to remove any remaining rows that slipped through. +- **`Unsupported`** -- Your source ignores this filter entirely. DataFusion + handles it. + +### Why Filter Pushdown Matters + +Consider a table with 1 billion rows partitioned by `region`, and a query: + +```sql +SELECT * FROM events WHERE region = 'us-east-1' AND event_type = 'click'; +``` + +**Without filter pushdown:** Your table provider reads all 1 billion rows +across all regions. DataFusion then applies both filters, discarding the vast +majority of the data. + +**With filter pushdown on `region`:** Your `scan()` method sees the +`region = 'us-east-1'` filter and constructs an execution plan that only reads +the `us-east-1` partition. If that partition holds 100 million rows, you have +just eliminated 90% of the I/O. DataFusion still applies the `event_type` +filter via `FilterExec` if you reported it as `Unsupported`. + +### Only Push Down Filters When the Data Source Can Do Better + +DataFusion already pushes filters as close to the data source as possible, typically placing them directly above the scan. `FilterExec` is also highly optimized, with vectorized evaluation and type-specialized kernels for fast predicate evaluation. + +Because of this, you should only implement filter pushdown when your data source +can do strictly better -- for example, by avoiding I/O entirely through +skipping files or partitions based on metadata. If your data source cannot +eliminate I/O in this way, it is usually better to let DataFusion handle the +filter, as its in-memory execution is already highly efficient. + +### Using EXPLAIN to Debug Your Table Provider + +The `EXPLAIN` statement is your best tool for understanding what DataFusion is +actually doing with your table provider. It shows the physical plan that +DataFusion will execute, including any operators it inserted: + +```sql +EXPLAIN SELECT * FROM events WHERE region = 'us-east-1' AND event_type = 'click'; +``` + +If you are using DataFrames, call `.explain(false, false)` for the logical plan +or `.explain(false, true)` for the physical plan. You can also print the plans +in verbose mode with `.explain(true, true)`. + +**Before filter pushdown**, the plan might look like: + +```text +FilterExec: region@0 = us-east-1 AND event_type@1 = click + MyExecPlan: partitions=50 +``` + +Here DataFusion is reading all 50 partitions and filtering everything +afterward. The `FilterExec` above your scan is doing all the predicate work. + +**After implementing pushdown for `region`** (reported as `Exact`): + +```text +FilterExec: event_type@1 = click + MyExecPlan: partitions=5, filter=[region = us-east-1] +``` + +Now your exec reads only the 5 partitions for `us-east-1`, and the remaining +`FilterExec` only handles the `event_type` predicate. The `region` filter has +been fully absorbed by your scan. + +**After implementing pushdown for both filters** (both `Exact`): + +```text +MyExecPlan: partitions=5, filter=[region = us-east-1 AND event_type = click] +``` + +No `FilterExec` at all -- your source handles everything. + +Similarly, `EXPLAIN` will reveal whether DataFusion is inserting unnecessary +`SortExec` or `RepartitionExec` nodes that you could eliminate by declaring +better output properties. Whenever your queries seem slower than expected, +`EXPLAIN` is the first place to look. + +### A Complete Filter Pushdown Example + +To make filter pushdown concrete, here is an illustrative example. Imagine a +table provider that reads from a set of date-partitioned directories on disk +(e.g., `data/2026-03-01/`, `data/2026-03-02/`, ...). Each directory contains +one or more Parquet files for that date. By pushing down a filter on the `date` +column, the provider can skip entire directories -- avoiding the I/O of listing +and reading files that cannot possibly match the query. + +```rust +# use std::any::Any; +# use std::collections::HashMap; +# use std::fmt; +# use std::sync::Arc; +# use arrow::datatypes::SchemaRef; +# use datafusion::catalog::{TableProvider, Session}; +# use datafusion::common::Result; +# use datafusion::common::tree_node::TreeNodeRecursion; +# use datafusion::datasource::TableType; +# use datafusion::execution::SendableRecordBatchStream; +# use datafusion::execution::context::TaskContext; +# use datafusion::logical_expr::{Expr, TableProviderFilterPushDown}; +# use datafusion::physical_expr::EquivalenceProperties; +# use datafusion::physical_plan::{DisplayAs, DisplayFormatType, ExecutionPlan, Partitioning, PhysicalExpr, PlanProperties, ChildrenPropertiesMode, ReplaceChildrenOptions}; +# use datafusion::physical_plan::execution_plan::{Boundedness, EmissionType}; +# +/// A table provider backed by date-partitioned directories. +/// Each date directory contains data files; by filtering on the +/// `date` column we can skip entire directories of I/O. +# #[derive(Debug)] +struct DatePartitionedTable { + schema: SchemaRef, + /// Maps date strings ("2026-03-01") to directory paths + partitions: HashMap, +} + +#[async_trait::async_trait] +impl TableProvider for DatePartitionedTable { + fn schema(&self) -> SchemaRef { Arc::clone(&self.schema) } + fn table_type(&self) -> TableType { TableType::Base } + + fn supports_filters_pushdown( + &self, + filters: &[&Expr], + ) -> Result> { + Ok(filters.iter().map(|f| { + if Self::is_date_equality_filter(f) { + // We can fully evaluate this: we will only read + // directories matching the date, so no rows with + // a different date will appear in the output. + TableProviderFilterPushDown::Exact + } else { + TableProviderFilterPushDown::Unsupported + } + }).collect()) + } + + async fn scan( + &self, + _state: &dyn Session, + projection: Option<&Vec>, + filters: &[Expr], + limit: Option, + ) -> Result> { + // Determine which date partitions to read by inspecting + // the pushed-down filters. This is the key optimization: + // we decide *during planning* which directories to scan, + // so that execution never touches irrelevant data. + let dates_to_read: Vec = self + .extract_date_values(filters) + .unwrap_or_else(|| + self.partitions.keys().cloned().collect() + ); + + let dirs: Vec = dates_to_read + .iter() + .filter_map(|d| self.partitions.get(d).cloned()) + .collect(); + let num_dirs = dirs.len(); + + Ok(Arc::new(DatePartitionedExec { + schema: Arc::clone(&self.schema), + directories: dirs, + properties: Arc::new(PlanProperties::new( + EquivalenceProperties::new( + Arc::clone(&self.schema), + ), + // One partition per date directory -- these + // will be read in parallel. + Partitioning::UnknownPartitioning(num_dirs), + EmissionType::Incremental, + Boundedness::Bounded, + )), + })) + } +} + +impl DatePartitionedTable { + /// Check if a filter is an equality comparison on the `date` column. + fn is_date_equality_filter(expr: &Expr) -> bool { + // In practice, match on BinaryExpr { left, op: Eq, right } + // and check if either side references the "date" column. + // Simplified here for clarity. + todo!("match on date equality expressions") + } + + /// Extract date literal values from pushed-down equality filters. + fn extract_date_values(&self, filters: &[Expr]) -> Option> { + // Parse filters like `date = '2026-03-01'` and return + // the literal date strings. Returns None if no date + // filters are present (meaning: read all partitions). + todo!("extract date literals from filter expressions") + } +} +# +# #[derive(Debug)] +# struct DatePartitionedExec { +# schema: SchemaRef, +# directories: Vec, +# properties: Arc, +# } +# +# impl DisplayAs for DatePartitionedExec { +# fn fmt_as(&self, _t: DisplayFormatType, f: &mut fmt::Formatter) -> fmt::Result { +# write!(f, "DatePartitionedExec") +# } +# } +# +# impl ExecutionPlan for DatePartitionedExec { +# fn name(&self) -> &str { "DatePartitionedExec" } +# fn properties(&self) -> &Arc { &self.properties } +# fn children(&self) -> Vec<&Arc> { vec![] } +# fn replace_children(self: Arc, _: Vec>, _: ReplaceChildrenOptions) -> Result> { Ok(self) } +# +# fn with_new_children( +# self: Arc, +# children: Vec>, +# ) -> Result> { +# self.replace_children(children, ReplaceChildrenOptions::new(ChildrenPropertiesMode::Recompute)) +# } +# +# fn execute(&self, _: usize, _: Arc) -> Result { todo!() } +# fn apply_expressions(&self, _f: &mut dyn FnMut(&Arc) -> Result) -> Result { Ok(TreeNodeRecursion::Continue) } +# } +``` + +The key insight is that the filter pushdown decision (`supports_filters_pushdown`) +and the partition pruning (`scan()`) work together: the first tells DataFusion +that a `FilterExec` is unnecessary for the `date` predicate, and the second +ensures that only the relevant directories are scanned. The actual file reading +happens later, in the stream produced by `execute()`. + +## Putting It All Together + +Here is a minimal but complete example of a custom table provider that generates +data lazily during streaming: + +```rust +use std::any::Any; +# use std::fmt; +use std::sync::Arc; + +use arrow::array::Int64Array; +use arrow::datatypes::{DataType, Field, Schema, SchemaRef}; +use arrow::record_batch::RecordBatch; +use datafusion::catalog::TableProvider; +use datafusion::common::Result; +# use datafusion::common::tree_node::TreeNodeRecursion; +use datafusion::datasource::TableType; +use datafusion::catalog::Session; +use datafusion::execution::SendableRecordBatchStream; +# use datafusion::execution::context::TaskContext; +use datafusion::logical_expr::Expr; +use datafusion::physical_expr::EquivalenceProperties; +use datafusion::physical_plan::execution_plan::{Boundedness, EmissionType}; +use datafusion::physical_plan::stream::RecordBatchStreamAdapter; +use datafusion::physical_plan::{ +# DisplayAs, DisplayFormatType, PhysicalExpr, + ChildrenPropertiesMode, ReplaceChildrenOptions, ExecutionPlan, Partitioning, PlanProperties, +}; +use futures::stream; + +/// A table provider that generates sequential numbers on demand. +# #[derive(Debug)] +struct CountingTable { + schema: SchemaRef, + num_partitions: usize, + rows_per_partition: usize, +} + +impl CountingTable { + fn new(num_partitions: usize, rows_per_partition: usize) -> Self { + let schema = Arc::new(Schema::new(vec![ + Field::new("partition", DataType::Int64, false), + Field::new("value", DataType::Int64, false), + ])); + Self { schema, num_partitions, rows_per_partition } + } +} + +#[async_trait::async_trait] +impl TableProvider for CountingTable { + fn schema(&self) -> SchemaRef { Arc::clone(&self.schema) } + fn table_type(&self) -> TableType { TableType::Base } + + async fn scan( + &self, + _state: &dyn Session, + projection: Option<&Vec>, + _filters: &[Expr], + limit: Option, + ) -> Result> { + // Light work only: build the plan with metadata + Ok(Arc::new(CountingExec { + schema: Arc::clone(&self.schema), + num_partitions: self.num_partitions, + rows_per_partition: limit + .unwrap_or(self.rows_per_partition) + .min(self.rows_per_partition), + properties: Arc::new(PlanProperties::new( + EquivalenceProperties::new(Arc::clone(&self.schema)), + Partitioning::UnknownPartitioning(self.num_partitions), + EmissionType::Incremental, + Boundedness::Bounded, + )), + })) + } +} + +# #[derive(Debug)] +struct CountingExec { + schema: SchemaRef, + num_partitions: usize, + rows_per_partition: usize, + properties: Arc, +} + +# impl DisplayAs for CountingExec { +# fn fmt_as(&self, _t: DisplayFormatType, f: &mut fmt::Formatter) -> fmt::Result { +# write!(f, "CountingExec: partitions={}", self.num_partitions) +# } +# } +# +impl ExecutionPlan for CountingExec { + fn name(&self) -> &str { "CountingExec" } + fn properties(&self) -> &Arc { &self.properties } + fn children(&self) -> Vec<&Arc> { vec![] } + + fn replace_children( + self: Arc, + _: Vec>, + _: ReplaceChildrenOptions, + ) -> Result> { + Ok(self) + } + + fn with_new_children( + self: Arc, + children: Vec>, + ) -> Result> { + self.replace_children(children, ReplaceChildrenOptions::new(ChildrenPropertiesMode::Recompute)) + } + + fn execute( + &self, + partition: usize, + _context: Arc, + ) -> Result { + let schema = Arc::clone(&self.schema); + let rows = self.rows_per_partition; + + // The heavy work (data generation) happens inside the stream, + // not here in execute(). + let batch_stream = stream::once(async move { + let partitions = Int64Array::from( + vec![partition as i64; rows], + ); + let values = Int64Array::from( + (0..rows as i64).collect::>(), + ); + let batch = RecordBatch::try_new( + Arc::clone(&schema), + vec![Arc::new(partitions), Arc::new(values)], + )?; + Ok(batch) + }); + + Ok(Box::pin(RecordBatchStreamAdapter::new( + Arc::clone(&self.schema), + batch_stream, + ))) + } + +# fn apply_expressions( +# &self, +# _f: &mut dyn FnMut(&Arc) -> Result, +# ) -> Result { +# Ok(TreeNodeRecursion::Continue) +# } +} +``` + +## Using Your Table Provider + +Once you have implemented a `TableProvider`, register it with a `SessionContext` +to make it queryable: + +```rust,ignore +use datafusion::execution::context::SessionContext; + +#[tokio::main] +async fn main() -> Result<()> { + let ctx = SessionContext::new(); + + let provider = CountingTable::new(4, 1000); + ctx.register_table("counting", Arc::new(provider))?; + + let df = ctx.sql("SELECT * FROM counting LIMIT 10").await?; + df.show().await?; + + Ok(()) +} +``` + +## Further Reading + +- [`TableProvider` API docs](https://docs.rs/datafusion/latest/datafusion/catalog/trait.TableProvider.html) +- [`ExecutionPlan` API docs](https://docs.rs/datafusion/latest/datafusion/physical_plan/trait.ExecutionPlan.html) +- [`SendableRecordBatchStream` API docs](https://docs.rs/datafusion/latest/datafusion/execution/type.SendableRecordBatchStream.html) +- [DataFusion examples directory](https://github.com/apache/datafusion/tree/main/datafusion-examples/examples) -- + contains working examples including custom table providers diff --git a/versions/55.0.0/_sources/library-user-guide/extending-operators.md.txt b/versions/55.0.0/_sources/library-user-guide/extending-operators.md.txt new file mode 100644 index 0000000000000..0a169531757c2 --- /dev/null +++ b/versions/55.0.0/_sources/library-user-guide/extending-operators.md.txt @@ -0,0 +1,62 @@ + + +# Extending Operators + +DataFusion supports extending operators by transforming [`LogicalPlan`] and [`ExecutionPlan`] through customized [optimizer rules](https://docs.rs/datafusion/latest/datafusion/optimizer/trait.OptimizerRule.html). This section will use the µWheel project to illustrate such capabilities. + +[`logicalplan`]: https://docs.rs/datafusion/latest/datafusion/logical_expr/enum.LogicalPlan.html +[`executionplan`]: https://docs.rs/datafusion/latest/datafusion/physical_plan/trait.ExecutionPlan.html + +## About DataFusion µWheel + +[DataFusion µWheel](https://github.com/uwheel/datafusion-uwheel/tree/main) is a native DataFusion optimizer which improves query performance for time-based analytics through fast temporal aggregation and pruning using custom indices. The integration of µWheel into DataFusion is a joint effort with the DataFusion community. + +### Optimizing Logical Plan + +The `rewrite` function transforms logical plans by identifying temporal patterns and aggregation functions that match the stored wheel indices. When match is found, it queries the corresponding index to retrieve pre-computed aggregate values, stores these results in a [MemTable](https://docs.rs/datafusion/latest/datafusion/datasource/memory/struct.MemTable.html), and returns as a new `LogicalPlan::TableScan`. If no match is found, the original plan proceeds unchanged through DataFusion's standard execution path. + +```rust,ignore +fn rewrite( + &self, + plan: LogicalPlan, + _config: &dyn OptimizerConfig, +) -> Result> { + // Attempts to rewrite a logical plan to a uwheel-based plan that either provides + // plan-time aggregates or skips execution based on min/max pruning. + if let Some(rewritten) = self.try_rewrite(&plan) { + Ok(Transformed::yes(rewritten)) + } else { + Ok(Transformed::no(plan)) + } +} +``` + +```rust,ignore +// Converts a uwheel aggregate result to a TableScan with a MemTable as source +fn agg_to_table_scan(result: f64, schema: SchemaRef) -> Result { + let data = Float64Array::from(vec![result]); + let record_batch = RecordBatch::try_new(schema.clone(), vec![Arc::new(data)])?; + let df_schema = Arc::new(DFSchema::try_from(schema.clone())?); + let mem_table = MemTable::try_new(schema, vec![vec![record_batch]])?; + mem_table_as_table_scan(mem_table, df_schema) +} +``` + +To get a deeper dive into the usage of the µWheel project, visit the [blog post](https://uwheel.rs/post/datafusion_uwheel/) by Max Meldrum. diff --git a/versions/55.0.0/_sources/library-user-guide/extending-sql.md.txt b/versions/55.0.0/_sources/library-user-guide/extending-sql.md.txt new file mode 100644 index 0000000000000..eea5b3b1acfc9 --- /dev/null +++ b/versions/55.0.0/_sources/library-user-guide/extending-sql.md.txt @@ -0,0 +1,389 @@ + + +# Extending SQL Syntax + +DataFusion provides a flexible extension system that allows you to customize SQL +parsing and planning without modifying the core codebase. This is useful when you +need to: + +- Support custom operators from other SQL dialects (e.g., PostgreSQL's `->` for JSON) +- Add custom data types not natively supported +- Implement SQL constructs like `TABLESAMPLE`, `PIVOT`/`UNPIVOT`, or `MATCH_RECOGNIZE` + +You can read more about this topic in the [Extending SQL in DataFusion: from ->> +to TABLESAMPLE] blog. + +[extending sql in datafusion: from ->> to tablesample]: https://datafusion.apache.org/blog/2026/01/12/extending-sql + +## Architecture Overview + +When DataFusion processes a SQL query, it goes through these stages: + +```text +┌─────────────┐ ┌─────────┐ ┌──────────────────────┐ ┌─────────────┐ +│ SQL String │───▶│ Parser │───▶│ SqlToRel │───▶│ LogicalPlan │ +└─────────────┘ └─────────┘ │ (SQL to LogicalPlan) │ └─────────────┘ + └──────────────────────┘ + │ + │ uses + ▼ + ┌───────────────────────┐ + │ Extension Planners │ + │ • ExprPlanner │ + │ • TypePlanner │ + │ • RelationPlanner │ + └───────────────────────┘ +``` + +The extension planners intercept specific parts of the SQL AST during the +`SqlToRel` phase and allow you to customize how they are converted to DataFusion's +logical plan. + +## Extension Points + +DataFusion provides three planner traits for extending SQL: + +| Trait | Purpose | Registration Method | +| ------------------- | --------------------------------------- | ------------------------------------------ | +| [`ExprPlanner`] | Custom expressions and operators | `ctx.register_expr_planner()` | +| [`TypePlanner`] | Custom SQL data types | `SessionStateBuilder::with_type_planner()` | +| [`RelationPlanner`] | Custom FROM clause elements (relations) | `ctx.register_relation_planner()` | + +**Planner Precedence**: Multiple [`ExprPlanner`]s and [`RelationPlanner`]s can be +registered; they are invoked in reverse registration order (last registered wins). +Return `Original(...)` to delegate to the next planner. Only one `TypePlanner` +can be active at a time. + +### ExprPlanner: Custom Expressions and Operators + +Use [`ExprPlanner`] to customize how SQL expressions are converted to DataFusion +logical expressions. This is useful for: + +- Custom binary operators (e.g., `->`, `->>`, `@>`, `?`) +- Custom field access patterns +- Custom aggregate or window function handling + +#### Available Methods + +| Category | Methods | +| ------------------ | ---------------------------------------------------------------------------------- | +| Operators | `plan_binary_op`, `plan_any` | +| Literals | `plan_array_literal`, `plan_dictionary_literal`, `plan_struct_literal` | +| Functions | `plan_extract`, `plan_substring`, `plan_overlay`, `plan_position`, `plan_make_map` | +| Identifiers | `plan_field_access`, `plan_compound_identifier` | +| Aggregates/Windows | `plan_aggregate`, `plan_window` | + +See the [ExprPlanner API documentation] for full method signatures. + +#### Example: Custom Arrow Operator + +This example maps the `->` operator to string concatenation: + +```rust +# use std::sync::Arc; +# use datafusion::common::DFSchema; +# use datafusion::error::Result; +# use datafusion::logical_expr::Operator; +# use datafusion::prelude::*; +# use datafusion::sql::sqlparser::ast::BinaryOperator; +use datafusion_expr::planner::{ExprPlanner, PlannerResult, RawBinaryExpr}; +# use datafusion_expr::BinaryExpr; + +#[derive(Debug)] +struct MyCustomPlanner; + +impl ExprPlanner for MyCustomPlanner { + fn plan_binary_op( + &self, + expr: RawBinaryExpr, + _schema: &DFSchema, + ) -> Result> { + match &expr.op { + // Map `->` to string concatenation + BinaryOperator::Arrow => { + Ok(PlannerResult::Planned(Expr::BinaryExpr(BinaryExpr { + left: Box::new(expr.left.clone()), + right: Box::new(expr.right.clone()), + op: Operator::StringConcat, + }))) + } + _ => Ok(PlannerResult::Original(expr)), + } + } +} + +#[tokio::main] +async fn main() -> Result<()> { + // Use postgres dialect to enable `->` operator parsing + let config = SessionConfig::new() + .set_str("datafusion.sql_parser.dialect", "postgres"); + let mut ctx = SessionContext::new_with_config(config); + + // Register the custom planner + ctx.register_expr_planner(Arc::new(MyCustomPlanner))?; + + // Now `->` works as string concatenation + let results = ctx.sql("SELECT 'hello'->'world'").await?.collect().await?; + // Returns: "helloworld" + Ok(()) +} +``` + +For more details, see the [ExprPlanner API documentation] and the +[expr_planner test examples]. + +### TypePlanner: Custom Data Types + +Use [`TypePlanner`] to map SQL data types to Arrow/DataFusion types. This is useful +when you need to support SQL types that aren't natively recognized. + +#### Example: Custom DATETIME Type + +```rust +# use std::sync::Arc; +# use arrow::datatypes::{DataType, FieldRef, TimeUnit}; +# use datafusion::error::Result; +# use datafusion::prelude::*; +# use datafusion::execution::SessionStateBuilder; +use datafusion_expr::planner::TypePlanner; +# use sqlparser::ast; + +#[derive(Debug)] +struct MyTypePlanner; + +impl TypePlanner for MyTypePlanner { + fn plan_type_field(&self, sql_type: &ast::DataType) -> Result> { + match sql_type { + // Map DATETIME(precision) to Arrow Timestamp + ast::DataType::Datetime(precision) => { + let time_unit = match precision { + Some(0) => TimeUnit::Second, + Some(3) => TimeUnit::Millisecond, + Some(6) => TimeUnit::Microsecond, + None | Some(9) => TimeUnit::Nanosecond, + _ => return Ok(None), // Let default handling take over + }; + Ok(Some( + DataType::Timestamp(time_unit, None).into_nullable_field_ref() + )) + } + _ => Ok(None), // Return None for types we don't handle + } + } +} + +#[tokio::main] +async fn main() -> Result<()> { + let state = SessionStateBuilder::new() + .with_default_features() + .with_type_planner(Arc::new(MyTypePlanner)) + .build(); + + let ctx = SessionContext::new_with_state(state); + + // Now DATETIME type is recognized + ctx.sql("CREATE TABLE events (ts DATETIME(3))").await?; + Ok(()) +} +``` + +#### Example: Supporting the UUID Type + +```rust +# use std::sync::Arc; +# use arrow::datatypes::{DataType, FieldRef, TimeUnit}; +# use datafusion::error::Result; +# use datafusion::prelude::*; +# use datafusion::execution::SessionStateBuilder; +use datafusion_expr::planner::TypePlanner; +# use sqlparser::ast; + +#[derive(Debug)] +struct MyTypePlanner; + +impl TypePlanner for MyTypePlanner { + fn plan_type_field(&self, sql_type: &ast::DataType) -> Result> { + match sql_type { + sqlparser::ast::DataType::Uuid => Ok(Some(Arc::new( + Field::new("", DataType::FixedSizeBinary(16), true).with_metadata( + [("ARROW:extension:name".to_string(), "arrow.uuid".to_string())] + .into(), + ), + ))), + _ => Ok(None), + } + } +} + +#[tokio::main] +async fn main() -> Result<()> { + let state = SessionStateBuilder::new() + .with_default_features() + .with_type_planner(Arc::new(MyTypePlanner)) + .build(); + + let ctx = SessionContext::new_with_state(state); + + // Now UUID type is recognized + ctx.sql("CREATE TABLE idx (uuid UUID)").await?; + Ok(()) +} +``` + +For more details, see the [TypePlanner API documentation]. + +### RelationPlanner: Custom FROM Clause Elements + +Use [`RelationPlanner`] to handle custom relations in the FROM clause. This +enables you to implement SQL constructs like: + +- `TABLESAMPLE` for sampling data +- `PIVOT` / `UNPIVOT` for data reshaping +- `MATCH_RECOGNIZE` for pattern matching +- Any custom relation syntax parsed by sqlparser + +#### The RelationPlannerContext + +When implementing [`RelationPlanner`], you receive a [`RelationPlannerContext`] that +provides utilities for planning: + +| Method | Purpose | +| --------------------------- | ----------------------------------------------- | +| `plan(relation)` | Recursively plan a nested relation | +| `sql_to_expr(expr, schema)` | Convert SQL expression to DataFusion Expr | +| `context_provider()` | Access session configuration, tables, functions | + +See the [RelationPlanner API documentation] for additional methods like +`normalize_ident()` and `object_name_to_table_reference()`. + +#### Implementation Strategies + +There are two main approaches when implementing a [`RelationPlanner`]: + +1. **Rewrite to Standard SQL**: Transform custom syntax into equivalent standard + operations that DataFusion already knows how to execute (e.g., PIVOT → GROUP BY + with CASE expressions). This is the simplest approach when possible. + +2. **Custom Logical and Physical Nodes**: Create a [`UserDefinedLogicalNode`] to + represent the operation in the logical plan, along with a custom [`ExecutionPlan`] + to execute it. Both are required for end-to-end execution. + +#### Example: Basic RelationPlanner Structure + +```rust +# use std::sync::Arc; +# use datafusion::error::Result; +# use datafusion::prelude::*; +use datafusion_expr::planner::{ + PlannedRelation, RelationPlanner, RelationPlannerContext, RelationPlanning, +}; +use datafusion_sql::sqlparser::ast::TableFactor; + +#[derive(Debug)] +struct MyRelationPlanner; + +impl RelationPlanner for MyRelationPlanner { + fn plan_relation( + &self, + relation: TableFactor, + ctx: &mut dyn RelationPlannerContext, + ) -> Result { + match relation { + // Handle your custom relation + TableFactor::Pivot { table, alias, .. } => { + // Plan the input table + let input = ctx.plan(*table)?; + + // Transform or wrap the plan as needed + // ... + + Ok(RelationPlanning::Planned(PlannedRelation::new(input, alias))) + } + + // Return Original for relations you don't handle + other => Ok(RelationPlanning::Original(other)), + } + } +} + +#[tokio::main] +async fn main() -> Result<()> { + let ctx = SessionContext::new(); + + // Register the custom planner + ctx.register_relation_planner(Arc::new(MyRelationPlanner))?; + + Ok(()) +} +``` + +## Complete Examples + +The DataFusion repository includes comprehensive examples demonstrating each +approach: + +### TABLESAMPLE (Custom Logical and Physical Nodes) + +The [table_sample.rs] example shows a complete end-to-end implementation of how to +support queries such as: + +```sql +SELECT * FROM table TABLESAMPLE BERNOULLI(10 PERCENT) REPEATABLE(42) +``` + +### PIVOT/UNPIVOT (Rewrite Strategy) + +The [pivot_unpivot.rs] example demonstrates rewriting custom syntax to standard SQL +for queries such as: + +```sql +SELECT * FROM sales + PIVOT (SUM(amount) FOR quarter IN ('Q1', 'Q2', 'Q3', 'Q4')) +``` + +## Recap + +1. Use [`ExprPlanner`] for custom operators and expression handling +2. Use [`TypePlanner` for custom SQL data types +3. Use [`RelationPlanner`] for custom FROM clause syntax (TABLESAMPLE, PIVOT, etc.) +4. Register planners via [`SessionContext`] or [`SessionStateBuilder`] + +## See Also + +- API Documentation: [`ExprPlanner`], [`TypePlanner`], [`RelationPlanner`] +- [relation_planner examples] - Complete TABLESAMPLE, PIVOT/UNPIVOT implementations +- [expr_planner test examples] - Custom operator examples +- [Custom Expression Planning](functions/adding-udfs.md#custom-expression-planning) in the UDF guide + +[`exprplanner`]: https://docs.rs/datafusion/latest/datafusion/logical_expr/planner/trait.ExprPlanner.html +[`typeplanner`]: https://docs.rs/datafusion/latest/datafusion/logical_expr/planner/trait.TypePlanner.html +[`relationplanner`]: https://docs.rs/datafusion/latest/datafusion/logical_expr/planner/trait.RelationPlanner.html +[`userdefinedlogicalnode`]: https://docs.rs/datafusion/latest/datafusion/logical_expr/trait.UserDefinedLogicalNode.html +[`executionplan`]: https://docs.rs/datafusion/latest/datafusion/physical_plan/trait.ExecutionPlan.html +[`sessioncontext`]: https://docs.rs/datafusion/latest/datafusion/execution/context/struct.SessionContext.html +[`sessionstatebuilder`]: https://docs.rs/datafusion/latest/datafusion/execution/session_state/struct.SessionStateBuilder.html +[`relationplannercontext`]: https://docs.rs/datafusion/latest/datafusion/logical_expr/planner/trait.RelationPlannerContext.html +[exprplanner api documentation]: https://docs.rs/datafusion/latest/datafusion/logical_expr/planner/trait.ExprPlanner.html +[typeplanner api documentation]: https://docs.rs/datafusion/latest/datafusion/logical_expr/planner/trait.TypePlanner.html +[relationplanner api documentation]: https://docs.rs/datafusion/latest/datafusion/logical_expr/planner/trait.RelationPlanner.html +[expr_planner test examples]: https://github.com/apache/datafusion/blob/main/datafusion/core/tests/user_defined/expr_planner.rs +[relation_planner examples]: https://github.com/apache/datafusion/tree/main/datafusion-examples/examples/relation_planner +[table_sample.rs]: https://github.com/apache/datafusion/blob/main/datafusion-examples/examples/relation_planner/table_sample.rs +[pivot_unpivot.rs]: https://github.com/apache/datafusion/blob/main/datafusion-examples/examples/relation_planner/pivot_unpivot.rs diff --git a/versions/55.0.0/_sources/library-user-guide/extensions.md.txt b/versions/55.0.0/_sources/library-user-guide/extensions.md.txt new file mode 100644 index 0000000000000..0c7c891f4b1e0 --- /dev/null +++ b/versions/55.0.0/_sources/library-user-guide/extensions.md.txt @@ -0,0 +1,64 @@ + + +# Extensions List + +DataFusion tries to provide a good set of features "out of the box" to quickly +start with a working system, but it can't include every useful feature (e.g. +`TableProvider`s for all data formats). + +Thankfully one of the core features of DataFusion is a flexible extension API +that allows users to extend its behavior at all points. This page lists some +community maintained extensions available for DataFusion. These extensions are +not part of the core DataFusion project, and not under Apache Software +Foundation governance but we list them here to be useful to others in the +community. + +If you know of an available extension that is not listed below, please open a PR +to add it to this page. If there is some feature you would like to see in +DataFusion, please consider creating a new extension in the `datafusion-contrib` +project (see [below](#datafusion-contrib)). Please [contact] us via github issue, slack, or Discord and +we'll gladly set up a new repository for your extension. + +| Name | Type | Description | +| ---------------------------- | ----------------- | --------------------------------------------------------------------------------- | +| [DataFusion Table Providers] | [`TableProvider`] | Support for `PostgreSQL`, `MySQL`, `SQLite`, `DuckDB`, and `Flight SQL` | +| [DataFusion Federation] | Framework | Allows DataFusion to execute (part of) a query plan by a remote execution engine. | +| [DataFusion ORC] | [`TableProvider`] | [Apache ORC] file format | +| [DataFusion JSON Functions] | Functions | Scalar functions for querying JSON strings | + +[`tableprovider`]: https://docs.rs/datafusion/latest/datafusion/catalog/trait.TableProvider.html +[datafusion table providers]: https://github.com/datafusion-contrib/datafusion-table-providers +[datafusion federation]: https://github.com/datafusion-contrib/datafusion-federation +[datafusion orc]: https://github.com/datafusion-contrib/datafusion-orc +[apache orc]: https://orc.apache.org/ +[datafusion json functions]: https://github.com/datafusion-contrib/datafusion-functions-json + +## `datafusion-contrib` + +The [`datafusion-contrib`] project contains a collection of community maintained +extensions that are not part of the core DataFusion project, and not under +Apache Software Foundation governance but may be useful to others in the +community. If you are interested adding a feature to DataFusion, a new extension +in `datafusion-contrib` is likely a good place to start. Please [contact] us via +github issue, slack, or Discord and we'll gladly set up a new repository for +your extension. + +[`datafusion-contrib`]: https://github.com/datafusion-contrib +[contact]: ../contributor-guide/communication.md diff --git a/versions/55.0.0/_sources/library-user-guide/functions/adding-udfs.md.txt b/versions/55.0.0/_sources/library-user-guide/functions/adding-udfs.md.txt new file mode 100644 index 0000000000000..c3a40557a006d --- /dev/null +++ b/versions/55.0.0/_sources/library-user-guide/functions/adding-udfs.md.txt @@ -0,0 +1,1603 @@ + + +# Adding User Defined Functions: Scalar/Window/Aggregate/Table Functions + +User Defined Functions (UDFs) are functions that can be used in the context of DataFusion execution. + +This page covers how to add UDFs to DataFusion. In particular, it covers how to add Scalar, Window, and Aggregate UDFs. + +| UDF Type | Description | Example(s) | +| -------------- | ---------------------------------------------------------------------------------------------------------- | ------------------------------------- | +| Scalar | A function that takes a row of data and returns a single value. | [simple_udf.rs] / [advanced_udf.rs] | +| Window | A function that takes a row of data and returns a single value, but also has access to the rows around it. | [simple_udwf.rs] / [advanced_udwf.rs] | +| Aggregate | A function that takes a group of rows and returns a single value. | [simple_udaf.rs] / [advanced_udaf.rs] | +| Table | A function that takes parameters and returns a `TableProvider` to be used in an query plan. | [simple_udtf.rs] | +| Scalar (async) | A scalar function for performing `async` operations (such as network or I/O calls) within the UDF. | [async_udf.rs] | + +[simple_udf.rs]: https://github.com/apache/datafusion/blob/main/datafusion-examples/examples/udf/simple_udf.rs +[advanced_udf.rs]: https://github.com/apache/datafusion/blob/main/datafusion-examples/examples/udf/advanced_udf.rs +[simple_udwf.rs]: https://github.com/apache/datafusion/blob/main/datafusion-examples/examples/udf/simple_udwf.rs +[advanced_udwf.rs]: https://github.com/apache/datafusion/blob/main/datafusion-examples/examples/udf/advanced_udwf.rs +[simple_udaf.rs]: https://github.com/apache/datafusion/blob/main/datafusion-examples/examples/udf/simple_udaf.rs +[advanced_udaf.rs]: https://github.com/apache/datafusion/blob/main/datafusion-examples/examples/udf/advanced_udaf.rs +[simple_udtf.rs]: https://github.com/apache/datafusion/blob/main/datafusion-examples/examples/udf/simple_udtf.rs +[async_udf.rs]: https://github.com/apache/datafusion/blob/main/datafusion-examples/examples/udf/async_udf.rs + +First we'll talk about adding an Scalar UDF end-to-end, then we'll talk about the differences between the different +types of UDFs. + +## Adding a Scalar UDF + +A Scalar UDF is a function that takes a row of data and returns a single value. To achieve good performance, +such functions are "vectorized" in DataFusion, meaning they get one or more Arrow Arrays as input and produce +an Arrow Array with the same number of rows as output. + +To create a Scalar UDF, you + +1. Implement the `ScalarUDFImpl` trait to tell DataFusion about your function such as what types of arguments it takes + and how to calculate the results. +2. Create a `ScalarUDF` and register it with `SessionContext::register_udf` so it can be invoked by name. + +In the following example, we will add a function takes a single i64 and returns a single i64 with 1 added to it: + +For brevity, we'll skip some error handling. +For production code, you may want to check, for example, that `args.len()` matches the expected number of arguments. + +### Adding by `impl ScalarUDFImpl` + +This a lower level API with more functionality but is more complex, also documented in [`advanced_udf.rs`]. + +```rust +use std::sync::Arc; +use std::any::Any; +use std::sync::LazyLock; +use arrow::datatypes::DataType; +use datafusion_common::cast::as_int64_array; +use datafusion_common::{DataFusionError, plan_err, Result}; +use datafusion_expr::{col, ColumnarValue, ScalarFunctionArgs, Signature, Volatility}; +use datafusion::arrow::array::{ArrayRef, Int64Array}; +use datafusion_expr::{ScalarUDFImpl, ScalarUDF}; +use datafusion_macros::user_doc; +use datafusion_doc::Documentation; + +/// This struct for a simple UDF that adds one to an int32 +#[user_doc( + doc_section(label = "Math Functions"), + description = "Add one udf", + syntax_example = "add_one(1)" +)] +#[derive(Debug, PartialEq, Eq, Hash)] +struct AddOne { + signature: Signature, +} + +impl AddOne { + fn new() -> Self { + Self { + signature: Signature::uniform(1, vec![DataType::Int32], Volatility::Immutable), + } + } +} + +/// Implement the ScalarUDFImpl trait for AddOne +impl ScalarUDFImpl for AddOne { + fn name(&self) -> &str { "add_one" } + fn signature(&self) -> &Signature { &self.signature } + fn return_type(&self, args: &[DataType]) -> Result { + if !matches!(args.get(0), Some(&DataType::Int32)) { + return plan_err!("add_one only accepts Int32 arguments"); + } + Ok(DataType::Int32) + } + // The actual implementation would add one to the argument + fn invoke_with_args(&self, args: ScalarFunctionArgs) -> Result { + let args = ColumnarValue::values_to_arrays(&args.args)?; + let i64s = as_int64_array(&args[0])?; + + let new_array = i64s + .iter() + .map(|array_elem| array_elem.map(|value| value + 1)) + .collect::(); + + Ok(ColumnarValue::from(Arc::new(new_array) as ArrayRef)) + } + fn documentation(&self) -> Option<&Documentation> { + self.doc() + } +} +``` + +We now need to register the function with DataFusion so that it can be used in the context of a query. + +```rust +# use std::sync::Arc; +# use std::any::Any; +# use std::sync::LazyLock; +# use arrow::datatypes::DataType; +# use datafusion_common::cast::as_int64_array; +# use datafusion_common::{DataFusionError, plan_err, Result}; +# use datafusion_expr::{col, ColumnarValue, ScalarFunctionArgs, Signature, Volatility}; +# use datafusion::arrow::array::{ArrayRef, Int64Array}; +# use datafusion_expr::{ScalarUDFImpl, ScalarUDF}; +# use datafusion_macros::user_doc; +# use datafusion_doc::Documentation; +# +# /// This struct for a simple UDF that adds one to an int32 +# #[user_doc( +# doc_section(label = "Math Functions"), +# description = "Add one udf", +# syntax_example = "add_one(1)" +# )] +# #[derive(Debug, PartialEq, Eq, Hash)] +# struct AddOne { +# signature: Signature, +# } +# +# impl AddOne { +# fn new() -> Self { +# Self { +# signature: Signature::uniform(1, vec![DataType::Int32], Volatility::Immutable), +# } +# } +# } +# +# /// Implement the ScalarUDFImpl trait for AddOne +# impl ScalarUDFImpl for AddOne { +# fn name(&self) -> &str { "add_one" } +# fn signature(&self) -> &Signature { &self.signature } +# fn return_type(&self, args: &[DataType]) -> Result { +# if !matches!(args.get(0), Some(&DataType::Int32)) { +# return plan_err!("add_one only accepts Int32 arguments"); +# } +# Ok(DataType::Int32) +# } +# // The actual implementation would add one to the argument +# fn invoke_with_args(&self, args: ScalarFunctionArgs) -> Result { +# let args = ColumnarValue::values_to_arrays(&args.args)?; +# let i64s = as_int64_array(&args[0])?; +# +# let new_array = i64s +# .iter() +# .map(|array_elem| array_elem.map(|value| value + 1)) +# .collect::(); +# +# Ok(ColumnarValue::from(Arc::new(new_array) as ArrayRef)) +# } +# fn documentation(&self) -> Option<&Documentation> { +# self.doc() +# } +# } +use datafusion::execution::context::SessionContext; + +// Create a new ScalarUDF from the implementation +let add_one = ScalarUDF::from(AddOne::new()); + +// Call the function `add_one(col)` +let expr = add_one.call(vec![col("a")]); + +// register the UDF with the context so it can be invoked by name and from SQL +let mut ctx = SessionContext::new(); +ctx.register_udf(add_one.clone()); +``` + +### Adding a Scalar UDF by [`create_udf`] + +There is a an older, more concise, but also more limited API [`create_udf`] available as well + +#### Adding a Scalar UDF + +```rust +use std::sync::Arc; +use datafusion::arrow::array::{ArrayRef, Int64Array}; +use datafusion::common::cast::as_int64_array; +use datafusion::common::Result; +use datafusion::logical_expr::ColumnarValue; + +pub fn add_one(args: &[ColumnarValue]) -> Result { + // Error handling omitted for brevity + let args = ColumnarValue::values_to_arrays(args)?; + let i64s = as_int64_array(&args[0])?; + + let new_array = i64s + .iter() + .map(|array_elem| array_elem.map(|value| value + 1)) + .collect::(); + + Ok(ColumnarValue::from(Arc::new(new_array) as ArrayRef)) +} +``` + +This "works" in isolation, i.e. if you have a slice of `ArrayRef`s, you can call `add_one` and it will return a new +`ArrayRef` with 1 added to each value. + +```rust +# use std::sync::Arc; +# use datafusion::arrow::array::{ArrayRef, Int64Array}; +# use datafusion::common::cast::as_int64_array; +# use datafusion::common::Result; +# use datafusion::logical_expr::ColumnarValue; +# +# pub fn add_one(args: &[ColumnarValue]) -> Result { +# // Error handling omitted for brevity +# let args = ColumnarValue::values_to_arrays(args)?; +# let i64s = as_int64_array(&args[0])?; +# +# let new_array = i64s +# .iter() +# .map(|array_elem| array_elem.map(|value| value + 1)) +# .collect::(); +# +# Ok(ColumnarValue::from(Arc::new(new_array) as ArrayRef)) +# } +let input = vec![Some(1), None, Some(3)]; +let input = ColumnarValue::from(Arc::new(Int64Array::from(input)) as ArrayRef); + +let result = add_one(&[input]).unwrap(); +let binding = result.into_array(1).unwrap(); +let result = binding.as_any().downcast_ref::().unwrap(); + +assert_eq!(result, &Int64Array::from(vec![Some(2), None, Some(4)])); +``` + +The challenge however is that DataFusion doesn't know about this function. We need to register it with DataFusion so +that it can be used in the context of a query. + +#### Registering a Scalar UDF + +To register a Scalar UDF, you need to wrap the function implementation in a [`ScalarUDF`] struct and then register it +with the `SessionContext`. +DataFusion provides the [`create_udf`] and helper functions to make this easier. + +```rust +# use std::sync::Arc; +# use datafusion::arrow::array::{ArrayRef, Int64Array}; +# use datafusion::common::cast::as_int64_array; +# use datafusion::common::Result; +# use datafusion::logical_expr::ColumnarValue; +# +# pub fn add_one(args: &[ColumnarValue]) -> Result { +# // Error handling omitted for brevity +# let args = ColumnarValue::values_to_arrays(args)?; +# let i64s = as_int64_array(&args[0])?; +# +# let new_array = i64s +# .iter() +# .map(|array_elem| array_elem.map(|value| value + 1)) +# .collect::(); +# +# Ok(ColumnarValue::from(Arc::new(new_array) as ArrayRef)) +# } +use datafusion::logical_expr::{Volatility, create_udf}; +use datafusion::arrow::datatypes::DataType; + +let udf = create_udf( + "add_one", + vec![DataType::Int64], + DataType::Int64, + Volatility::Immutable, + Arc::new(add_one), +); +``` + +A few things to note on `create_udf`: + +- The first argument is the name of the function. This is the name that will be used in SQL queries. +- The second argument is a vector of `DataType`s. This is the list of argument types that the function accepts. I.e. in + this case, the function accepts a single `Int64` argument. +- The third argument is the return type of the function. I.e. in this case, the function returns an `Int64`. +- The fourth argument is the volatility of the function. In short, this is used to determine if the function's + performance can be optimized in some situations. In this case, the function is `Immutable` because it always returns + the same value for the same input. A random number generator would be `Volatile` because it returns a different value + for the same input. +- The fifth argument is the function implementation. This is the function that we defined above. + +That gives us a `ScalarUDF` that we can register with the `SessionContext`: + +```rust +# use std::sync::Arc; +# use datafusion::arrow::array::{ArrayRef, Int64Array}; +# use datafusion::common::cast::as_int64_array; +# use datafusion::common::Result; +# use datafusion::logical_expr::ColumnarValue; +# +# pub fn add_one(args: &[ColumnarValue]) -> Result { +# // Error handling omitted for brevity +# let args = ColumnarValue::values_to_arrays(args)?; +# let i64s = as_int64_array(&args[0])?; +# +# let new_array = i64s +# .iter() +# .map(|array_elem| array_elem.map(|value| value + 1)) +# .collect::(); +# +# Ok(ColumnarValue::from(Arc::new(new_array) as ArrayRef)) +# } +use datafusion::logical_expr::{Volatility, create_udf}; +use datafusion::arrow::datatypes::DataType; +use datafusion::execution::context::SessionContext; + +#[tokio::main] +async fn main() { + let udf = create_udf( + "add_one", + vec![DataType::Int64], + DataType::Int64, + Volatility::Immutable, + Arc::new(add_one), + ); + + let mut ctx = SessionContext::new(); + ctx.register_udf(udf); + + // At this point, you can use the `add_one` function in your query: + let query = "SELECT add_one(1)"; + let df = ctx.sql(&query).await.unwrap(); +} +``` + +## Adding an Async Scalar UDF + +An Async Scalar UDF allows you to implement user-defined functions that support +asynchronous execution, such as performing network or I/O operations within the +UDF. + +To add a Scalar Async UDF, you need to: + +1. Implement the `AsyncScalarUDFImpl` trait to define your async function logic, signature, and types. +2. Wrap your implementation with `AsyncScalarUDF::new` and register it with the `SessionContext`. + +### Adding by `impl AsyncScalarUDFImpl` + +```rust +# use arrow::array::{ArrayIter, ArrayRef, AsArray, StringArray}; +# use arrow_schema::DataType; +# use async_trait::async_trait; +# use datafusion::common::error::Result; +# use datafusion::common::{internal_err, not_impl_err}; +# use datafusion::common::types::logical_string; +# use datafusion::config::ConfigOptions; +# use datafusion_expr::ScalarUDFImpl; +# use datafusion::logical_expr::async_udf::AsyncScalarUDFImpl; +# use datafusion::logical_expr::{ +# ColumnarValue, Signature, TypeSignature, TypeSignatureClass, Volatility, ScalarFunctionArgs +# }; +# use datafusion::logical_expr_common::signature::Coercion; +# use std::any::Any; +# use std::sync::Arc; + +#[derive(Debug, PartialEq, Eq, Hash)] +pub struct AsyncUpper { + signature: Signature, +} + +impl Default for AsyncUpper { + fn default() -> Self { + Self::new() + } +} + +impl AsyncUpper { + pub fn new() -> Self { + Self { + signature: Signature::new( + TypeSignature::Coercible(vec![Coercion::new_exact( + TypeSignatureClass::Native(logical_string()), + )]), + Volatility::Volatile, + ), + } + } +} + +/// Implement the normal ScalarUDFImpl trait for AsyncUpper +#[async_trait] +impl ScalarUDFImpl for AsyncUpper { + fn name(&self) -> &str { + "async_upper" + } + + fn signature(&self) -> &Signature { + &self.signature + } + + fn return_type(&self, _arg_types: &[DataType]) -> Result { + Ok(DataType::Utf8) + } + + // Note the normal invoke_with_args method is not called for Async UDFs + fn invoke_with_args( + &self, + _args: ScalarFunctionArgs, + ) -> Result { + not_impl_err!("AsyncUpper can only be called from async contexts") + } +} + +/// The actual implementation of the async UDF +#[async_trait] +impl AsyncScalarUDFImpl for AsyncUpper { + fn ideal_batch_size(&self) -> Option { + Some(10) + } + + /// This method is called to execute the async UDF and is similar + /// to the normal `invoke_with_args` except it is `async`. + async fn invoke_async_with_args( + &self, + args: ScalarFunctionArgs, + ) -> Result { + let value = &args.args[0]; + // This function simply implements a simple string to uppercase conversion + // but can be used for any async operation such as network calls. + let result = match value { + ColumnarValue::Array(array) => { + let string_array = array.as_string::(); + let iter = ArrayIter::new(string_array); + let result = iter + .map(|string| string.map(|s| s.to_uppercase())) + .collect::(); + Arc::new(result) as ArrayRef + } + _ => return internal_err!("Expected a string argument, got {:?}", value), + }; + Ok(ColumnarValue::from(result)) + } +} +``` + +We can now transfer the async UDF into the normal scalar using `into_scalar_udf` to register the function with DataFusion so that it can be used in the context of a query. + +```rust +# use arrow::array::{ArrayIter, ArrayRef, AsArray, StringArray}; +# use arrow_schema::DataType; +# use async_trait::async_trait; +# use datafusion::common::error::Result; +# use datafusion::common::{internal_err, not_impl_err}; +# use datafusion::common::types::logical_string; +# use datafusion::config::ConfigOptions; +# use datafusion_expr::ScalarUDFImpl; +# use datafusion::logical_expr::async_udf::AsyncScalarUDFImpl; +# use datafusion::logical_expr::{ +# ColumnarValue, Signature, TypeSignature, TypeSignatureClass, Volatility, ScalarFunctionArgs +# }; +# use datafusion::logical_expr_common::signature::Coercion; +# use log::trace; +# use std::any::Any; +# use std::sync::Arc; +# +# #[derive(Debug, PartialEq, Eq, Hash)] +# pub struct AsyncUpper { +# signature: Signature, +# } +# +# impl Default for AsyncUpper { +# fn default() -> Self { +# Self::new() +# } +# } +# +# impl AsyncUpper { +# pub fn new() -> Self { +# Self { +# signature: Signature::new( +# TypeSignature::Coercible(vec![Coercion::new_exact( +# TypeSignatureClass::Native(logical_string()), +# )]), +# Volatility::Volatile, +# ), +# } +# } +# } +# +# #[async_trait] +# impl ScalarUDFImpl for AsyncUpper { +# fn name(&self) -> &str { +# "async_upper" +# } +# +# fn signature(&self) -> &Signature { +# &self.signature +# } +# +# fn return_type(&self, _arg_types: &[DataType]) -> Result { +# Ok(DataType::Utf8) +# } +# +# fn invoke_with_args( +# &self, +# _args: ScalarFunctionArgs, +# ) -> Result { +# not_impl_err!("AsyncUpper can only be called from async contexts") +# } +# } +# +# #[async_trait] +# impl AsyncScalarUDFImpl for AsyncUpper { +# fn ideal_batch_size(&self) -> Option { +# Some(10) +# } +# +# async fn invoke_async_with_args( +# &self, +# args: ScalarFunctionArgs, +# ) -> Result { +# trace!("Invoking async_upper with args: {:?}", args); +# let value = &args.args[0]; +# let result = match value { +# ColumnarValue::Array(array) => { +# let string_array = array.as_string::(); +# let iter = ArrayIter::new(string_array); +# let result = iter +# .map(|string| string.map(|s| s.to_uppercase())) +# .collect::(); +# Arc::new(result) as ArrayRef +# } +# _ => return internal_err!("Expected a string argument, got {:?}", value), +# }; +# Ok(ColumnarValue::from(result)) +# } +# } +use datafusion::execution::context::SessionContext; +use datafusion::logical_expr::async_udf::AsyncScalarUDF; + +let async_upper = AsyncUpper::new(); +let udf = AsyncScalarUDF::new(Arc::new(async_upper)); +let mut ctx = SessionContext::new(); +ctx.register_udf(udf.into_scalar_udf()); +``` + +After registration, you can use these async UDFs directly in SQL queries, for example: + +```sql +SELECT async_upper('datafusion'); +``` + +For async UDF implementation details, see [`async_udf.rs`](https://github.com/apache/datafusion/blob/main/datafusion-examples/examples/udf/async_udf.rs). + +[`scalarudf`]: https://docs.rs/datafusion/latest/datafusion/logical_expr/struct.ScalarUDF.html +[`create_udf`]: https://docs.rs/datafusion/latest/datafusion/logical_expr/fn.create_udf.html +[`advanced_udf.rs`]: https://github.com/apache/datafusion/blob/main/datafusion-examples/examples/udf/advanced_udf.rs + +## Named Arguments + +DataFusion supports named arguments for Scalar, Window, and Aggregate UDFs, allowing you to pass arguments by parameter name: + +```sql +-- Scalar function +SELECT substr(str => 'hello', start_pos => 2, length => 3); + +-- Window function +SELECT lead(expr => value, offset => 1) OVER (ORDER BY id) FROM table; + +-- Aggregate function +SELECT corr(y => col1, x => col2) FROM table; +``` + +Named arguments can be mixed with positional arguments, but positional arguments must come first: + +```sql +SELECT substr('hello', start_pos => 2, length => 3); -- Valid +``` + +### Implementing Functions with Named Arguments + +To support named arguments in your UDF, add parameter names to your function's signature using `.with_parameter_names()`. This works the same way for Scalar, Window, and Aggregate UDFs: + +```rust +# use std::sync::Arc; +# use std::any::Any; +# use arrow::datatypes::DataType; +# use datafusion_common::Result; +# use datafusion_expr::{ColumnarValue, ScalarFunctionArgs, Signature, Volatility}; +# use datafusion_expr::ScalarUDFImpl; + +#[derive(Debug, PartialEq, Eq, Hash)] +struct PowerFunction { + signature: Signature, +} + +impl PowerFunction { + fn new() -> Self { + Self { + signature: Signature::uniform( + 2, + vec![DataType::Float64], + Volatility::Immutable + ) + .with_parameter_names(vec![ + "base".to_string(), + "exponent".to_string() + ]) + .expect("valid parameter names"), + } + } +} + +impl ScalarUDFImpl for PowerFunction { + fn name(&self) -> &str { "power" } + fn signature(&self) -> &Signature { &self.signature } + + fn return_type(&self, _args: &[DataType]) -> Result { + Ok(DataType::Float64) + } + + fn invoke_with_args(&self, _args: ScalarFunctionArgs) -> Result { + // Your implementation - arguments are in correct positional order + unimplemented!() + } +} +``` + +The parameter names should match the order of arguments in your function's signature. DataFusion automatically resolves named arguments to the correct positional order before invoking your function. + +Once registered, users can call your functions with named arguments in any order: + +```sql +-- All equivalent +SELECT power(base => 2.0, exponent => 3.0); +SELECT power(exponent => 3.0, base => 2.0); +SELECT power(2.0, exponent => 3.0); +``` + +### Error Messages + +When a function call fails due to incorrect arguments, DataFusion will show the parameter names in error messages to help users: + +```text +No function matches the given name and argument types substr(Utf8). + Candidate functions: + substr(str: Any, start_pos: Any) + substr(str: Any, start_pos: Any, length: Any) +``` + +## Adding a Window UDF + +Scalar UDFs are functions that take a row of data and return a single value. Window UDFs are similar, but they also have +access to the rows around them. Access to the proximal rows is helpful, but adds some complexity to the implementation. + +For background and other considerations, see the [User defined Window Functions in DataFusion] blog. + +[user defined window functions in datafusion]: https://datafusion.apache.org/blog/2025/04/19/user-defined-window-functions + +For example, we will declare a user defined window function that computes a moving average. + +```rust +use datafusion::arrow::{array::{ArrayRef, Float64Array, AsArray}, datatypes::Float64Type}; +use datafusion::logical_expr::{PartitionEvaluator}; +use datafusion::common::ScalarValue; +use datafusion::error::Result; +/// This implements the lowest level evaluation for a window function +/// +/// It handles calculating the value of the window function for each +/// distinct values of `PARTITION BY` +#[derive(Clone, Debug)] +struct MyPartitionEvaluator {} + +impl MyPartitionEvaluator { + fn new() -> Self { + Self {} + } +} + +/// Different evaluation methods are called depending on the various +/// settings of WindowUDF. This example uses the simplest and most +/// general, `evaluate`. See `PartitionEvaluator` for the other more +/// advanced uses. +impl PartitionEvaluator for MyPartitionEvaluator { + /// Tell DataFusion the window function varies based on the value + /// of the window frame. + fn uses_window_frame(&self) -> bool { + true + } + + /// This function is called once per input row. + /// + /// `range`specifies which indexes of `values` should be + /// considered for the calculation. + /// + /// Note this is the SLOWEST, but simplest, way to evaluate a + /// window function. It is much faster to implement + /// evaluate_all or evaluate_all_with_rank, if possible + fn evaluate( + &mut self, + values: &[ArrayRef], + range: &std::ops::Range, + ) -> Result { + // Again, the input argument is an array of floating + // point numbers to calculate a moving average + let arr: &Float64Array = values[0].as_ref().as_primitive::(); + + let range_len = range.end - range.start; + + // our smoothing function will average all the values in the + let output = if range_len > 0 { + let sum: f64 = arr.values().iter().skip(range.start).take(range_len).sum(); + Some(sum / range_len as f64) + } else { + None + }; + + Ok(ScalarValue::Float64(output)) + } +} + +/// Create a `PartitionEvaluator` to evaluate this function on a new +/// partition. +fn make_partition_evaluator() -> Result> { + Ok(Box::new(MyPartitionEvaluator::new())) +} +``` + +### Registering a Window UDF + +To register a Window UDF, you need to wrap the function implementation in a [`WindowUDF`] struct and then register it +with the `SessionContext`. DataFusion provides the [`create_udwf`] helper functions to make this easier. +There is a lower level API with more functionality but is more complex, that is documented in [`advanced_udwf.rs`]. + +```rust +# use datafusion::arrow::{array::{ArrayRef, Float64Array, AsArray}, datatypes::Float64Type}; +# use datafusion::logical_expr::{PartitionEvaluator}; +# use datafusion::common::ScalarValue; +# use datafusion::error::Result; +# +# #[derive(Clone, Debug)] +# struct MyPartitionEvaluator {} +# +# impl MyPartitionEvaluator { +# fn new() -> Self { +# Self {} +# } +# } +# +# impl PartitionEvaluator for MyPartitionEvaluator { +# fn uses_window_frame(&self) -> bool { +# true +# } +# +# fn evaluate( +# &mut self, +# values: &[ArrayRef], +# range: &std::ops::Range, +# ) -> Result { +# // Again, the input argument is an array of floating +# // point numbers to calculate a moving average +# let arr: &Float64Array = values[0].as_ref().as_primitive::(); +# +# let range_len = range.end - range.start; +# +# // our smoothing function will average all the values in the +# let output = if range_len > 0 { +# let sum: f64 = arr.values().iter().skip(range.start).take(range_len).sum(); +# Some(sum / range_len as f64) +# } else { +# None +# }; +# +# Ok(ScalarValue::Float64(output)) +# } +# } +# fn make_partition_evaluator() -> Result> { +# Ok(Box::new(MyPartitionEvaluator::new())) +# } +use datafusion::logical_expr::{Volatility, create_udwf}; +use datafusion::arrow::datatypes::DataType; +use std::sync::Arc; + +// here is where we define the UDWF. We also declare its signature: +let smooth_it = create_udwf( + "smooth_it", + DataType::Float64, + Arc::new(DataType::Float64), + Volatility::Immutable, + Arc::new(make_partition_evaluator), +); +``` + +[`windowudf`]: https://docs.rs/datafusion/latest/datafusion/logical_expr/struct.WindowUDF.html +[`create_udwf`]: https://docs.rs/datafusion/latest/datafusion/logical_expr/fn.create_udwf.html +[`advanced_udwf.rs`]: https://github.com/apache/datafusion/blob/main/datafusion-examples/examples/udf/advanced_udwf.rs + +The `create_udwf` has five arguments to check: + +- The first argument is the name of the function. This is the name that will be used in SQL queries. +- **The second argument** is the `DataType` of input array (attention: this is not a list of arrays). I.e. in this case, + the function accepts `Float64` as argument. +- The third argument is the return type of the function. I.e. in this case, the function returns an `Float64`. +- The fourth argument is the volatility of the function. In short, this is used to determine if the function's + performance can be optimized in some situations. In this case, the function is `Immutable` because it always returns + the same value for the same input. A random number generator would be `Volatile` because it returns a different value + for the same input. +- **The fifth argument** is the function implementation. This is the function that we defined above. + +That gives us a `WindowUDF` that we can register with the `SessionContext`: + +```rust +# use datafusion::arrow::{array::{ArrayRef, Float64Array, AsArray}, datatypes::Float64Type}; +# use datafusion::logical_expr::{PartitionEvaluator}; +# use datafusion::common::ScalarValue; +# use datafusion::error::Result; +# +# #[derive(Clone, Debug)] +# struct MyPartitionEvaluator {} +# +# impl MyPartitionEvaluator { +# fn new() -> Self { +# Self {} +# } +# } +# +# impl PartitionEvaluator for MyPartitionEvaluator { +# fn uses_window_frame(&self) -> bool { +# true +# } +# +# fn evaluate( +# &mut self, +# values: &[ArrayRef], +# range: &std::ops::Range, +# ) -> Result { +# // Again, the input argument is an array of floating +# // point numbers to calculate a moving average +# let arr: &Float64Array = values[0].as_ref().as_primitive::(); +# +# let range_len = range.end - range.start; +# +# // our smoothing function will average all the values in the +# let output = if range_len > 0 { +# let sum: f64 = arr.values().iter().skip(range.start).take(range_len).sum(); +# Some(sum / range_len as f64) +# } else { +# None +# }; +# +# Ok(ScalarValue::Float64(output)) +# } +# } +# fn make_partition_evaluator() -> Result> { +# Ok(Box::new(MyPartitionEvaluator::new())) +# } +# use datafusion::logical_expr::{Volatility, create_udwf}; +# use datafusion::arrow::datatypes::DataType; +# use std::sync::Arc; +# +# // here is where we define the UDWF. We also declare its signature: +# let smooth_it = create_udwf( +# "smooth_it", +# DataType::Float64, +# Arc::new(DataType::Float64), +# Volatility::Immutable, +# Arc::new(make_partition_evaluator), +# ); +use datafusion::execution::context::SessionContext; + +let ctx = SessionContext::new(); + +ctx.register_udwf(smooth_it); +``` + +At this point, you can use the `smooth_it` function in your query: + +For example, if we have a [`cars.csv`](https://github.com/apache/datafusion/blob/main/datafusion/core/tests/data/cars.csv) whose contents like + +```csv +car,speed,time +red,20.0,1996-04-12T12:05:03.000000000 +red,20.3,1996-04-12T12:05:04.000000000 +green,10.0,1996-04-12T12:05:03.000000000 +green,10.3,1996-04-12T12:05:04.000000000 +... +``` + +Then, we can query like below: + +```rust +# use datafusion::arrow::{array::{ArrayRef, Float64Array, AsArray}, datatypes::Float64Type}; +# use datafusion::logical_expr::{PartitionEvaluator}; +# use datafusion::common::ScalarValue; +# use datafusion::error::Result; +# +# #[derive(Clone, Debug)] +# struct MyPartitionEvaluator {} +# +# impl MyPartitionEvaluator { +# fn new() -> Self { +# Self {} +# } +# } +# +# impl PartitionEvaluator for MyPartitionEvaluator { +# fn uses_window_frame(&self) -> bool { +# true +# } +# +# fn evaluate( +# &mut self, +# values: &[ArrayRef], +# range: &std::ops::Range, +# ) -> Result { +# // Again, the input argument is an array of floating +# // point numbers to calculate a moving average +# let arr: &Float64Array = values[0].as_ref().as_primitive::(); +# +# let range_len = range.end - range.start; +# +# // our smoothing function will average all the values in the +# let output = if range_len > 0 { +# let sum: f64 = arr.values().iter().skip(range.start).take(range_len).sum(); +# Some(sum / range_len as f64) +# } else { +# None +# }; +# +# Ok(ScalarValue::Float64(output)) +# } +# } +# fn make_partition_evaluator() -> Result> { +# Ok(Box::new(MyPartitionEvaluator::new())) +# } +# use datafusion::logical_expr::{Volatility, create_udwf}; +# use datafusion::arrow::datatypes::DataType; +# use std::sync::Arc; +# use datafusion::execution::context::SessionContext; + +use datafusion::datasource::file_format::options::CsvReadOptions; + +#[tokio::main] +async fn main() -> Result<()> { + + let ctx = SessionContext::new(); + + let smooth_it = create_udwf( + "smooth_it", + DataType::Float64, + Arc::new(DataType::Float64), + Volatility::Immutable, + Arc::new(make_partition_evaluator), + ); + ctx.register_udwf(smooth_it); + + // register csv table first + let csv_path = "../../datafusion/core/tests/data/cars.csv".to_string(); + ctx.register_csv("cars", &csv_path, CsvReadOptions::default().has_header(true)).await?; + + // do query with smooth_it + let df = ctx + .sql(r#" + SELECT + car, + speed, + smooth_it(speed) OVER (PARTITION BY car ORDER BY time) as smooth_speed, + time + FROM cars + ORDER BY car + "#) + .await?; + + // print the results + df.show().await?; + Ok(()) +} +``` + +The output will be like: + +```text ++-------+-------+--------------------+---------------------+ +| car | speed | smooth_speed | time | ++-------+-------+--------------------+---------------------+ +| green | 10.0 | 10.0 | 1996-04-12T12:05:03 | +| green | 10.3 | 10.15 | 1996-04-12T12:05:04 | +| green | 10.4 | 10.233333333333334 | 1996-04-12T12:05:05 | +| green | 10.5 | 10.3 | 1996-04-12T12:05:06 | +| green | 11.0 | 10.440000000000001 | 1996-04-12T12:05:07 | +| green | 12.0 | 10.700000000000001 | 1996-04-12T12:05:08 | +| green | 14.0 | 11.171428571428573 | 1996-04-12T12:05:09 | +| green | 15.0 | 11.65 | 1996-04-12T12:05:10 | +| green | 15.1 | 12.033333333333333 | 1996-04-12T12:05:11 | +| green | 15.2 | 12.35 | 1996-04-12T12:05:12 | +| green | 8.0 | 11.954545454545455 | 1996-04-12T12:05:13 | +| green | 2.0 | 11.125 | 1996-04-12T12:05:14 | +| red | 20.0 | 20.0 | 1996-04-12T12:05:03 | +| red | 20.3 | 20.15 | 1996-04-12T12:05:04 | +... +``` + +## Adding an Aggregate UDF + +Aggregate UDFs are functions that take a group of rows and return a single value. These are akin to SQL's `SUM` or +`COUNT` functions. + +For example, we will declare a single-type, single return type UDAF that computes the geometric mean. + +```rust + +use datafusion::arrow::array::ArrayRef; +use datafusion::scalar::ScalarValue; +use datafusion::{error::Result, physical_plan::Accumulator}; + +/// A UDAF has state across multiple rows, and thus we require a `struct` with that state. +#[derive(Debug)] +struct GeometricMean { + n: u32, + prod: f64, +} + +impl GeometricMean { + // how the struct is initialized + pub fn new() -> Self { + GeometricMean { n: 0, prod: 1.0 } + } +} + +// UDAFs are built using the trait `Accumulator`, that offers DataFusion the necessary functions +// to use them. +impl Accumulator for GeometricMean { + // This function serializes our state to `ScalarValue`, which DataFusion uses + // to pass this state between execution stages. + // Note that this can be arbitrary data. + fn state(&mut self) -> Result> { + Ok(vec![ + ScalarValue::from(self.prod), + ScalarValue::from(self.n), + ]) + } + + // DataFusion expects this function to return the final value of this aggregator. + // in this case, this is the formula of the geometric mean + fn evaluate(&mut self) -> Result { + let value = self.prod.powf(1.0 / self.n as f64); + Ok(ScalarValue::from(value)) + } + + // DataFusion calls this function to update the accumulator's state for a batch + // of inputs rows. In this case the product is updated with values from the first column + // and the count is updated based on the row count + fn update_batch(&mut self, values: &[ArrayRef]) -> Result<()> { + if values.is_empty() { + return Ok(()); + } + let arr = &values[0]; + (0..arr.len()).try_for_each(|index| { + let v = ScalarValue::try_from_array(arr, index)?; + + if let ScalarValue::Float64(Some(value)) = v { + self.prod *= value; + self.n += 1; + } else { + unreachable!("") + } + Ok(()) + }) + } + + // Optimization hint: this trait also supports `update_batch` and `merge_batch`, + // that can be used to perform these operations on arrays instead of single values. + fn merge_batch(&mut self, states: &[ArrayRef]) -> Result<()> { + if states.is_empty() { + return Ok(()); + } + let arr = &states[0]; + (0..arr.len()).try_for_each(|index| { + let v = states + .iter() + .map(|array| ScalarValue::try_from_array(array, index)) + .collect::>>()?; + if let (ScalarValue::Float64(Some(prod)), ScalarValue::UInt32(Some(n))) = (&v[0], &v[1]) + { + self.prod *= prod; + self.n += n; + } else { + unreachable!("") + } + Ok(()) + }) + } + + fn size(&self) -> usize { + std::mem::size_of_val(self) + } +} +``` + +### Registering an Aggregate UDF + +To register a Aggregate UDF, you need to wrap the function implementation in a [`AggregateUDF`] struct and then register +it with the `SessionContext`. DataFusion provides the [`create_udaf`] helper functions to make this easier. +There is a lower level API with more functionality but is more complex, that is documented in [`advanced_udaf.rs`]. + +```rust +# use datafusion::arrow::array::ArrayRef; +# use datafusion::scalar::ScalarValue; +# use datafusion::{error::Result, physical_plan::Accumulator}; +# +# #[derive(Debug)] +# struct GeometricMean { +# n: u32, +# prod: f64, +# } +# +# impl GeometricMean { +# pub fn new() -> Self { +# GeometricMean { n: 0, prod: 1.0 } +# } +# } +# +# impl Accumulator for GeometricMean { +# fn state(&mut self) -> Result> { +# Ok(vec![ +# ScalarValue::from(self.prod), +# ScalarValue::from(self.n), +# ]) +# } +# +# fn evaluate(&mut self) -> Result { +# let value = self.prod.powf(1.0 / self.n as f64); +# Ok(ScalarValue::from(value)) +# } +# +# fn update_batch(&mut self, values: &[ArrayRef]) -> Result<()> { +# if values.is_empty() { +# return Ok(()); +# } +# let arr = &values[0]; +# (0..arr.len()).try_for_each(|index| { +# let v = ScalarValue::try_from_array(arr, index)?; +# +# if let ScalarValue::Float64(Some(value)) = v { +# self.prod *= value; +# self.n += 1; +# } else { +# unreachable!("") +# } +# Ok(()) +# }) +# } +# +# fn merge_batch(&mut self, states: &[ArrayRef]) -> Result<()> { +# if states.is_empty() { +# return Ok(()); +# } +# let arr = &states[0]; +# (0..arr.len()).try_for_each(|index| { +# let v = states +# .iter() +# .map(|array| ScalarValue::try_from_array(array, index)) +# .collect::>>()?; +# if let (ScalarValue::Float64(Some(prod)), ScalarValue::UInt32(Some(n))) = (&v[0], &v[1]) +# { +# self.prod *= prod; +# self.n += n; +# } else { +# unreachable!("") +# } +# Ok(()) +# }) +# } +# +# fn size(&self) -> usize { +# std::mem::size_of_val(self) +# } +# } + +use datafusion::logical_expr::{Volatility, create_udaf}; +use datafusion::arrow::datatypes::DataType; +use std::sync::Arc; + +// here is where we define the UDAF. We also declare its signature: +let geometric_mean = create_udaf( + // the name; used to represent it in plan descriptions and in the registry, to use in SQL. + "geo_mean", + // the input type; DataFusion guarantees that the first entry of `values` in `update` has this type. + vec![DataType::Float64], + // the return type; DataFusion expects this to match the type returned by `evaluate`. + Arc::new(DataType::Float64), + Volatility::Immutable, + // This is the accumulator factory; DataFusion uses it to create new accumulators. + Arc::new( | _ | Ok(Box::new(GeometricMean::new()))), + // This is the description of the state. `state()` must match the types here. + Arc::new(vec![DataType::Float64, DataType::UInt32]), +); +``` + +The `create_udaf` has six arguments to check: + +- The first argument is the name of the function. This is the name that will be used in SQL queries. +- The second argument is a vector of `DataType`s. This is the list of argument types that the function accepts. I.e. in + this case, the function accepts a single `Float64` argument. +- The third argument is the return type of the function. I.e. in this case, the function returns an `Int64`. +- The fourth argument is the volatility of the function. In short, this is used to determine if the function's + performance can be optimized in some situations. In this case, the function is `Immutable` because it always returns + the same value for the same input. A random number generator would be `Volatile` because it returns a different value + for the same input. +- The fifth argument is the function implementation. This is the function that we defined above. +- The sixth argument is the description of the state, which will by passed between execution stages. + +### Returning multiple values from an Aggregate UDF + +An aggregate UDF can return a `DataType::Struct` when one aggregate result needs +to carry multiple values. This is useful for time-windowing extensions that +need to return metadata such as the window start, window end, and the aggregate +value together. + +Pass the relevant input columns to the aggregate so the accumulator has enough +information to update and merge state normally in multi-stage aggregate plans. +For example, rows can be grouped into time buckets with the built-in `date_bin` +function, while a struct-returning aggregate computes the value and carries +metadata about each bucket: + +```sql +SELECT + augmented_avg(time, value)['window_start'] AS window_start, + augmented_avg(time, value)['window_end'] AS window_end, + augmented_avg(time, value)['window_duration'] AS window_duration, + augmented_avg(time, value)['avg_value'] AS avg_value +FROM t +GROUP BY date_bin(INTERVAL '30 seconds', time) +ORDER BY window_start; +``` + +In this pattern `date_bin(...)` assigns rows to a time bucket, while +`augmented_avg(time, value)` is a normal aggregate UDF whose accumulator stores +mergeable state such as `window_start`, `window_end`, `sum`, and `count`. +The aggregate's `evaluate` method returns a `ScalarValue::Struct`, and callers +can project individual fields from that struct. + +```rust + +# use datafusion::arrow::array::ArrayRef; +# use datafusion::scalar::ScalarValue; +# use datafusion::{error::Result, physical_plan::Accumulator}; +# +# #[derive(Debug)] +# struct GeometricMean { +# n: u32, +# prod: f64, +# } +# +# impl GeometricMean { +# pub fn new() -> Self { +# GeometricMean { n: 0, prod: 1.0 } +# } +# } +# +# impl Accumulator for GeometricMean { +# fn state(&mut self) -> Result> { +# Ok(vec![ +# ScalarValue::from(self.prod), +# ScalarValue::from(self.n), +# ]) +# } +# +# fn evaluate(&mut self) -> Result { +# let value = self.prod.powf(1.0 / self.n as f64); +# Ok(ScalarValue::from(value)) +# } +# +# fn update_batch(&mut self, values: &[ArrayRef]) -> Result<()> { +# if values.is_empty() { +# return Ok(()); +# } +# let arr = &values[0]; +# (0..arr.len()).try_for_each(|index| { +# let v = ScalarValue::try_from_array(arr, index)?; +# +# if let ScalarValue::Float64(Some(value)) = v { +# self.prod *= value; +# self.n += 1; +# } else { +# unreachable!("") +# } +# Ok(()) +# }) +# } +# +# fn merge_batch(&mut self, states: &[ArrayRef]) -> Result<()> { +# if states.is_empty() { +# return Ok(()); +# } +# let arr = &states[0]; +# (0..arr.len()).try_for_each(|index| { +# let v = states +# .iter() +# .map(|array| ScalarValue::try_from_array(array, index)) +# .collect::>>()?; +# if let (ScalarValue::Float64(Some(prod)), ScalarValue::UInt32(Some(n))) = (&v[0], &v[1]) +# { +# self.prod *= prod; +# self.n += n; +# } else { +# unreachable!("") +# } +# Ok(()) +# }) +# } +# +# fn size(&self) -> usize { +# std::mem::size_of_val(self) +# } +# } + +use datafusion::logical_expr::{Volatility, create_udaf}; +use datafusion::arrow::datatypes::DataType; +use std::sync::Arc; +use datafusion::execution::context::SessionContext; +use datafusion::datasource::file_format::options::CsvReadOptions; + +#[tokio::main] +async fn main() -> Result<()> { + let geometric_mean = create_udaf( + "geo_mean", + vec![DataType::Float64], + Arc::new(DataType::Float64), + Volatility::Immutable, + Arc::new( | _ | Ok(Box::new(GeometricMean::new()))), + Arc::new(vec![DataType::Float64, DataType::UInt32]), + ); + + // That gives us a `AggregateUDF` that we can register with the `SessionContext`: + use datafusion::execution::context::SessionContext; + + let ctx = SessionContext::new(); + ctx.register_udaf(geometric_mean); + + // register csv table first + let csv_path = "../../datafusion/core/tests/data/cars.csv".to_string(); + ctx.register_csv("cars", &csv_path, CsvReadOptions::default().has_header(true)).await?; + + // Then, we can query like below: + let df = ctx.sql("SELECT geo_mean(speed) FROM cars").await?; + Ok(()) +} + +``` + +[`aggregateudf`]: https://docs.rs/datafusion/latest/datafusion/logical_expr/struct.AggregateUDF.html +[`create_udaf`]: https://docs.rs/datafusion/latest/datafusion/logical_expr/fn.create_udaf.html +[`advanced_udaf.rs`]: https://github.com/apache/datafusion/blob/main/datafusion-examples/examples/udf/advanced_udaf.rs + +## Adding a Table UDF + +A User-Defined Table Function (UDTF) is a function that takes parameters and returns a `TableProvider`. + +Because we're returning a `TableProvider`, in this example we'll use the `MemTable` data source to represent a table. +This is a simple struct that holds a set of RecordBatches in memory and treats them as a table. In your case, this would +be replaced with your own struct that implements `TableProvider`. + +While this is a simple example for illustrative purposes, UDTFs have a lot of potential use cases. And can be +particularly useful for reading data from external sources and interactive analysis. See the [working example][simple_udtf.rs] +which reads from a CSV file. As another example, you could use the built-in UDTF `parquet_metadata` +in the CLI to read the metadata from a Parquet file. + +```console +> select filename, row_group_id, row_group_num_rows, row_group_bytes, stats_min, stats_max from parquet_metadata('./benchmarks/data/hits.parquet') where column_id = 17 limit 10; ++--------------------------------+--------------+--------------------+-----------------+-----------+-----------+ +| filename | row_group_id | row_group_num_rows | row_group_bytes | stats_min | stats_max | ++--------------------------------+--------------+--------------------+-----------------+-----------+-----------+ +| ./benchmarks/data/hits.parquet | 0 | 450560 | 188921521 | 0 | 73256 | +| ./benchmarks/data/hits.parquet | 1 | 612174 | 210338885 | 0 | 109827 | +| ./benchmarks/data/hits.parquet | 2 | 344064 | 161242466 | 0 | 122484 | +| ./benchmarks/data/hits.parquet | 3 | 606208 | 235549898 | 0 | 121073 | +| ./benchmarks/data/hits.parquet | 4 | 335872 | 137103898 | 0 | 108996 | +| ./benchmarks/data/hits.parquet | 5 | 311296 | 145453612 | 0 | 108996 | +| ./benchmarks/data/hits.parquet | 6 | 303104 | 138833963 | 0 | 108996 | +| ./benchmarks/data/hits.parquet | 7 | 303104 | 191140113 | 0 | 73256 | +| ./benchmarks/data/hits.parquet | 8 | 573440 | 208038598 | 0 | 95823 | +| ./benchmarks/data/hits.parquet | 9 | 344064 | 147838157 | 0 | 73256 | ++--------------------------------+--------------+--------------------+-----------------+-----------+-----------+ +``` + +### Writing the UDTF + +The simple UDTF used here takes a single `Int64` argument and returns a table with a single column with the value of the +argument. To create a function in DataFusion, you need to implement the `TableFunctionImpl` trait. This trait has a +single method, `call_with_args`, that takes a `TableFunctionArgs` struct and returns a `Result>`. +Passed struct includes function arguments as a slice of `Expr`s. + +In the `call_with_args` method, you parse the input `Expr`s and return a `TableProvider`. You might also want to do some +validation of the input `Expr`s, e.g. checking that the number of arguments is correct. + +```rust +use std::sync::Arc; +use datafusion::common::{plan_err, ScalarValue, Result}; +use datafusion::catalog::{TableFunctionArgs, TableFunctionImpl, TableProvider}; +use datafusion::arrow::array::{ArrayRef, Int64Array}; +use datafusion::datasource::memory::MemTable; +use arrow::record_batch::RecordBatch; +use arrow::datatypes::{DataType, Field, Schema}; +use datafusion_expr::Expr; + +/// A table function that returns a table provider with the value as a single column +#[derive(Debug)] +pub struct EchoFunction {} + +impl TableFunctionImpl for EchoFunction { + fn call_with_args(&self, args: TableFunctionArgs) -> Result> { + let exprs = args.exprs(); + let Some(Expr::Literal(ScalarValue::Int64(Some(value)), _)) = exprs.get(0) else { + return plan_err!("First argument must be an integer"); + }; + + // Create the schema for the table + let schema = Arc::new(Schema::new(vec![Field::new("a", DataType::Int64, false)])); + + // Create a single RecordBatch with the value as a single column + let batch = RecordBatch::try_new( + schema.clone(), + vec![Arc::new(Int64Array::from(vec![*value]))], + )?; + + // Create a MemTable plan that returns the RecordBatch + let provider = MemTable::try_new(schema, vec![vec![batch]])?; + + Ok(Arc::new(provider)) + } +} +``` + +### Registering and Using the UDTF + +With the UDTF implemented, you can register it with the `SessionContext`: + +```rust +# use std::sync::Arc; +# use datafusion::common::{plan_err, ScalarValue, Result}; +# use datafusion::catalog::{TableFunctionArgs, TableFunctionImpl, TableProvider}; +# use datafusion::arrow::array::{ArrayRef, Int64Array}; +# use datafusion::datasource::memory::MemTable; +# use arrow::record_batch::RecordBatch; +# use arrow::datatypes::{DataType, Field, Schema}; +# use datafusion_expr::Expr; +# +# /// A table function that returns a table provider with the value as a single column +# #[derive(Debug, Default)] +# pub struct EchoFunction {} +# +# impl TableFunctionImpl for EchoFunction { +# fn call_with_args(&self, args: TableFunctionArgs) -> Result> { +# let exprs = args.exprs(); +# let Some(Expr::Literal(ScalarValue::Int64(Some(value)), _)) = exprs.get(0) else { +# return plan_err!("First argument must be an integer"); +# }; +# +# // Create the schema for the table +# let schema = Arc::new(Schema::new(vec![Field::new("a", DataType::Int64, false)])); +# +# // Create a single RecordBatch with the value as a single column +# let batch = RecordBatch::try_new( +# schema.clone(), +# vec![Arc::new(Int64Array::from(vec![*value]))], +# )?; +# +# // Create a MemTable plan that returns the RecordBatch +# let provider = MemTable::try_new(schema, vec![vec![batch]])?; +# +# Ok(Arc::new(provider)) +# } +# } + +use datafusion::execution::context::SessionContext; +use datafusion::arrow::util::pretty; + +#[tokio::main] +async fn main() -> Result<()> { + let ctx = SessionContext::new(); + + ctx.register_udtf("echo", Arc::new(EchoFunction::default())); + + // And if all goes well, you can use it in your query: + + let results = ctx.sql("SELECT * FROM echo(1)").await?.collect().await?; + pretty::print_batches(&results)?; + Ok(()) +} + +// +---+ +// | a | +// +---+ +// | 1 | +// +---+ +``` + +## Custom Expression Planning + +DataFusion provides native support for common SQL operators and constructs by default such as `+`, `-`, `||`. However it does not provide support for other operators such as `@>` or constructs like `TABLESAMPLE` which are less common or vary more between SQL dialects. To override DataFusion's default handling or support these unsupported features, developers can extend DataFusion by implementing custom expression planning, a core feature of DataFusion. + +For a comprehensive guide on extending SQL syntax including `ExprPlanner`, `TypePlanner`, and `RelationPlanner`, see [Extending DataFusion's SQL Syntax](../extending-sql.md) + +### Implementing Custom Expression Planning + +To extend DataFusion with support for custom operators not natively available, you need to: + +1. Implement the `ExprPlanner` trait: This allows you to define custom logic for planning expressions that DataFusion doesn't natively recognize. The trait provides the necessary interface to translate SQL AST nodes into logical `Expr`. + + For detailed documentation please see: [Trait ExprPlanner](https://docs.rs/datafusion/latest/datafusion/logical_expr/planner/trait.ExprPlanner.html) + +2. Register your custom planner: Integrate your implementation with DataFusion's `SessionContext` to ensure your custom planning logic is invoked during the query optimization and execution planning phase. + + For a detailed documentation see: [fn register_expr_planner](https://docs.rs/datafusion/latest/datafusion/execution/trait.FunctionRegistry.html#method.register_expr_planner) + +See example below: + +```rust +# use arrow::array::RecordBatch; +# use std::sync::Arc; + +# use datafusion::common::{assert_batches_eq, DFSchema}; +# use datafusion::error::Result; +# use datafusion::execution::FunctionRegistry; +# use datafusion::logical_expr::Operator; +# use datafusion::prelude::*; +# use datafusion::sql::sqlparser::ast::BinaryOperator; +# use datafusion_common::ScalarValue; +# use datafusion_expr::expr::Alias; +# use datafusion_expr::planner::{ExprPlanner, PlannerResult, RawBinaryExpr}; +# use datafusion_expr::BinaryExpr; + +# #[derive(Debug)] +# // Define the custom planner +# struct MyCustomPlanner; + +// Implement ExprPlanner to add support for the `->` custom operator +impl ExprPlanner for MyCustomPlanner { + fn plan_binary_op( + &self, + expr: RawBinaryExpr, + _schema: &DFSchema, + ) -> Result> { + match &expr.op { + // Map `->` to string concatenation + BinaryOperator::Arrow => { + // Rewrite `->` as a string concatenation operation + // - `left` and `right` are the operands (e.g., 'hello' and 'world') + // - `Operator::StringConcat` tells DataFusion to concatenate them + Ok(PlannerResult::Planned(Expr::BinaryExpr(BinaryExpr { + left: Box::new(expr.left.clone()), + right: Box::new(expr.right.clone()), + op: Operator::StringConcat, + }))) + } + _ => Ok(PlannerResult::Original(expr)), + } + } +} + +use datafusion::execution::context::SessionContext; +use datafusion::arrow::util::pretty; + +#[tokio::main] +async fn main() -> Result<()> { + let config = SessionConfig::new().set_str("datafusion.sql_parser.dialect", "postgres"); + let mut ctx = SessionContext::new_with_config(config); + ctx.register_expr_planner(Arc::new(MyCustomPlanner))?; + let results = ctx.sql("select 'foo'->'bar';").await?.collect().await?; + + let expected = [ + "+----------------------------+", + "| Utf8(\"foo\") || Utf8(\"bar\") |", + "+----------------------------+", + "| foobar |", + "+----------------------------+", + ]; + assert_batches_eq!(&expected, &results); + + pretty::print_batches(&results)?; + Ok(()) +} +``` diff --git a/versions/55.0.0/_sources/library-user-guide/functions/index.rst.txt b/versions/55.0.0/_sources/library-user-guide/functions/index.rst.txt new file mode 100644 index 0000000000000..d6127446c2286 --- /dev/null +++ b/versions/55.0.0/_sources/library-user-guide/functions/index.rst.txt @@ -0,0 +1,25 @@ +.. 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. + +Functions +============= + +.. toctree:: + :maxdepth: 2 + + adding-udfs + spark diff --git a/versions/55.0.0/_sources/library-user-guide/functions/spark.md.txt b/versions/55.0.0/_sources/library-user-guide/functions/spark.md.txt new file mode 100644 index 0000000000000..c371ae1cb5a86 --- /dev/null +++ b/versions/55.0.0/_sources/library-user-guide/functions/spark.md.txt @@ -0,0 +1,29 @@ + + +# Spark Compatible Functions + +The [`datafusion-spark`] crate provides Apache Spark-compatible expressions for +use with DataFusion. + +[`datafusion-spark`]: https://crates.io/crates/datafusion-spark + +Please see the documentation for the [`datafusion-spark` crate] for more details. + +[`datafusion-spark` crate]: https://docs.rs/datafusion-spark/latest/datafusion_spark/ diff --git a/versions/55.0.0/_sources/library-user-guide/index.md.txt b/versions/55.0.0/_sources/library-user-guide/index.md.txt new file mode 100644 index 0000000000000..fd126a1120edf --- /dev/null +++ b/versions/55.0.0/_sources/library-user-guide/index.md.txt @@ -0,0 +1,43 @@ + + +# Introduction + +The library user guide explains how to use the DataFusion library as a +dependency in your Rust project and customize its behavior using its extension APIs. + +Please check out the [user guide] for getting started using +DataFusion's SQL and DataFrame APIs, or the [contributor guide] +for details on how to contribute to DataFusion. + +If you haven't reviewed the [architecture section in the docs][docs], it's a +useful place to get the lay of the land before starting down a specific path. + +DataFusion is designed to be extensible at all points, including + +- [x] User Defined Functions (UDFs) +- [x] User Defined Aggregate Functions (UDAFs) +- [x] User Defined Table Source (`TableProvider`) for tables +- [x] User Defined `Optimizer` passes (plan rewrites) +- [x] User Defined `LogicalPlan` nodes +- [x] User Defined `ExecutionPlan` nodes + +[user guide]: ../user-guide/example-usage.md +[contributor guide]: ../contributor-guide/index.md +[docs]: https://docs.rs/datafusion/latest/datafusion/#architecture diff --git a/versions/55.0.0/_sources/library-user-guide/profiling.md.txt b/versions/55.0.0/_sources/library-user-guide/profiling.md.txt new file mode 100644 index 0000000000000..a2ea6723e55a7 --- /dev/null +++ b/versions/55.0.0/_sources/library-user-guide/profiling.md.txt @@ -0,0 +1,124 @@ + + +# Profiling Cookbook + +The section contains examples how to perform CPU profiling for Apache DataFusion on different operating systems. + +## Building a flame graph + +[Video: how to CPU profile DataFusion with a Flamegraph](https://youtu.be/2z11xtYw_xs) + +A flamegraph is a visual representation of which functions are being run +You can create flamegraphs in many ways; The instructions below are for +[cargo-flamegraph](https://github.com/flamegraph-rs/flamegraph) which results +in images such as this: + +![Flamegraph](../_static/images/flamegraph.svg) + +## MacOS + +### Step 1: Install the flamegraph Tool + +To install flamegraph, run: + +```shell +cargo install flamegraph +``` + +### Step 2: Prepare Your Environment + +Ensure that you're in the directory containing the necessary data files for your DataFusion query. The flamegraph tool will profile the execution of your query against this data. + +### Step 3: Running the Flamegraph Tool + +To generate a flamegraph, you'll need to use the `--` separator to pass arguments to the binary you're profiling. For datafusion-cli, you need to make sure to run the command with sudo permissions (especially on macOS, where DTrace requires elevated privileges). + +Here is a general example: + +```shell +sudo flamegraph -- datafusion-cli -f +``` + +#### Example: Generating a Flamegraph for a Specific Query + +Here is an example using `28.sql`: + +```shell +sudo flamegraph -- datafusion-cli -f 28.sql +``` + +You can also invoke the flamegraph tool with `cargo` to profile a specific test or benchmark. + +#### Example: Flamegraph for a specific test: + +```bash +CARGO_PROFILE_RELEASE_DEBUG=true cargo flamegraph --root --unit-test datafusion -- dataframe::tests::test_array_agg +``` + +#### Example: Flamegraph for a benchmark + +```bash +CARGO_PROFILE_RELEASE_DEBUG=true cargo flamegraph --root --bench sql_planner -- --bench +``` + +### CPU profiling with XCode Instruments + +[Video: how to CPU profile DataFusion with XCode Instruments](https://youtu.be/P3dXH61Kr5U) + +## Profiling using Samply cross platform profiler + +There is an opportunity to build flamegraphs, call trees and stack charts on any platform using +[Samply](https://github.com/mstange/samply) + +Install Samply profiler + +```shell +cargo install --locked samply +``` + +More Samply [installation options](https://github.com/mstange/samply?tab=readme-ov-file#installation) + +Run the profiler + +```shell +samply record --profile profiling ./my-application my-arguments +``` + +### Profile the benchmark + +[Set up benchmarks](https://github.com/apache/datafusion/blob/main/benchmarks/README.md#running-the-benchmarks) if not yet done + +Example: Profile Q22 query from TPC-H benchmark. +Note: `--profile profiling` to profile release optimized artifact with debug symbols + +```shell +cargo build --profile profiling --bin tpch +samply record ./target/profiling/tpch benchmark datafusion --iterations 5 --path datafusion/benchmarks/data/tpch_sf10 --prefer_hash_join true --format parquet -o datafusion/benchmarks/results/dev2/tpch_sf10.json --query 22 +``` + +After sampling has completed the Samply starts a local server and navigates to the profiler + +```shell +Local server listening at http://127.0.0.1:3000 +``` + +![img.png](samply_profiler.png) + +Note: The Firefox profiler cannot be opened in Safari, please use Chrome or Firefox instead diff --git a/versions/55.0.0/_sources/library-user-guide/query-optimizer.md.txt b/versions/55.0.0/_sources/library-user-guide/query-optimizer.md.txt new file mode 100644 index 0000000000000..a3853d39ddced --- /dev/null +++ b/versions/55.0.0/_sources/library-user-guide/query-optimizer.md.txt @@ -0,0 +1,522 @@ + + +# Query Optimizer + +[DataFusion][df] is an extensible query execution framework, written in Rust, that uses Apache Arrow as its in-memory +format. + +DataFusion has modular design, allowing individual crates to be re-used in other projects. + +This crate is a submodule of DataFusion that provides a query optimizer for logical plans, and +contains an extensive set of [`OptimizerRule`]s and [`PhysicalOptimizerRule`]s that may rewrite the plan and/or its expressions so +they execute more quickly while still computing the same result. + +For a reference list of the built-in analyzer, logical optimizer, and physical optimizer rules, +see [Optimizer Rule Reference]. + +For a deeper background on optimizer architecture and rule types and predicates, see +[Optimizing SQL (and DataFrames) in DataFusion, Part 1], [Part 2], +[Using Ordering for Better Plans in Apache DataFusion], and +[Dynamic Filters: Passing Information Between Operators During Execution for 25x Faster Queries]. + +[`optimizerrule`]: https://docs.rs/datafusion/latest/datafusion/optimizer/trait.OptimizerRule.html +[`physicaloptimizerrule`]: https://docs.rs/datafusion/latest/datafusion/physical_optimizer/trait.PhysicalOptimizerRule.html +[optimizing sql (and dataframes) in datafusion, part 1]: https://datafusion.apache.org/blog/2025/06/15/optimizing-sql-dataframes-part-one +[part 2]: https://datafusion.apache.org/blog/2025/06/15/optimizing-sql-dataframes-part-two +[using ordering for better plans in apache datafusion]: https://datafusion.apache.org/blog/2025/03/11/ordering-analysis +[dynamic filters: passing information between operators during execution for 25x faster queries]: https://datafusion.apache.org/blog/2025/09/10/dynamic-filters +[optimizer rule reference]: https://docs.rs/datafusion/latest/datafusion/index.html#built-in-optimizer-rules +[`logicalplan`]: https://docs.rs/datafusion/latest/datafusion/logical_expr/enum.LogicalPlan.html + +## Running the Optimizer + +The following code demonstrates the basic flow of creating the optimizer with a default set of optimization rules +and applying it to a logical plan to produce an optimized logical plan. + +```rust + +use std::sync::Arc; +use datafusion::logical_expr::{col, lit, LogicalPlan, LogicalPlanBuilder}; +use datafusion::optimizer::{OptimizerRule, OptimizerContext, Optimizer}; + +// We need a logical plan as the starting point. There are many ways to build a logical plan: +// +// The `datafusion-expr` crate provides a LogicalPlanBuilder +// The `datafusion-sql` crate provides a SQL query planner that can create a LogicalPlan from SQL +// The `datafusion` crate provides a DataFrame API that can create a LogicalPlan + +let initial_logical_plan = LogicalPlanBuilder::empty(false).build().unwrap(); + +// use builtin rules or customized rules +let rules: Vec> = vec![]; + +let optimizer = Optimizer::with_rules(rules); + +let config = OptimizerContext::new().with_max_passes(16); + +let optimized_plan = optimizer.optimize(initial_logical_plan.clone(), &config, observer); + +fn observer(plan: &LogicalPlan, rule: &dyn OptimizerRule) { + println!( + "After applying rule '{}':\n{}", + rule.name(), + plan.display_indent() + ) +} +``` + +## Writing Optimization Rules + +Please refer to the +[optimizer_rule.rs](https://github.com/apache/datafusion/blob/main/datafusion-examples/examples/query_planning/optimizer_rule.rs) +example to learn more about the general approach to writing optimizer rules and +then move onto studying the existing rules. + +`OptimizerRule` transforms one [`LogicalPlan`] into another which +computes the same results, but in a potentially more efficient +way. If there are no suitable transformations for the input plan, +the optimizer can simply return it as is. + +All rules must implement the `OptimizerRule` trait. + +```rust +# use datafusion::common::tree_node::Transformed; +# use datafusion::common::Result; +# use datafusion::logical_expr::LogicalPlan; +# use datafusion::optimizer::{OptimizerConfig, OptimizerRule}; +# + +#[derive(Default, Debug)] +struct MyOptimizerRule {} + +impl OptimizerRule for MyOptimizerRule { + fn name(&self) -> &str { + "my_optimizer_rule" + } + + fn rewrite( + &self, + plan: LogicalPlan, + _config: &dyn OptimizerConfig, + ) -> Result> { + unimplemented!() + } +} +``` + +## Providing Custom Rules + +The optimizer can be created with a custom set of rules. + +```rust +# use std::sync::Arc; +# use datafusion::logical_expr::{col, lit, LogicalPlan, LogicalPlanBuilder}; +# use datafusion::optimizer::{OptimizerRule, OptimizerConfig, OptimizerContext, Optimizer}; +# use datafusion::common::tree_node::Transformed; +# use datafusion::common::Result; +# +# #[derive(Default, Debug)] +# struct MyOptimizerRule {} +# +# impl OptimizerRule for MyOptimizerRule { +# fn name(&self) -> &str { +# "my_optimizer_rule" +# } +# +# fn rewrite( +# &self, +# plan: LogicalPlan, +# _config: &dyn OptimizerConfig, +# ) -> Result> { +# unimplemented!() +# } +# } + +let optimizer = Optimizer::with_rules(vec![ + Arc::new(MyOptimizerRule {}) +]); +``` + +### General Guidelines + +Rules typical walk the logical plan and walk the expression trees inside operators and selectively mutate +individual operators or expressions. + +Sometimes there is an initial pass that visits the plan and builds state that is used in a second pass that performs +the actual optimization. This approach is used in projection push down and filter push down. + +### Expression Naming + +Every expression in DataFusion has a name, which is used as the column name. For example, in this example the output +contains a single column with the name `"COUNT(aggregate_test_100.c9)"`: + +```text +> select count(c9) from aggregate_test_100; ++------------------------------+ +| COUNT(aggregate_test_100.c9) | ++------------------------------+ +| 100 | ++------------------------------+ +``` + +These names are used to refer to the columns in both subqueries as well as internally from one stage of the LogicalPlan +to another. For example: + +```text +> select "COUNT(aggregate_test_100.c9)" + 1 from (select count(c9) from aggregate_test_100) as sq; ++--------------------------------------------+ +| sq.COUNT(aggregate_test_100.c9) + Int64(1) | ++--------------------------------------------+ +| 101 | ++--------------------------------------------+ +``` + +### Implication + +Because DataFusion identifies columns using a string name, it means it is critical that the names of expressions are +not changed by the optimizer when it rewrites expressions. This is typically accomplished by renaming a rewritten +expression by adding an alias. + +Here is a simple example of such a rewrite. The expression `1 + 2` can be internally simplified to 3 but must still be +displayed the same as `1 + 2`: + +```text +> select 1 + 2; ++---------------------+ +| Int64(1) + Int64(2) | ++---------------------+ +| 3 | ++---------------------+ +``` + +Looking at the `EXPLAIN` output we can see that the optimizer has effectively rewritten `1 + 2` into effectively +`3 as "1 + 2"`: + +```text +> explain format indent select 1 + 2; ++---------------+-------------------------------------------------+ +| plan_type | plan | ++---------------+-------------------------------------------------+ +| logical_plan | Projection: Int64(3) AS Int64(1) + Int64(2) | +| | EmptyRelation | +| physical_plan | ProjectionExec: expr=[3 as Int64(1) + Int64(2)] | +| | PlaceholderRowExec | +| | | ++---------------+-------------------------------------------------+ +``` + +If the expression name is not preserved, bugs such as [#3704](https://github.com/apache/datafusion/issues/3704) +and [#3555](https://github.com/apache/datafusion/issues/3555) occur where the expected columns can not be found. + +### Building Expression Names + +There are currently two ways to create a name for an expression in the logical plan. + +```rust +# use datafusion::common::Result; +# struct Expr; + +impl Expr { + /// Returns the name of this expression as it should appear in a schema. This name + /// will not include any CAST expressions. + pub fn display_name(&self) -> Result { + Ok("display_name".to_string()) + } + + /// Returns a full and complete string representation of this expression. + pub fn canonical_name(&self) -> String { + "canonical_name".to_string() + } +} +``` + +When comparing expressions to determine if they are equivalent, `canonical_name` should be used, and when creating a +name to be used in a schema, `display_name` should be used. + +### Utilities + +There are a number of [utility methods][util] provided that take care of some common tasks. + +[util]: https://github.com/apache/datafusion/blob/main/datafusion/expr/src/utils.rs + +### Recursively walk an expression tree + +The [TreeNode API] provides a convenient way to recursively walk an expression or plan tree. + +For example, to find all subquery references in a logical plan, the following code can be used: + +```rust +# use datafusion::prelude::*; +# use datafusion::common::tree_node::{TreeNode, TreeNodeRecursion}; +# use datafusion::common::Result; +// Return all subquery references in an expression +fn extract_subquery_filters(expression: &Expr) -> Result> { + let mut extracted = vec![]; + expression.apply(|expr| { + if let Expr::InSubquery(_) = expr { + extracted.push(expr); + } + Ok(TreeNodeRecursion::Continue) + })?; + Ok(extracted) +} +``` + +Likewise you can use the [TreeNode API] to rewrite a `LogicalPlan` or `ExecutionPlan` + +```rust +# use datafusion::prelude::*; +# use datafusion::logical_expr::{LogicalPlan, Join}; +# use datafusion::common::tree_node::{TreeNode, TreeNodeRecursion}; +# use datafusion::common::Result; +// Return all joins in a logical plan +fn find_joins(overall_plan: &LogicalPlan) -> Result> { + let mut extracted = vec![]; + overall_plan.apply(|plan| { + if let LogicalPlan::Join(join) = plan { + extracted.push(join); + } + Ok(TreeNodeRecursion::Continue) + })?; + Ok(extracted) +} +``` + +### Rewriting expressions + +The [TreeNode API] also provides a convenient way to rewrite expressions and +plans as well. For example to rewrite all expressions like + +```sql +col BETWEEN x AND y +``` + +into + +```sql +col >= x AND col <= y +``` + +you can use the following code: + +```rust +# use datafusion::prelude::*; +# use datafusion::logical_expr::{Between}; +# use datafusion::logical_expr::expr_fn::*; +# use datafusion::common::tree_node::{Transformed, TreeNode, TreeNodeRecursion}; +# use datafusion::common::Result; +// Recursively rewrite all BETWEEN expressions +// returns Transformed::yes if any changes were made +fn rewrite_between(expr: Expr) -> Result> { + // transform_up does a bottom up rewrite + expr.transform_up(|expr| { + // only handle BETWEEN expressions + let Expr::Between(Between { + negated, + expr, + low, + high, + }) = expr else { + return Ok(Transformed::no(expr)) + }; + let rewritten_expr = if negated { + // don't rewrite NOT BETWEEN + Expr::Between(Between::new(expr, negated, low, high)) + } else { + // rewrite to (expr >= low) AND (expr <= high) + expr.clone().gt_eq(*low).and(expr.lt_eq(*high)) + }; + Ok(Transformed::yes(rewritten_expr)) + }) +} +``` + +### Writing Tests + +There should be unit tests in the same file as the new rule that test the effect of the rule being applied to a plan +in isolation (without any other rule being applied). + +There should also be a test in `integration-tests.rs` that tests the rule as part of the overall optimization process. + +### Debugging + +The `EXPLAIN VERBOSE` command can be used to show the effect of each optimization rule on a query. + +In the following example, the `type_coercion` and `simplify_expressions` passes have simplified the plan so that it returns the constant `"3.2"` rather than doing a computation at execution time. + +```text +> explain verbose select cast(1 + 2.2 as string) as foo; ++------------------------------------------------------------+---------------------------------------------------------------------------+ +| plan_type | plan | ++------------------------------------------------------------+---------------------------------------------------------------------------+ +| initial_logical_plan | Projection: CAST(Int64(1) + Float64(2.2) AS Utf8) AS foo | +| | EmptyRelation | +| logical_plan after type_coercion | Projection: CAST(CAST(Int64(1) AS Float64) + Float64(2.2) AS Utf8) AS foo | +| | EmptyRelation | +| logical_plan after simplify_expressions | Projection: Utf8("3.2") AS foo | +| | EmptyRelation | +| logical_plan after unwrap_cast_in_comparison | SAME TEXT AS ABOVE | +| logical_plan after decorrelate_where_exists | SAME TEXT AS ABOVE | +| logical_plan after decorrelate_where_in | SAME TEXT AS ABOVE | +| logical_plan after scalar_subquery_to_join | SAME TEXT AS ABOVE | +| logical_plan after subquery_filter_to_join | SAME TEXT AS ABOVE | +| logical_plan after simplify_expressions | SAME TEXT AS ABOVE | +| logical_plan after eliminate_filter | SAME TEXT AS ABOVE | +| logical_plan after reduce_cross_join | SAME TEXT AS ABOVE | +| logical_plan after common_sub_expression_eliminate | SAME TEXT AS ABOVE | +| logical_plan after eliminate_limit | SAME TEXT AS ABOVE | +| logical_plan after projection_push_down | SAME TEXT AS ABOVE | +| logical_plan after rewrite_disjunctive_predicate | SAME TEXT AS ABOVE | +| logical_plan after reduce_outer_join | SAME TEXT AS ABOVE | +| logical_plan after filter_push_down | SAME TEXT AS ABOVE | +| logical_plan after limit_push_down | SAME TEXT AS ABOVE | +| logical_plan after single_distinct_aggregation_to_group_by | SAME TEXT AS ABOVE | +| logical_plan | Projection: Utf8("3.2") AS foo | +| | EmptyRelation | +| initial_physical_plan | ProjectionExec: expr=[3.2 as foo] | +| | PlaceholderRowExec | +| | | +| physical_plan after aggregate_statistics | SAME TEXT AS ABOVE | +| physical_plan after join_selection | SAME TEXT AS ABOVE | +| physical_plan after coalesce_batches | SAME TEXT AS ABOVE | +| physical_plan after repartition | SAME TEXT AS ABOVE | +| physical_plan after add_merge_exec | SAME TEXT AS ABOVE | +| physical_plan | ProjectionExec: expr=[3.2 as foo] | +| | PlaceholderRowExec | +| | | ++------------------------------------------------------------+---------------------------------------------------------------------------+ +``` + +[df]: https://crates.io/crates/datafusion + +## Thinking about Query Optimization + +Query optimization in DataFusion uses a cost based model. The cost based model +relies on table and column level statistics to estimate selectivity; selectivity +estimates are an important piece in cost analysis for filters and projections +as they allow estimating the cost of joins and filters. + +An important piece of building these estimates is _boundary analysis_ which uses +interval arithmetic to take an expression such as `a > 2500 AND a <= 5000` and +build an accurate selectivity estimate that can then be used to find more efficient +plans. + +### `AnalysisContext` API + +The `AnalysisContext` serves as a shared knowledge base during expression evaluation +and boundary analysis. Think of it as a dynamic repository that maintains information about: + +1. Current known boundaries for columns and expressions +2. Statistics that have been gathered or inferred +3. A mutable state that can be updated as analysis progresses + +What makes `AnalysisContext` particularly powerful is its ability to propagate information +through the expression tree. As each node in the expression tree is analyzed, it can both +read from and write to this shared context, allowing for sophisticated boundary analysis and inference. + +### `ColumnStatistics` for Cardinality Estimation + +Column statistics form the foundation of optimization decisions. Rather than just tracking +simple metrics, DataFusion's `ColumnStatistics` provides a rich set of information including: + +- Null value counts +- Maximum and minimum values +- Value sums (for numeric columns) +- Distinct value counts + +Each of these statistics is wrapped in a `Precision` type that indicates whether the value is +exact or estimated, allowing the optimizer to make informed decisions about the reliability +of its cardinality estimates. + +### Boundary Analysis Flow + +The boundary analysis process flows through several stages, with each stage building +upon the information gathered in previous stages. The `AnalysisContext` is continuously +updated as the analysis progresses through the expression tree. + +#### Expression Boundary Analysis + +When analyzing expressions, DataFusion runs boundary analysis using interval arithmetic. +Consider a simple predicate like age > 18 AND age <= 25. The analysis flows as follows: + +1. Context Initialization + + - Begin with known column statistics + - Set up initial boundaries based on column constraints + - Initialize the shared analysis context + +2. Expression Tree Walk + + - Analyze each node in the expression tree + - Propagate boundary information upward + - Allow child nodes to influence parent boundaries + +3. Boundary Updates + - Each expression can update the shared context + - Changes flow through the entire expression tree + - Final boundaries inform optimization decisions + +### Working with the analysis API + +The following example shows how you can run an analysis pass on a physical expression +to infer the selectivity of the expression and the space of possible values it can +take. + +```rust +# use std::sync::Arc; +# use datafusion::prelude::*; +# use datafusion::physical_expr::{analyze, AnalysisContext, ExprBoundaries}; +# use datafusion::arrow::datatypes::{DataType, Field, Schema, TimeUnit}; +# use datafusion::common::stats::Precision; +# +# use datafusion::common::{ColumnStatistics, DFSchema}; +# use datafusion::common::{ScalarValue, ToDFSchema}; +# use datafusion::error::Result; +fn analyze_filter_example() -> Result<()> { + // Create a schema with an 'age' column + let age = Field::new("age", DataType::Int64, false); + let schema = Arc::new(Schema::new(vec![age])); + + // Define column statistics + let column_stats = ColumnStatistics::default() + .with_min_value(Precision::Exact(ScalarValue::Int64(Some(14)))) + .with_max_value(Precision::Exact(ScalarValue::Int64(Some(79)))) + .with_null_count(Precision::Exact(0)); + + // Create expression: age > 18 AND age <= 25 + let expr = col("age") + .gt(lit(18i64)) + .and(col("age").lt_eq(lit(25i64))); + + // Initialize analysis context + let initial_boundaries = vec![ExprBoundaries::try_from_column( + &schema, &column_stats, 0)?]; + let context = AnalysisContext::new(initial_boundaries); + + // Analyze expression + let df_schema = DFSchema::try_from(schema)?; + let physical_expr = SessionContext::new().create_physical_expr(expr, &df_schema)?; + let analysis = analyze(&physical_expr, context, df_schema.as_ref())?; + + Ok(()) +} +``` + +[treenode api]: https://docs.rs/datafusion/latest/datafusion/common/tree_node/trait.TreeNode.html diff --git a/versions/55.0.0/_sources/library-user-guide/table-constraints.md.txt b/versions/55.0.0/_sources/library-user-guide/table-constraints.md.txt new file mode 100644 index 0000000000000..252817822d990 --- /dev/null +++ b/versions/55.0.0/_sources/library-user-guide/table-constraints.md.txt @@ -0,0 +1,42 @@ + + +# Table Constraint Enforcement + +Table providers can describe table constraints using the +[`TableConstraint`] and [`Constraints`] APIs. These constraints include +primary keys, unique keys, foreign keys and check constraints. + +DataFusion does **not** currently enforce these constraints at runtime. +They are provided for informational purposes and can be used by custom +`TableProvider` implementations or other parts of the system. + +- **Nullability**: The only property enforced by DataFusion is the + nullability of each [`Field`] in a schema. Returning data with null values + for Columns marked as not nullable will result in runtime errors during execution. DataFusion + does not check or enforce nullability when data is ingested. +- **Primary and unique keys**: DataFusion does not verify that the data + satisfies primary or unique key constraints. Table providers that + require this behaviour must implement their own checks. +- **Foreign keys and check constraints**: These constraints are parsed + but are not validated or used during query planning. + +[`tableconstraint`]: https://docs.rs/datafusion/latest/datafusion/logical_expr/sqlparser/ast/enum.TableConstraint.html +[`constraints`]: https://docs.rs/datafusion/latest/datafusion/common/struct.Constraints.html +[`field`]: https://docs.rs/arrow/latest/arrow/datatypes/struct.Field.html diff --git a/versions/55.0.0/_sources/library-user-guide/upgrading/46.0.0.md.txt b/versions/55.0.0/_sources/library-user-guide/upgrading/46.0.0.md.txt new file mode 100644 index 0000000000000..e38d18c3d6609 --- /dev/null +++ b/versions/55.0.0/_sources/library-user-guide/upgrading/46.0.0.md.txt @@ -0,0 +1,310 @@ + + +# Upgrade Guides + +## DataFusion 46.0.0 + +### Use `invoke_with_args` instead of `invoke()` and `invoke_batch()` + +DataFusion is moving to a consistent API for invoking ScalarUDFs, +[`ScalarUDFImpl::invoke_with_args()`], and deprecating +[`ScalarUDFImpl::invoke()`], [`ScalarUDFImpl::invoke_batch()`], and [`ScalarUDFImpl::invoke_no_args()`] + +If you see errors such as the following it means the older APIs are being used: + +```text +This feature is not implemented: Function concat does not implement invoke but called +``` + +To fix this error, use [`ScalarUDFImpl::invoke_with_args()`] instead, as shown +below. See [PR 14876] for an example. + +Given existing code like this: + +```rust +# /* comment to avoid running +impl ScalarUDFImpl for SparkConcat { +... + fn invoke_batch(&self, args: &[ColumnarValue], number_rows: usize) -> Result { + if args + .iter() + .any(|arg| matches!(arg.data_type(), DataType::List(_))) + { + ArrayConcat::new().invoke_batch(args, number_rows) + } else { + ConcatFunc::new().invoke_batch(args, number_rows) + } + } +} +# */ +``` + +To + +```rust +# /* comment to avoid running +impl ScalarUDFImpl for SparkConcat { + ... + fn invoke_with_args(&self, args: ScalarFunctionArgs) -> Result { + if args + .args + .iter() + .any(|arg| matches!(arg.data_type(), DataType::List(_))) + { + ArrayConcat::new().invoke_with_args(args) + } else { + ConcatFunc::new().invoke_with_args(args) + } + } +} + # */ +``` + +[`scalarudfimpl::invoke()`]: https://docs.rs/datafusion/latest/datafusion/logical_expr/trait.ScalarUDFImpl.html#method.invoke +[`scalarudfimpl::invoke_batch()`]: https://docs.rs/datafusion/latest/datafusion/logical_expr/trait.ScalarUDFImpl.html#method.invoke_batch +[`scalarudfimpl::invoke_no_args()`]: https://docs.rs/datafusion/latest/datafusion/logical_expr/trait.ScalarUDFImpl.html#method.invoke_no_args +[`scalarudfimpl::invoke_with_args()`]: https://docs.rs/datafusion/latest/datafusion/logical_expr/trait.ScalarUDFImpl.html#method.invoke_with_args +[pr 14876]: https://github.com/apache/datafusion/pull/14876 + +### `ParquetExec`, `AvroExec`, `CsvExec`, `JsonExec` deprecated + +DataFusion 46 has a major change to how the built in DataSources are organized. +Instead of individual `ExecutionPlan`s for the different file formats they now +all use `DataSourceExec` and the format specific information is embodied in new +traits `DataSource` and `FileSource`. + +Here is more information about + +- [Design Ticket] +- Change PR [PR #14224] +- Example of an Upgrade [PR in delta-rs] + +[design ticket]: https://github.com/apache/datafusion/issues/13838 +[pr #14224]: https://github.com/apache/datafusion/pull/14224 +[pr in delta-rs]: https://github.com/delta-io/delta-rs/pull/3261 + +### Cookbook: Changes to `ParquetExecBuilder` + +Code that looks for `ParquetExec` like this will no longer work: + +```rust +# /* comment to avoid running + if let Some(parquet_exec) = plan.as_any().downcast_ref::() { + // Do something with ParquetExec here + } +# */ +``` + +Instead, with `DataSourceExec`, the same information is now on `FileScanConfig` and +`ParquetSource`. The equivalent code is + +```rust +# /* comment to avoid running +if let Some(datasource_exec) = plan.as_any().downcast_ref::() { + if let Some(scan_config) = datasource_exec.data_source().as_any().downcast_ref::() { + // FileGroups, and other information is on the FileScanConfig + // parquet + if let Some(parquet_source) = scan_config.file_source.as_any().downcast_ref::() + { + // Information on PruningPredicates and parquet options are here + } +} +# */ +``` + +### Cookbook: Changes to `ParquetExecBuilder` + +Likewise code that builds `ParquetExec` using the `ParquetExecBuilder` such as +the following must be changed: + +```rust +# /* comment to avoid running +let mut exec_plan_builder = ParquetExecBuilder::new( + FileScanConfig::new(self.log_store.object_store_url(), file_schema) + .with_projection(self.projection.cloned()) + .with_limit(self.limit) + .with_table_partition_cols(table_partition_cols), +) +.with_schema_adapter_factory(Arc::new(DeltaSchemaAdapterFactory {})) +.with_table_parquet_options(parquet_options); + +// Add filter +if let Some(predicate) = logical_filter { + if config.enable_parquet_pushdown { + exec_plan_builder = exec_plan_builder.with_predicate(predicate); + } +}; +# */ +``` + +New code should use `FileScanConfig` to build the appropriate `DataSourceExec`: + +```rust +# /* comment to avoid running +let mut file_source = ParquetSource::new(parquet_options) + .with_schema_adapter_factory(Arc::new(DeltaSchemaAdapterFactory {})); + +// Add filter +if let Some(predicate) = logical_filter { + if config.enable_parquet_pushdown { + file_source = file_source.with_predicate(predicate); + } +}; + +let file_scan_config = FileScanConfig::new( + self.log_store.object_store_url(), + file_schema, + Arc::new(file_source), +) +.with_statistics(stats) +.with_projection(self.projection.cloned()) +.with_limit(self.limit) +.with_table_partition_cols(table_partition_cols); + +// Build the actual scan like this +parquet_scan: file_scan_config.build(), +# */ +``` + +### `datafusion-cli` no longer automatically unescapes strings + +`datafusion-cli` previously would incorrectly unescape string literals (see [ticket] for more details). + +To escape `'` in SQL literals, use `''`: + +```sql +> select 'it''s escaped'; ++----------------------+ +| Utf8("it's escaped") | ++----------------------+ +| it's escaped | ++----------------------+ +1 row(s) fetched. +``` + +To include special characters (such as newlines via `\n`) you can use an `E` literal string. For example + +```sql +> select 'foo\nbar'; ++------------------+ +| Utf8("foo\nbar") | ++------------------+ +| foo\nbar | ++------------------+ +1 row(s) fetched. +Elapsed 0.005 seconds. +``` + +### Changes to array scalar function signatures + +DataFusion 46 has changed the way scalar array function signatures are +declared. Previously, functions needed to select from a list of predefined +signatures within the `ArrayFunctionSignature` enum. Now the signatures +can be defined via a `Vec` of pseudo-types, which each correspond to a +single argument. Those pseudo-types are the variants of the +`ArrayFunctionArgument` enum and are as follows: + +- `Array`: An argument of type List/LargeList/FixedSizeList. All Array + arguments must be coercible to the same type. +- `Element`: An argument that is coercible to the inner type of the `Array` + arguments. +- `Index`: An `Int64` argument. + +Each of the old variants can be converted to the new format as follows: + +`TypeSignature::ArraySignature(ArrayFunctionSignature::ArrayAndElement)`: + +```rust +# use datafusion::common::utils::ListCoercion; +# use datafusion_expr_common::signature::{ArrayFunctionArgument, ArrayFunctionSignature, TypeSignature}; + +TypeSignature::ArraySignature(ArrayFunctionSignature::Array { + arguments: vec![ArrayFunctionArgument::Array, ArrayFunctionArgument::Element], + array_coercion: Some(ListCoercion::FixedSizedListToList), +}); +``` + +`TypeSignature::ArraySignature(ArrayFunctionSignature::ElementAndArray)`: + +```rust +# use datafusion::common::utils::ListCoercion; +# use datafusion_expr_common::signature::{ArrayFunctionArgument, ArrayFunctionSignature, TypeSignature}; + +TypeSignature::ArraySignature(ArrayFunctionSignature::Array { + arguments: vec![ArrayFunctionArgument::Element, ArrayFunctionArgument::Array], + array_coercion: Some(ListCoercion::FixedSizedListToList), +}); +``` + +`TypeSignature::ArraySignature(ArrayFunctionSignature::ArrayAndIndex)`: + +```rust +# use datafusion::common::utils::ListCoercion; +# use datafusion_expr_common::signature::{ArrayFunctionArgument, ArrayFunctionSignature, TypeSignature}; + +TypeSignature::ArraySignature(ArrayFunctionSignature::Array { + arguments: vec![ArrayFunctionArgument::Array, ArrayFunctionArgument::Index], + array_coercion: None, +}); +``` + +`TypeSignature::ArraySignature(ArrayFunctionSignature::ArrayAndElementAndOptionalIndex)`: + +```rust +# use datafusion::common::utils::ListCoercion; +# use datafusion_expr_common::signature::{ArrayFunctionArgument, ArrayFunctionSignature, TypeSignature}; + +TypeSignature::OneOf(vec![ + TypeSignature::ArraySignature(ArrayFunctionSignature::Array { + arguments: vec![ArrayFunctionArgument::Array, ArrayFunctionArgument::Element], + array_coercion: None, + }), + TypeSignature::ArraySignature(ArrayFunctionSignature::Array { + arguments: vec![ + ArrayFunctionArgument::Array, + ArrayFunctionArgument::Element, + ArrayFunctionArgument::Index, + ], + array_coercion: None, + }), +]); +``` + +`TypeSignature::ArraySignature(ArrayFunctionSignature::Array)`: + +```rust +# use datafusion::common::utils::ListCoercion; +# use datafusion_expr_common::signature::{ArrayFunctionArgument, ArrayFunctionSignature, TypeSignature}; + +TypeSignature::ArraySignature(ArrayFunctionSignature::Array { + arguments: vec![ArrayFunctionArgument::Array], + array_coercion: None, +}); +``` + +Alternatively, you can switch to using one of the following functions which +take care of constructing the `TypeSignature` for you: + +- `Signature::array_and_element` +- `Signature::array_and_element_and_optional_index` +- `Signature::array_and_index` +- `Signature::array` + +[ticket]: https://github.com/apache/datafusion/issues/13286 diff --git a/versions/55.0.0/_sources/library-user-guide/upgrading/47.0.0.md.txt b/versions/55.0.0/_sources/library-user-guide/upgrading/47.0.0.md.txt new file mode 100644 index 0000000000000..354b6740df02f --- /dev/null +++ b/versions/55.0.0/_sources/library-user-guide/upgrading/47.0.0.md.txt @@ -0,0 +1,135 @@ + + +# Upgrade Guides + +## DataFusion 47.0.0 + +This section calls out some of the major changes in the `47.0.0` release of DataFusion. + +Here are some example upgrade PRs that demonstrate changes required when upgrading from DataFusion 46.0.0: + +- [delta-rs Upgrade to `47.0.0`](https://github.com/delta-io/delta-rs/pull/3378) +- [DataFusion Comet Upgrade to `47.0.0`](https://github.com/apache/datafusion-comet/pull/1563) +- [Sail Upgrade to `47.0.0`](https://github.com/lakehq/sail/pull/434) + +### Upgrades to `arrow-rs` and `arrow-parquet` 55.0.0 and `object_store` 0.12.0 + +Several APIs are changed in the underlying arrow and parquet libraries to use a +`u64` instead of `usize` to better support WASM (See [#7371] and [#6961]) + +Additionally `ObjectStore::list` and `ObjectStore::list_with_offset` have been changed to return `static` lifetimes (See [#6619]) + +[#6619]: https://github.com/apache/arrow-rs/pull/6619 +[#7371]: https://github.com/apache/arrow-rs/pull/7371 + +This requires converting from `usize` to `u64` occasionally as well as changes to `ObjectStore` implementations such as + +```rust +# /* comment to avoid running +impl Objectstore { + ... + // The range is now a u64 instead of usize + async fn get_range(&self, location: &Path, range: Range) -> ObjectStoreResult { + self.inner.get_range(location, range).await + } + ... + // the lifetime is now 'static instead of `_ (meaning the captured closure can't contain references) + // (this also applies to list_with_offset) + fn list(&self, prefix: Option<&Path>) -> BoxStream<'static, ObjectStoreResult> { + self.inner.list(prefix) + } +} +# */ +``` + +The `ParquetObjectReader` has been updated to no longer require the object size +(it can be fetched using a single suffix request). See [#7334] for details + +[#7334]: https://github.com/apache/arrow-rs/pull/7334 + +Pattern in DataFusion `46.0.0`: + +```rust +# /* comment to avoid running +let meta: ObjectMeta = ...; +let reader = ParquetObjectReader::new(store, meta); +# */ +``` + +Pattern in DataFusion `47.0.0`: + +```rust +# /* comment to avoid running +let meta: ObjectMeta = ...; +let reader = ParquetObjectReader::new(store, location) + .with_file_size(meta.size); +# */ +``` + +### `DisplayFormatType::TreeRender` + +DataFusion now supports [`tree` style explain plans]. Implementations of +`Executionplan` must also provide a description in the +`DisplayFormatType::TreeRender` format. This can be the same as the existing +`DisplayFormatType::Default`. + +[`tree` style explain plans]: https://datafusion.apache.org/user-guide/sql/explain.html#tree-format-default + +### Removed Deprecated APIs + +Several APIs have been removed in this release. These were either deprecated +previously or were hard to use correctly such as the multiple different +`ScalarUDFImpl::invoke*` APIs. See [#15130], [#15123], and [#15027] for more +details. + +[#15130]: https://github.com/apache/datafusion/pull/15130 +[#15123]: https://github.com/apache/datafusion/pull/15123 +[#15027]: https://github.com/apache/datafusion/pull/15027 + +### `FileScanConfig` --> `FileScanConfigBuilder` + +Previously, `FileScanConfig::build()` directly created ExecutionPlans. In +DataFusion 47.0.0 this has been changed to use `FileScanConfigBuilder`. See +[#15352] for details. + +[#15352]: https://github.com/apache/datafusion/pull/15352 + +Pattern in DataFusion `46.0.0`: + +```rust +# /* comment to avoid running +let plan = FileScanConfig::new(url, schema, Arc::new(file_source)) + .with_statistics(stats) + ... + .build() +# */ +``` + +Pattern in DataFusion `47.0.0`: + +```rust +# /* comment to avoid running +let config = FileScanConfigBuilder::new(url, Arc::new(file_source)) + .with_statistics(stats) + ... + .build(); +let scan = DataSourceExec::from_data_source(config); +# */ +``` diff --git a/versions/55.0.0/_sources/library-user-guide/upgrading/48.0.0.md.txt b/versions/55.0.0/_sources/library-user-guide/upgrading/48.0.0.md.txt new file mode 100644 index 0000000000000..7872a6f54f245 --- /dev/null +++ b/versions/55.0.0/_sources/library-user-guide/upgrading/48.0.0.md.txt @@ -0,0 +1,244 @@ + + +# Upgrade Guides + +## DataFusion 48.0.0 + +### `Expr::Literal` has optional metadata + +The [`Expr::Literal`] variant now includes optional metadata, which allows for +carrying through Arrow field metadata to support extension types and other uses. + +This means code such as + +```rust +# /* comment to avoid running +match expr { +... + Expr::Literal(scalar) => ... +... +} +# */ +``` + +Should be updated to: + +```rust +# /* comment to avoid running +match expr { +... + Expr::Literal(scalar, _metadata) => ... +... +} +# */ +``` + +Likewise constructing `Expr::Literal` requires metadata as well. The [`lit`] function +has not changed and returns an `Expr::Literal` with no metadata. + +[`expr::literal`]: https://docs.rs/datafusion/latest/datafusion/logical_expr/enum.Expr.html#variant.Literal +[`lit`]: https://docs.rs/datafusion/latest/datafusion/logical_expr/fn.lit.html + +### `Expr::WindowFunction` is now `Box`ed + +`Expr::WindowFunction` is now a `Box` instead of a `WindowFunction` directly. +This change was made to reduce the size of `Expr` and improve performance when +planning queries (see [details on #16207]). + +This is a breaking change, so you will need to update your code if you match +on `Expr::WindowFunction` directly. For example, if you have code like this: + +```rust +# /* comment to avoid running +match expr { + Expr::WindowFunction(WindowFunction { + params: + WindowFunctionParams { + partition_by, + order_by, + .. + } + }) => { + // Use partition_by and order_by as needed + } + _ => { + // other expr + } +} +# */ +``` + +You will need to change it to: + +```rust +# /* comment to avoid running +match expr { + Expr::WindowFunction(window_fun) => { + let WindowFunction { + fun, + params: WindowFunctionParams { + args, + partition_by, + .. + }, + } = window_fun.as_ref(); + // Use partition_by and order_by as needed + } + _ => { + // other expr + } +} +# */ +``` + +[details on #16207]: https://github.com/apache/datafusion/pull/16207#issuecomment-2922659103 + +### The `VARCHAR` SQL type is now represented as `Utf8View` in Arrow + +The mapping of the SQL `VARCHAR` type has been changed from `Utf8` to `Utf8View` +which improves performance for many string operations. You can read more about +`Utf8View` in the [DataFusion blog post on German-style strings] + +[datafusion blog post on german-style strings]: https://datafusion.apache.org/blog/2024/09/13/string-view-german-style-strings-part-1/ + +This means that when you create a table with a `VARCHAR` column, it will now use +`Utf8View` as the underlying data type. For example: + +```sql +> CREATE TABLE my_table (my_column VARCHAR); +0 row(s) fetched. +Elapsed 0.001 seconds. + +> DESCRIBE my_table; ++-------------+-----------+-------------+ +| column_name | data_type | is_nullable | ++-------------+-----------+-------------+ +| my_column | Utf8View | YES | ++-------------+-----------+-------------+ +1 row(s) fetched. +Elapsed 0.000 seconds. +``` + +You can restore the old behavior of using `Utf8` by changing the +`datafusion.sql_parser.map_varchar_to_utf8view` configuration setting. For +example + +```sql +> set datafusion.sql_parser.map_varchar_to_utf8view = false; +0 row(s) fetched. +Elapsed 0.001 seconds. + +> CREATE TABLE my_table (my_column VARCHAR); +0 row(s) fetched. +Elapsed 0.014 seconds. + +> DESCRIBE my_table; ++-------------+-----------+-------------+ +| column_name | data_type | is_nullable | ++-------------+-----------+-------------+ +| my_column | Utf8 | YES | ++-------------+-----------+-------------+ +1 row(s) fetched. +Elapsed 0.004 seconds. +``` + +### `ListingOptions` default for `collect_stat` changed from `true` to `false` + +This makes it agree with the default for `SessionConfig`. +Most users won't be impacted by this change but if you were using `ListingOptions` directly +and relied on the default value of `collect_stat` being `true`, you will need to +explicitly set it to `true` in your code. + +```rust +# /* comment to avoid running +ListingOptions::new(Arc::new(ParquetFormat::default())) + .with_collect_stat(true) + // other options +# */ +``` + +### Processing `FieldRef` instead of `DataType` for user defined functions + +In order to support metadata handling and extension types, user defined functions are +now switching to traits which use `FieldRef` rather than a `DataType` and nullability. +This gives a single interface to both of these parameters and additionally allows +access to metadata fields, which can be used for extension types. + +To upgrade structs which implement `ScalarUDFImpl`, if you have implemented +`return_type_from_args` you need instead to implement `return_field_from_args`. +If your functions do not need to handle metadata, this should be straightforward +repackaging of the output data into a `FieldRef`. The name you specify on the +field is not important. It will be overwritten during planning. `ReturnInfo` +has been removed, so you will need to remove all references to it. + +`ScalarFunctionArgs` now contains a field called `arg_fields`. You can use this +to access the metadata associated with the columnar values during invocation. + +To upgrade user defined aggregate functions, there is now a function +`return_field` that will allow you to specify both metadata and nullability of +your function. You are not required to implement this if you do not need to +handle metadata. + +The largest change to aggregate functions happens in the accumulator arguments. +Both the `AccumulatorArgs` and `StateFieldsArgs` now contain `FieldRef` rather +than `DataType`. + +To upgrade window functions, `ExpressionArgs` now contains input fields instead +of input data types. When setting these fields, the name of the field is +not important since this gets overwritten during the planning stage. All you +should need to do is wrap your existing data types in fields with nullability +set depending on your use case. + +### Physical Expression return `Field` + +To support the changes to user defined functions processing metadata, the +`PhysicalExpr` trait, which now must specify a return `Field` based on the input +schema. To upgrade structs which implement `PhysicalExpr` you need to implement +the `return_field` function. There are numerous examples in the `physical-expr` +crate. + +### `FileFormat::supports_filters_pushdown` replaced with `FileSource::try_pushdown_filters` + +To support more general filter pushdown, the `FileFormat::supports_filters_pushdown` was replaced with +`FileSource::try_pushdown_filters`. +If you implemented a custom `FileFormat` that uses a custom `FileSource` you will need to implement +`FileSource::try_pushdown_filters`. +See `ParquetSource::try_pushdown_filters` for an example of how to implement this. + +`FileFormat::supports_filters_pushdown` has been removed. + +### `ParquetExec`, `AvroExec`, `CsvExec`, `JsonExec` Removed + +`ParquetExec`, `AvroExec`, `CsvExec`, and `JsonExec` were deprecated in +DataFusion 46 and are removed in DataFusion 48. This is sooner than the normal +process described in the [API Deprecation Guidelines] because all the tests +cover the new `DataSourceExec` rather than the older structures. As we evolve +`DataSource`, the old structures began to show signs of "bit rotting" (not +working but no one knows due to lack of test coverage). + +[api deprecation guidelines]: https://datafusion.apache.org/contributor-guide/api-health.html#deprecation-guidelines + +### `PartitionedFile` added as an argument to the `FileOpener` trait + +This is necessary to properly fix filter pushdown for filters that combine partition +columns and file columns (e.g. `day = username['dob']`). + +If you implemented a custom `FileOpener` you will need to add the `PartitionedFile` argument +but are not required to use it in any way. diff --git a/versions/55.0.0/_sources/library-user-guide/upgrading/48.0.1.md.txt b/versions/55.0.0/_sources/library-user-guide/upgrading/48.0.1.md.txt new file mode 100644 index 0000000000000..5dfb9e1e3d0b1 --- /dev/null +++ b/versions/55.0.0/_sources/library-user-guide/upgrading/48.0.1.md.txt @@ -0,0 +1,39 @@ + + +# Upgrade Guides + +## DataFusion 48.0.1 + +### `datafusion.execution.collect_statistics` now defaults to `true` + +The default value of the `datafusion.execution.collect_statistics` configuration +setting is now true. This change impacts users that use that value directly and relied +on its default value being `false`. + +This change also restores the default behavior of `ListingTable` to its previous. If you use it directly +you can maintain the current behavior by overriding the default value in your code. + +```rust +# /* comment to avoid running +ListingOptions::new(Arc::new(ParquetFormat::default())) + .with_collect_stat(false) + // other options +# */ +``` diff --git a/versions/55.0.0/_sources/library-user-guide/upgrading/49.0.0.md.txt b/versions/55.0.0/_sources/library-user-guide/upgrading/49.0.0.md.txt new file mode 100644 index 0000000000000..92267a80fae69 --- /dev/null +++ b/versions/55.0.0/_sources/library-user-guide/upgrading/49.0.0.md.txt @@ -0,0 +1,222 @@ + + +# Upgrade Guides + +## DataFusion 49.0.0 + +### `MSRV` updated to 1.85.1 + +The Minimum Supported Rust Version (MSRV) has been updated to [`1.85.1`]. See +[#16728] for details. + +[`1.85.1`]: https://releases.rs/docs/1.85.1/ +[#16728]: https://github.com/apache/datafusion/pull/16728 + +### `DataFusionError` variants are now `Box`ed + +To reduce the size of `DataFusionError`, several variants that were previously stored inline are now `Box`ed. This reduces the size of `Result` and thus stack usage and async state machine size. Please see [#16652] for more details. + +The following variants of `DataFusionError` are now boxed: + +- `ArrowError` +- `SQL` +- `SchemaError` + +This is a breaking change. Code that constructs or matches on these variants will need to be updated. + +For example, to create a `SchemaError`, instead of: + +```rust +# /* comment to avoid running +use datafusion_common::{DataFusionError, SchemaError}; +DataFusionError::SchemaError( + SchemaError::DuplicateUnqualifiedField { name: "foo".to_string() }, + Box::new(None) +) +# */ +``` + +You now need to `Box` the inner error: + +```rust +# /* comment to avoid running +use datafusion_common::{DataFusionError, SchemaError}; +DataFusionError::SchemaError( + Box::new(SchemaError::DuplicateUnqualifiedField { name: "foo".to_string() }), + Box::new(None) +) +# */ +``` + +[#16652]: https://github.com/apache/datafusion/issues/16652 + +### Metadata on Arrow Types is now represented by `FieldMetadata` + +Metadata from the Arrow `Field` is now stored using the `FieldMetadata` +structure. In prior versions it was stored as both a `HashMap` +and a `BTreeMap`. `FieldMetadata` is a easier to work with and +is more efficient. + +To create `FieldMetadata` from a `Field`: + +```rust +# /* comment to avoid running + let metadata = FieldMetadata::from(&field); +# */ +``` + +To add metadata to a `Field`, use the `add_to_field` method: + +```rust +# /* comment to avoid running +let updated_field = metadata.add_to_field(field); +# */ +``` + +See [#16317] for details. + +[#16317]: https://github.com/apache/datafusion/pull/16317 + +### New `datafusion.execution.spill_compression` configuration option + +DataFusion 49.0.0 adds support for compressing spill files when data is written to disk during spilling query execution. A new configuration option `datafusion.execution.spill_compression` controls the compression codec used. + +**Configuration:** + +- **Key**: `datafusion.execution.spill_compression` +- **Default**: `uncompressed` +- **Valid values**: `uncompressed`, `lz4_frame`, `zstd` + +**Usage:** + +```rust +# /* comment to avoid running +use datafusion::prelude::*; +use datafusion_common::config::SpillCompression; + +let config = SessionConfig::default() + .with_spill_compression(SpillCompression::Zstd); +let ctx = SessionContext::new_with_config(config); +# */ +``` + +Or via SQL: + +```sql +SET datafusion.execution.spill_compression = 'zstd'; +``` + +For more details about this configuration option, including performance trade-offs between different compression codecs, see the [Configuration Settings](../../user-guide/configs.md) documentation. + +### Deprecated `map_varchar_to_utf8view` configuration option + +See [issue #16290](https://github.com/apache/datafusion/pull/16290) for more information +The old configuration + +```text +datafusion.sql_parser.map_varchar_to_utf8view +``` + +is now **deprecated** in favor of the unified option below.\ +If you previously used this to control only `VARCHAR`→`Utf8View` mapping, please migrate to `map_string_types_to_utf8view`. + +--- + +### New `map_string_types_to_utf8view` configuration option + +To unify **all** SQL string types (`CHAR`, `VARCHAR`, `TEXT`, `STRING`) to Arrow’s zero‑copy `Utf8View`, DataFusion 49.0.0 introduces: + +- **Key**: `datafusion.sql_parser.map_string_types_to_utf8view` +- **Default**: `true` + +**Description:** + +- When **true** (default), **all** SQL string types are mapped to `Utf8View`, avoiding full‑copy UTF‑8 allocations and improving performance. +- When **false**, DataFusion falls back to the legacy `Utf8` mapping for **all** string types. + +#### Examples + +```rust +# /* comment to avoid running +// Disable Utf8View mapping for all SQL string types +let opts = datafusion::sql::planner::ParserOptions::new() + .with_map_string_types_to_utf8view(false); + +// Verify the setting is applied +assert!(!opts.map_string_types_to_utf8view); +# */ +``` + +--- + +```sql +-- Disable Utf8View mapping globally +SET datafusion.sql_parser.map_string_types_to_utf8view = false; + +-- Now VARCHAR, CHAR, TEXT, STRING all use Utf8 rather than Utf8View +CREATE TABLE my_table (a VARCHAR, b TEXT, c STRING); +DESCRIBE my_table; +``` + +### Deprecating `SchemaAdapterFactory` and `SchemaAdapter` + +We are moving away from converting data (using `SchemaAdapter`) to converting the expressions themselves (which is more efficient and flexible). + +See [issue #16800](https://github.com/apache/datafusion/issues/16800) for more information +The first place this change has taken place is in predicate pushdown for Parquet. +By default if you do not use a custom `SchemaAdapterFactory` we will use expression conversion instead. +If you do set a custom `SchemaAdapterFactory` we will continue to use it but emit a warning about that code path being deprecated. + +To resolve this you need to implement a custom `PhysicalExprAdapterFactory` and use that instead of a `SchemaAdapterFactory`. +See the [default values](https://github.com/apache/datafusion/blob/main/datafusion-examples/examples/custom_data_source/default_column_values.rs) for an example of how to do this. +Opting into the new APIs will set you up for future changes since we plan to expand use of `PhysicalExprAdapterFactory` to other areas of DataFusion. + +See [#16800] for details. + +[#16800]: https://github.com/apache/datafusion/issues/16800 + +### `TableParquetOptions` Updated + +The `TableParquetOptions` struct has a new `crypto` field to specify encryption +options for Parquet files. The `ParquetEncryptionOptions` implements `Default` +so you can upgrade your existing code like this: + +```rust +# /* comment to avoid running +TableParquetOptions { + global, + column_specific_options, + key_value_metadata, +} +# */ +``` + +To this: + +```rust +# /* comment to avoid running +TableParquetOptions { + global, + column_specific_options, + key_value_metadata, + crypto: Default::default(), // New crypto field +} +# */ +``` diff --git a/versions/55.0.0/_sources/library-user-guide/upgrading/50.0.0.md.txt b/versions/55.0.0/_sources/library-user-guide/upgrading/50.0.0.md.txt new file mode 100644 index 0000000000000..d8155dab58962 --- /dev/null +++ b/versions/55.0.0/_sources/library-user-guide/upgrading/50.0.0.md.txt @@ -0,0 +1,330 @@ + + +# Upgrade Guides + +## DataFusion 50.0.0 + +### ListingTable automatically detects Hive Partitioned tables + +DataFusion 50.0.0 automatically infers Hive partitions when using the `ListingTableFactory` and `CREATE EXTERNAL TABLE`. Previously, +when creating a `ListingTable`, datasets that use Hive partitioning (e.g. +`/table_root/column1=value1/column2=value2/data.parquet`) would not have the Hive columns reflected in +the table's schema or data. The previous behavior can be +restored by setting the `datafusion.execution.listing_table_factory_infer_partitions` configuration option to `false`. +See [issue #17049] for more details. + +[issue #17049]: https://github.com/apache/datafusion/issues/17049 + +### `MSRV` updated to 1.86.0 + +The Minimum Supported Rust Version (MSRV) has been updated to [`1.86.0`]. +See [#17230] for details. + +[`1.86.0`]: https://releases.rs/docs/1.86.0/ +[#17230]: https://github.com/apache/datafusion/pull/17230 + +### `ScalarUDFImpl`, `AggregateUDFImpl` and `WindowUDFImpl` traits now require `PartialEq`, `Eq`, and `Hash` traits + +To address error-proneness of `ScalarUDFImpl::equals`, `AggregateUDFImpl::equals`and +`WindowUDFImpl::equals` methods and to make it easy to implement function equality correctly, +the `equals` and `hash_value` methods have been removed from `ScalarUDFImpl`, `AggregateUDFImpl` +and `WindowUDFImpl` traits. They are replaced the requirement to implement the `PartialEq`, `Eq`, +and `Hash` traits on any type implementing `ScalarUDFImpl`, `AggregateUDFImpl` or `WindowUDFImpl`. +Please see [issue #16677] for more details. + +Most of the scalar functions are stateless and have a `signature` field. These can be migrated +using regular expressions + +- search for `\#\[derive\(Debug\)\](\n *(pub )?struct \w+ \{\n *signature\: Signature\,\n *\})`, +- replace with `#[derive(Debug, PartialEq, Eq, Hash)]$1`, +- review all the changes and make sure only function structs were changed. + +[issue #16677]: https://github.com/apache/datafusion/issues/16677 + +### `AsyncScalarUDFImpl::invoke_async_with_args` returns `ColumnarValue` + +In order to enable single value optimizations and be consistent with other +user defined function APIs, the `AsyncScalarUDFImpl::invoke_async_with_args` method now +returns a `ColumnarValue` instead of a `ArrayRef`. + +To upgrade, change the return type of your implementation + +```rust +# /* comment to avoid running +impl AsyncScalarUDFImpl for AskLLM { + async fn invoke_async_with_args( + &self, + args: ScalarFunctionArgs, + _option: &ConfigOptions, + ) -> Result { + .. + return array_ref; // old code + } +} +# */ +``` + +To return a `ColumnarValue` + +```rust +# /* comment to avoid running +impl AsyncScalarUDFImpl for AskLLM { + async fn invoke_async_with_args( + &self, + args: ScalarFunctionArgs, + _option: &ConfigOptions, + ) -> Result { + .. + return ColumnarValue::from(array_ref); // new code + } +} +# */ +``` + +See [#16896](https://github.com/apache/datafusion/issues/16896) for more details. + +### `ProjectionExpr` changed from type alias to struct + +`ProjectionExpr` has been changed from a type alias to a struct with named fields to improve code clarity and maintainability. + +**Before:** + +```rust,ignore +pub type ProjectionExpr = (Arc, String); +``` + +**After:** + +```rust,ignore +#[derive(Debug, Clone)] +pub struct ProjectionExpr { + pub expr: Arc, + pub alias: String, +} +``` + +To upgrade your code: + +- Replace tuple construction `(expr, alias)` with `ProjectionExpr::new(expr, alias)` or `ProjectionExpr { expr, alias }` +- Replace tuple field access `.0` and `.1` with `.expr` and `.alias` +- Update pattern matching from `(expr, alias)` to `ProjectionExpr { expr, alias }` + +This mainly impacts use of `ProjectionExec`. + +This change was done in [#17398] + +[#17398]: https://github.com/apache/datafusion/pull/17398 + +### `SessionState`, `SessionConfig`, and `OptimizerConfig` returns `&Arc` instead of `&ConfigOptions` + +To provide broader access to `ConfigOptions` and reduce required clones, some +APIs have been changed to return a `&Arc` instead of a +`&ConfigOptions`. This allows sharing the same `ConfigOptions` across multiple +threads without needing to clone the entire `ConfigOptions` structure unless it +is modified. + +Most users will not be impacted by this change since the Rust compiler typically +automatically dereference the `Arc` when needed. However, in some cases you may +have to change your code to explicitly call `as_ref()` for example, from + +```rust +# /* comment to avoid running +let optimizer_config: &ConfigOptions = state.options(); +# */ +``` + +To + +```rust +# /* comment to avoid running +let optimizer_config: &ConfigOptions = state.options().as_ref(); +# */ +``` + +See PR [#16970](https://github.com/apache/datafusion/pull/16970) + +### API Change to `AsyncScalarUDFImpl::invoke_async_with_args` + +The `invoke_async_with_args` method of the `AsyncScalarUDFImpl` trait has been +updated to remove the `_option: &ConfigOptions` parameter to simplify the API +now that the `ConfigOptions` can be accessed through the `ScalarFunctionArgs` +parameter. + +You can change your code like this + +```rust +# /* comment to avoid running +impl AsyncScalarUDFImpl for AskLLM { + async fn invoke_async_with_args( + &self, + args: ScalarFunctionArgs, + _option: &ConfigOptions, + ) -> Result { + .. + } + ... +} +# */ +``` + +To this: + +```rust +# /* comment to avoid running + +impl AsyncScalarUDFImpl for AskLLM { + async fn invoke_async_with_args( + &self, + args: ScalarFunctionArgs, + ) -> Result { + let options = &args.config_options; + .. + } + ... +} +# */ +``` + +### Schema Rewriter Module Moved to New Crate + +The `schema_rewriter` module and its associated symbols have been moved from `datafusion_physical_expr` to a new crate `datafusion_physical_expr_adapter`. This affects the following symbols: + +- `DefaultPhysicalExprAdapter` +- `DefaultPhysicalExprAdapterFactory` +- `PhysicalExprAdapter` +- `PhysicalExprAdapterFactory` + +To upgrade, change your imports to: + +```rust +use datafusion_physical_expr_adapter::{ + DefaultPhysicalExprAdapter, DefaultPhysicalExprAdapterFactory, + PhysicalExprAdapter, PhysicalExprAdapterFactory +}; +``` + +### Upgrade to arrow `56.0.0` and parquet `56.0.0` + +This version of DataFusion upgrades the underlying Apache Arrow implementation +to version `56.0.0`. See the [release notes](https://github.com/apache/arrow-rs/releases/tag/56.0.0) +for more details. + +### Added `ExecutionPlan::reset_state` + +In order to fix a bug in DataFusion `49.0.0` where dynamic filters (currently only generated in the presence of a query such as `ORDER BY ... LIMIT ...`) +produced incorrect results in recursive queries, a new method `reset_state` has been added to the `ExecutionPlan` trait. + +Any `ExecutionPlan` that needs to maintain internal state or references to other nodes in the execution plan tree should implement this method to reset that state. +See [#17028] for more details and an example implementation for `SortExec`. + +[#17028]: https://github.com/apache/datafusion/pull/17028 + +### Nested Loop Join input sort order cannot be preserved + +The Nested Loop Join operator has been rewritten from scratch to improve performance and memory efficiency. From the micro-benchmarks: this change introduces up to 5X speed-up and uses only 1% memory in extreme cases compared to the previous implementation. + +However, the new implementation cannot preserve input sort order like the old version could. This is a fundamental design trade-off that prioritizes performance and memory efficiency over sort order preservation. + +See [#16996] for details. + +[#16996]: https://github.com/apache/datafusion/pull/16996 + +### Add `as_any()` method to `LazyBatchGenerator` + +To help with protobuf serialization, the `as_any()` method has been added to the `LazyBatchGenerator` trait. This means you will need to add `as_any()` to your implementation of `LazyBatchGenerator`: + +```rust +# /* comment to avoid running + +impl LazyBatchGenerator for MyBatchGenerator { + fn as_any(&self) -> &dyn Any { + self + } + + ... +} + +# */ +``` + +See [#17200](https://github.com/apache/datafusion/pull/17200) for details. + +### Refactored `DataSource::try_swapping_with_projection` + +We refactored `DataSource::try_swapping_with_projection` to simplify the method and minimize leakage across the ExecutionPlan <-> DataSource abstraction layer. +Reimplementation for any custom `DataSource` should be relatively straightforward, see [#17395] for more details. + +[#17395]: https://github.com/apache/datafusion/pull/17395/ + +### `FileOpenFuture` now uses `DataFusionError` instead of `ArrowError` + +The `FileOpenFuture` type alias has been updated to use `DataFusionError` instead of `ArrowError` for its error type. This change affects the `FileOpener` trait and any implementations that work with file streaming operations. + +**Before:** + +```rust,ignore +pub type FileOpenFuture = BoxFuture<'static, Result>>>; +``` + +**After:** + +```rust,ignore +pub type FileOpenFuture = BoxFuture<'static, Result>>>; +``` + +If you have custom implementations of `FileOpener` or work directly with `FileOpenFuture`, you'll need to update your error handling to use `DataFusionError` instead of `ArrowError`. The `FileStreamState` enum's `Open` variant has also been updated accordingly. See [#17397] for more details. + +[#17397]: https://github.com/apache/datafusion/pull/17397 + +### FFI user defined aggregate function signature change + +The Foreign Function Interface (FFI) signature for user defined aggregate functions +has been updated to call `return_field` instead of `return_type` on the underlying +aggregate function. This is to support metadata handling with these aggregate functions. +This change should be transparent to most users. If you have written unit tests to call +`return_type` directly, you may need to change them to calling `return_field` instead. + +This update is a breaking change to the FFI API. The current best practice when using the +FFI crate is to ensure that all libraries that are interacting are using the same +underlying Rust version. Issue [#17374] has been opened to discuss stabilization of +this interface so that these libraries can be used across different DataFusion versions. + +See [#17407] for details. + +[#17407]: https://github.com/apache/datafusion/pull/17407 +[#17374]: https://github.com/apache/datafusion/issues/17374 + +### Added `PhysicalExpr::is_volatile_node` + +We added a method to `PhysicalExpr` to mark a `PhysicalExpr` as volatile: + +```rust,ignore +impl PhysicalExpr for MyRandomExpr { + fn is_volatile_node(&self) -> bool { + true + } +} +``` + +We've shipped this with a default value of `false` to minimize breakage but we highly recommend that implementers of `PhysicalExpr` opt into a behavior, even if it is returning `false`. + +You can see more discussion and example implementations in [#17351]. + +[#17351]: https://github.com/apache/datafusion/pull/17351 diff --git a/versions/55.0.0/_sources/library-user-guide/upgrading/51.0.0.md.txt b/versions/55.0.0/_sources/library-user-guide/upgrading/51.0.0.md.txt new file mode 100644 index 0000000000000..c3acfe15c493f --- /dev/null +++ b/versions/55.0.0/_sources/library-user-guide/upgrading/51.0.0.md.txt @@ -0,0 +1,272 @@ + + +# Upgrade Guides + +## DataFusion 51.0.0 + +### `arrow` / `parquet` updated to 57.0.0 + +### Upgrade to arrow `57.0.0` and parquet `57.0.0` + +This version of DataFusion upgrades the underlying Apache Arrow implementation +to version `57.0.0`, including several dependent crates such as `prost`, +`tonic`, `pyo3`, and `substrait`. . See the [release +notes](https://github.com/apache/arrow-rs/releases/tag/57.0.0) for more details. + +### `MSRV` updated to 1.88.0 + +The Minimum Supported Rust Version (MSRV) has been updated to [`1.88.0`]. + +[`1.88.0`]: https://releases.rs/docs/1.88.0/ + +### `FunctionRegistry` exposes two additional methods + +`FunctionRegistry` exposes two additional methods `udafs` and `udwfs` which expose set of registered user defined aggregation and window function names. To upgrade implement methods returning set of registered function names: + +```diff +impl FunctionRegistry for FunctionRegistryImpl { + fn udfs(&self) -> HashSet { + self.scalar_functions.keys().cloned().collect() + } ++ fn udafs(&self) -> HashSet { ++ self.aggregate_functions.keys().cloned().collect() ++ } ++ ++ fn udwfs(&self) -> HashSet { ++ self.window_functions.keys().cloned().collect() ++ } +} +``` + +### `datafusion-proto` use `TaskContext` rather than `SessionContext` in physical plan serde methods + +There have been changes in the public API methods of `datafusion-proto` which handle physical plan serde. + +Methods like `physical_plan_from_bytes`, `parse_physical_expr` and similar, expect `TaskContext` instead of `SessionContext` + +```diff +- let plan2 = physical_plan_from_bytes(&bytes, &ctx)?; ++ let plan2 = physical_plan_from_bytes(&bytes, &ctx.task_ctx())?; +``` + +as `TaskContext` contains `RuntimeEnv` methods such as `try_into_physical_plan` will not have explicit `RuntimeEnv` parameter. + +```diff +let result_exec_plan: Arc = proto +- .try_into_physical_plan(&ctx, runtime.deref(), &composed_codec) ++. .try_into_physical_plan(&ctx.task_ctx(), &composed_codec) +``` + +`PhysicalExtensionCodec::try_decode()` expects `TaskContext` instead of `FunctionRegistry`: + +```diff +pub trait PhysicalExtensionCodec { + fn try_decode( + &self, + buf: &[u8], + inputs: &[Arc], +- registry: &dyn FunctionRegistry, ++ ctx: &TaskContext, + ) -> Result>; +``` + +See [issue #17601] for more details. + +[issue #17601]: https://github.com/apache/datafusion/issues/17601 + +### `SessionState`'s `sql_to_statement` method takes `Dialect` rather than a `str` + +The `dialect` parameter of `sql_to_statement` method defined in `datafusion::execution::session_state::SessionState` +has changed from `&str` to `&Dialect`. +`Dialect` is an enum defined in the `datafusion-common` +crate under the `config` module that provides type safety +and better validation for SQL dialect selection + +### Reorganization of `ListingTable` into `datafusion-catalog-listing` crate + +There has been a long standing request to remove features such as `ListingTable` +from the `datafusion` crate to support faster build times. The structs +`ListingOptions`, `ListingTable`, and `ListingTableConfig` are now available +within the `datafusion-catalog-listing` crate. These are re-exported in +the `datafusion` crate, so this should be a minimal impact to existing users. + +See [issue #14462] and [issue #17713] for more details. + +[issue #14462]: https://github.com/apache/datafusion/issues/14462 +[issue #17713]: https://github.com/apache/datafusion/issues/17713 + +### Reorganization of `ArrowSource` into `datafusion-datasource-arrow` crate + +To support [issue #17713] the `ArrowSource` code has been removed from +the `datafusion` core crate into it's own crate, `datafusion-datasource-arrow`. +This follows the pattern for the AVRO, CSV, JSON, and Parquet data sources. +Users may need to update their paths to account for these changes. + +See [issue #17713] for more details. + +### `FileScanConfig::projection` renamed to `FileScanConfig::projection_exprs` + +The `projection` field in `FileScanConfig` has been renamed to `projection_exprs` and its type has changed from `Option>` to `Option`. This change enables more powerful projection pushdown capabilities by supporting arbitrary physical expressions rather than just column indices. + +**Impact on direct field access:** + +If you directly access the `projection` field: + +```rust,ignore +let config: FileScanConfig = ...; +let projection = config.projection; +``` + +You should update to: + +```rust,ignore +let config: FileScanConfig = ...; +let projection_exprs = config.projection_exprs; +``` + +**Impact on builders:** + +The `FileScanConfigBuilder::with_projection()` method has been deprecated in favor of `with_projection_indices()`: + +```diff +let config = FileScanConfigBuilder::new(url, file_source) +- .with_projection(Some(vec![0, 2, 3])) ++ .with_projection_indices(Some(vec![0, 2, 3])) + .build(); +``` + +Note: `with_projection()` still works but is deprecated and will be removed in a future release. + +**What is `ProjectionExprs`?** + +`ProjectionExprs` is a new type that represents a list of physical expressions for projection. While it can be constructed from column indices (which is what `with_projection_indices` does internally), it also supports arbitrary physical expressions, enabling advanced features like expression evaluation during scanning. + +You can access column indices from `ProjectionExprs` using its methods if needed: + +```rust,ignore +let projection_exprs: ProjectionExprs = ...; +// Get the column indices if the projection only contains simple column references +let indices = projection_exprs.column_indices(); +``` + +### `DESCRIBE query` support + +`DESCRIBE query` was previously an alias for `EXPLAIN query`, which outputs the +_execution plan_ of the query. With this release, `DESCRIBE query` now outputs +the computed _schema_ of the query, consistent with the behavior of `DESCRIBE table_name`. + +### `datafusion.execution.time_zone` default configuration changed + +The default value for `datafusion.execution.time_zone` previously was a string value of `+00:00` (GMT/Zulu time). +This was changed to be an `Option` with a default of `None`. If you want to change the timezone back +to the previous value you can execute the sql: + +```sql +SET +TIMEZONE = '+00:00'; +``` + +This change was made to better support using the default timezone in scalar UDF functions such as +`now`, `current_date`, `current_time`, and `to_timestamp` among others. + +### Introduction of `TableSchema` and changes to `FileSource::with_schema()` method + +A new `TableSchema` struct has been introduced in the `datafusion-datasource` crate to better manage table schemas with partition columns. This struct helps distinguish between: + +- **File schema**: The schema of actual data files on disk +- **Partition columns**: Columns derived from directory structure (e.g., Hive-style partitioning) +- **Table schema**: The complete schema combining both file and partition columns + +As part of this change, the `FileSource::with_schema()` method signature has changed from accepting a `SchemaRef` to accepting a `TableSchema`. + +**Who is affected:** + +- Users who have implemented custom `FileSource` implementations will need to update their code +- Users who only use built-in file sources (Parquet, CSV, JSON, AVRO, Arrow) are not affected + +**Migration guide for custom `FileSource` implementations:** + +```diff + use datafusion_datasource::file::FileSource; +-use arrow::datatypes::SchemaRef; ++use datafusion_datasource::TableSchema; + + impl FileSource for MyCustomSource { +- fn with_schema(&self, schema: SchemaRef) -> Arc { ++ fn with_schema(&self, schema: TableSchema) -> Arc { + Arc::new(Self { +- schema: Some(schema), ++ // Use schema.file_schema() to get the file schema without partition columns ++ schema: Some(Arc::clone(schema.file_schema())), + ..self.clone() + }) + } + } +``` + +For implementations that need access to partition columns: + +```rust,ignore +fn with_schema(&self, schema: TableSchema) -> Arc { + Arc::new(Self { + file_schema: Arc::clone(schema.file_schema()), + partition_cols: schema.table_partition_cols().clone(), + table_schema: Arc::clone(schema.table_schema()), + ..self.clone() + }) +} +``` + +**Note**: Most `FileSource` implementations only need to store the file schema (without partition columns), as shown in the first example. The second pattern of storing all three schema components is typically only needed for advanced use cases where you need access to different schema representations for different operations (e.g., ParquetSource uses the file schema for building pruning predicates but needs the table schema for filter pushdown logic). + +**Using `TableSchema` directly:** + +If you're constructing a `FileScanConfig` or working with table schemas and partition columns, you can now use `TableSchema`: + +```rust +use datafusion_datasource::TableSchema; +use arrow::datatypes::{Schema, Field, DataType}; +use std::sync::Arc; + +// Create a TableSchema with partition columns +let file_schema = Arc::new(Schema::new(vec![ + Field::new("user_id", DataType::Int64, false), + Field::new("amount", DataType::Float64, false), +])); + +let partition_cols = vec![ + Arc::new(Field::new("date", DataType::Utf8, false)), + Arc::new(Field::new("region", DataType::Utf8, false)), +]; + +let table_schema = TableSchema::new(file_schema, partition_cols); + +// Access different schema representations +let file_schema_ref = table_schema.file_schema(); // Schema without partition columns +let full_schema = table_schema.table_schema(); // Complete schema with partition columns +let partition_cols_ref = table_schema.table_partition_cols(); // Just the partition columns +``` + +### `AggregateUDFImpl::is_ordered_set_aggregate` has been renamed to `AggregateUDFImpl::supports_within_group_clause` + +This method has been renamed to better reflect the actual impact it has for aggregate UDF implementations. +The accompanying `AggregateUDF::is_ordered_set_aggregate` has also been renamed to `AggregateUDF::supports_within_group_clause`. +No functionality has been changed with regards to this method; it still refers only to permitting use of `WITHIN GROUP` +SQL syntax for the aggregate function. diff --git a/versions/55.0.0/_sources/library-user-guide/upgrading/52.0.0.md.txt b/versions/55.0.0/_sources/library-user-guide/upgrading/52.0.0.md.txt new file mode 100644 index 0000000000000..8bf2f803bede6 --- /dev/null +++ b/versions/55.0.0/_sources/library-user-guide/upgrading/52.0.0.md.txt @@ -0,0 +1,669 @@ + + +# Upgrade Guides + +## DataFusion 52.0.0 + +### Changes to DFSchema API + +To permit more efficient planning, several methods on `DFSchema` have been +changed to return references to the underlying [`&FieldRef`] rather than +[`&Field`]. This allows planners to more cheaply copy the references via +`Arc::clone` rather than cloning the entire `Field` structure. + +You may need to change code to use `Arc::clone` instead of `.as_ref().clone()` +directly on the `Field`. For example: + +```diff +- let field = df_schema.field("my_column").as_ref().clone(); ++ let field = Arc::clone(df_schema.field("my_column")); +``` + +### ListingTableProvider now caches `LIST` commands + +In prior versions, `ListingTableProvider` would issue `LIST` commands to +the underlying object store each time it needed to list files for a query. +To improve performance, `ListingTableProvider` now caches the results of +`LIST` commands for the lifetime of the `ListingTableProvider` instance or +until a cache entry expires. + +Note that by default the cache has no expiration time, so if files are added or removed +from the underlying object store, the `ListingTableProvider` will not see +those changes until the `ListingTableProvider` instance is dropped and recreated. + +You can configure the maximum cache size and cache entry expiration time via configuration options: + +- `datafusion.runtime.list_files_cache_limit` - Limits the size of the cache in bytes +- `datafusion.runtime.list_files_cache_ttl` - Limits the TTL (time-to-live) of an entry in minutes and/or seconds + +Detailed configuration information can be found in the [DataFusion Runtime +Configuration](https://datafusion.apache.org/user-guide/configs.html#runtime-configuration-settings) user's guide. + +Caching can be disabled by setting the limit to 0: + +```sql +SET datafusion.runtime.list_files_cache_limit TO "0K"; +``` + +Note that the internal API has changed to use a trait `ListFilesCache` instead of a type alias. + +### `newlines_in_values` moved from `FileScanConfig` to `CsvOptions` + +The CSV-specific `newlines_in_values` configuration option has been moved from `FileScanConfig` to `CsvOptions`, as it only applies to CSV file parsing. + +**Who is affected:** + +- Users who set `newlines_in_values` via `FileScanConfigBuilder::with_newlines_in_values()` + +**Migration guide:** + +Set `newlines_in_values` in `CsvOptions` instead of on `FileScanConfigBuilder`: + +**Before:** + +```rust,ignore +let source = Arc::new(CsvSource::new(file_schema.clone())); +let config = FileScanConfigBuilder::new(object_store_url, source) + .with_newlines_in_values(true) + .build(); +``` + +**After:** + +```rust,ignore +let options = CsvOptions { + newlines_in_values: Some(true), + ..Default::default() +}; +let source = Arc::new(CsvSource::new(file_schema.clone()) + .with_csv_options(options)); +let config = FileScanConfigBuilder::new(object_store_url, source) + .build(); +``` + +### Removal of `pyarrow` feature + +The `pyarrow` feature flag has been removed. This feature has been migrated to +the `datafusion-python` repository since version `44.0.0`. + +### Refactoring of `FileSource` constructors and `FileScanConfigBuilder` to accept schemas upfront + +The way schemas are passed to file sources and scan configurations has been significantly refactored. File sources now require the schema (including partition columns) to be provided at construction time, and `FileScanConfigBuilder` no longer takes a separate schema parameter. + +**Who is affected:** + +- Users who create `FileScanConfig` or file sources (`ParquetSource`, `CsvSource`, `JsonSource`, `AvroSource`) directly +- Users who implement custom `FileFormat` implementations + +**Key changes:** + +1. **FileSource constructors now require TableSchema**: All built-in file sources now take the schema in their constructor: + + ```diff + - let source = ParquetSource::default(); + + let source = ParquetSource::new(table_schema); + ``` + +2. **FileScanConfigBuilder no longer takes schema as a parameter**: The schema is now passed via the FileSource: + + ```diff + - FileScanConfigBuilder::new(url, schema, source) + + FileScanConfigBuilder::new(url, source) + ``` + +3. **Partition columns are now part of TableSchema**: The `with_table_partition_cols()` method has been removed from `FileScanConfigBuilder`. Partition columns are now passed as part of the `TableSchema` to the FileSource constructor: + + ```diff + + let table_schema = TableSchema::new( + + file_schema, + + vec![Arc::new(Field::new("date", DataType::Utf8, false))], + + ); + + let source = ParquetSource::new(table_schema); + let config = FileScanConfigBuilder::new(url, source) + - .with_table_partition_cols(vec![Field::new("date", DataType::Utf8, false)]) + .with_file(partitioned_file) + .build(); + ``` + +4. **FileFormat::file_source() now takes TableSchema parameter**: Custom `FileFormat` implementations must be updated: + ```diff + impl FileFormat for MyFileFormat { + - fn file_source(&self) -> Arc { + + fn file_source(&self, table_schema: TableSchema) -> Arc { + - Arc::new(MyFileSource::default()) + + Arc::new(MyFileSource::new(table_schema)) + } + } + ``` + +**Migration examples:** + +For Parquet files: + +```diff +- let source = Arc::new(ParquetSource::default()); +- let config = FileScanConfigBuilder::new(url, schema, source) ++ let table_schema = TableSchema::new(schema, vec![]); ++ let source = Arc::new(ParquetSource::new(table_schema)); ++ let config = FileScanConfigBuilder::new(url, source) + .with_file(partitioned_file) + .build(); +``` + +For CSV files with partition columns: + +```diff +- let source = Arc::new(CsvSource::new(true, b',', b'"')); +- let config = FileScanConfigBuilder::new(url, file_schema, source) +- .with_table_partition_cols(vec![Field::new("year", DataType::Int32, false)]) ++ let options = CsvOptions { ++ has_header: Some(true), ++ delimiter: b',', ++ quote: b'"', ++ ..Default::default() ++ }; ++ let table_schema = TableSchema::new( ++ file_schema, ++ vec![Arc::new(Field::new("year", DataType::Int32, false))], ++ ); ++ let source = Arc::new(CsvSource::new(table_schema).with_csv_options(options)); ++ let config = FileScanConfigBuilder::new(url, source) + .build(); +``` + +### Adaptive filter representation in Parquet filter pushdown + +As of Arrow 57.1.0, DataFusion uses a new adaptive filter strategy when +evaluating pushed down filters for Parquet files. This new strategy improves +performance for certain types of queries where the results of filtering are +more efficiently represented with a bitmask rather than a selection. +See [arrow-rs #5523] for more details. + +This change only applies to the built-in Parquet data source with filter-pushdown enabled ( +which is [not yet the default behavior]). + +You can disable the new behavior by setting the +`datafusion.execution.parquet.force_filter_selections` [configuration setting] to true. + +```sql +> set datafusion.execution.parquet.force_filter_selections = true; +``` + +[arrow-rs #5523]: https://github.com/apache/arrow-rs/issues/5523 +[configuration setting]: https://datafusion.apache.org/user-guide/configs.html +[not yet the default behavior]: https://github.com/apache/datafusion/issues/3463 + +### Statistics handling moved from `FileSource` to `FileScanConfig` + +Statistics are now managed directly by `FileScanConfig` instead of being delegated to `FileSource` implementations. This simplifies the `FileSource` trait and provides more consistent statistics handling across all file formats. + +**Who is affected:** + +- Users who have implemented custom `FileSource` implementations + +**Breaking changes:** + +Two methods have been removed from the `FileSource` trait: + +- `with_statistics(&self, statistics: Statistics) -> Arc` +- `statistics(&self) -> Result` + +**Migration guide:** + +If you have a custom `FileSource` implementation, you need to: + +1. Remove the `with_statistics` method implementation +2. Remove the `statistics` method implementation +3. Remove any internal state that was storing statistics + +**Before:** + +```rust,ignore +#[derive(Clone)] +struct MyCustomSource { + table_schema: TableSchema, + projected_statistics: Option, + // other fields... +} + +impl FileSource for MyCustomSource { + fn with_statistics(&self, statistics: Statistics) -> Arc { + Arc::new(Self { + table_schema: self.table_schema.clone(), + projected_statistics: Some(statistics), + // other fields... + }) + } + + fn statistics(&self) -> Result { + Ok(self.projected_statistics.clone().unwrap_or_else(|| + Statistics::new_unknown(self.table_schema.file_schema()) + )) + } + + // other methods... +} +``` + +**After:** + +```rust,ignore +#[derive(Clone)] +struct MyCustomSource { + table_schema: TableSchema, + // projected_statistics field removed + // other fields... +} + +impl FileSource for MyCustomSource { + // with_statistics method removed + // statistics method removed + + // other methods... +} +``` + +**Accessing statistics:** + +Statistics are now accessed through `FileScanConfig` instead of `FileSource`: + +```diff +- let stats = config.file_source.statistics()?; ++ let stats = config.statistics(); +``` + +Note that `FileScanConfig::statistics()` automatically marks statistics as inexact when filters are present, ensuring correctness when filters are pushed down. + +### Partition column handling moved out of `PhysicalExprAdapter` + +Partition column replacement is now a separate preprocessing step performed before expression rewriting via `PhysicalExprAdapter`. This change provides better separation of concerns and makes the adapter more focused on schema differences rather than partition value substitution. + +**Who is affected:** + +- Users who have custom implementations of `PhysicalExprAdapterFactory` that handle partition columns +- Users who directly use the `FilePruner` API + +**Breaking changes:** + +1. `FilePruner::try_new()` signature changed: the `partition_fields` parameter has been removed since partition column handling is now done separately +2. Partition column replacement must now be done via `replace_columns_with_literals()` before expressions are passed to the adapter + +**Migration guide:** + +If you have code that creates a `FilePruner` with partition fields: + +**Before:** + +```rust,ignore +use datafusion_pruning::FilePruner; + +let pruner = FilePruner::try_new( + predicate, + file_schema, + partition_fields, // This parameter is removed + file_stats, +)?; +``` + +**After:** + +```rust,ignore +use datafusion_pruning::FilePruner; + +// Partition fields are no longer needed +let pruner = FilePruner::try_new( + predicate, + file_schema, + file_stats, +)?; +``` + +If you have custom code that relies on `PhysicalExprAdapter` to handle partition columns, you must now call `replace_columns_with_literals()` separately: + +**Before:** + +```rust,ignore +// Adapter handled partition column replacement internally +let adapted_expr = adapter.rewrite(expr)?; +``` + +**After:** + +```rust,ignore +use datafusion_physical_expr_adapter::replace_columns_with_literals; + +// Replace partition columns first +let expr_with_literals = replace_columns_with_literals(expr, &partition_values)?; +// Then apply the adapter +let adapted_expr = adapter.rewrite(expr_with_literals)?; +``` + +### `build_row_filter` signature simplified + +The `build_row_filter` function in `datafusion-datasource-parquet` has been simplified to take a single schema parameter instead of two. +The expectation is now that the filter has been adapted to the physical file schema (the arrow representation of the parquet file's schema) before being passed to this function +using a `PhysicalExprAdapter` for example. + +**Who is affected:** + +- Users who call `build_row_filter` directly + +**Breaking changes:** + +The function signature changed from: + +```rust,ignore +pub fn build_row_filter( + expr: &Arc, + physical_file_schema: &SchemaRef, + predicate_file_schema: &SchemaRef, // removed + metadata: &ParquetMetaData, + reorder_predicates: bool, + file_metrics: &ParquetFileMetrics, +) -> Result> +``` + +To: + +```rust,ignore +pub fn build_row_filter( + expr: &Arc, + file_schema: &SchemaRef, + metadata: &ParquetMetaData, + reorder_predicates: bool, + file_metrics: &ParquetFileMetrics, +) -> Result> +``` + +**Migration guide:** + +Remove the duplicate schema parameter from your call: + +```diff +- build_row_filter(&predicate, &file_schema, &file_schema, metadata, reorder, metrics) ++ build_row_filter(&predicate, &file_schema, metadata, reorder, metrics) +``` + +### Planner now requires explicit opt-in for WITHIN GROUP syntax + +The SQL planner now enforces the aggregate UDF contract more strictly: the +`WITHIN GROUP (ORDER BY ...)` syntax is accepted only if the aggregate UDAF +explicitly advertises support by returning `true` from +`AggregateUDFImpl::supports_within_group_clause()`. + +Previously the planner forwarded a `WITHIN GROUP` clause to order-sensitive +aggregates even when they did not implement ordered-set semantics, which could +cause queries such as `SUM(x) WITHIN GROUP (ORDER BY x)` to plan successfully. +This behavior was too permissive and has been changed to match PostgreSQL and +the documented semantics. + +Migration: If your UDAF intentionally implements ordered-set semantics and +wants to accept the `WITHIN GROUP` SQL syntax, update your implementation to +return `true` from `supports_within_group_clause()` and handle the ordering +semantics in your accumulator implementation. If your UDAF is merely +order-sensitive (but not an ordered-set aggregate), do not advertise +`supports_within_group_clause()` and clients should use alternative function +signatures (for example, explicit ordering as a function argument) instead. + +### `AggregateUDFImpl::supports_null_handling_clause` now defaults to `false` + +This method specifies whether an aggregate function allows `IGNORE NULLS`/`RESPECT NULLS` +during SQL parsing, with the implication it respects these configs during computation. + +Most DataFusion aggregate functions silently ignored this syntax in prior versions +as they did not make use of it and it was permitted by default. We change this so +only the few functions which do respect this clause (e.g. `array_agg`, `first_value`, +`last_value`) need to implement it. + +Custom user defined aggregate functions will also error if this syntax is used, +unless they explicitly declare support by overriding the method. + +For example, SQL parsing will now fail for queries such as this: + +```sql +SELECT median(c1) IGNORE NULLS FROM table +``` + +Instead of silently succeeding. + +### API change for `CacheAccessor` trait + +The remove API no longer requires a mutable instance + +### FFI crate updates + +Many of the structs in the `datafusion-ffi` crate have been updated to allow easier +conversion to the underlying trait types they represent. This simplifies some code +paths, but also provides an additional improvement in cases where library code goes +through a round trip via the foreign function interface. + +To update your code, suppose you have a `FFI_SchemaProvider` called `ffi_provider` +and you wish to use this as a `SchemaProvider`. In the old approach you would do +something like: + +```rust,ignore + let foreign_provider: ForeignSchemaProvider = ffi_provider.into(); + let foreign_provider = Arc::new(foreign_provider) as Arc; +``` + +This code should now be written as: + +```rust,ignore + let foreign_provider: Arc = ffi_provider.into(); + let foreign_provider = foreign_provider as Arc; +``` + +For the case of user defined functions, the updates are similar but you +may need to change the way you call the creation of the `ScalarUDF`. +Aggregate and window functions follow the same pattern. + +Previously you may write: + +```rust,ignore + let foreign_udf: ForeignScalarUDF = ffi_udf.try_into()?; + let foreign_udf: ScalarUDF = foreign_udf.into(); +``` + +Instead this should now be: + +```rust,ignore + let foreign_udf: Arc = ffi_udf.into(); + let foreign_udf = ScalarUDF::new_from_shared_impl(foreign_udf); +``` + +When creating any of the following structs, we now require the user to +provide a `TaskContextProvider` and optionally a `LogicalExtensionCodec`: + +- `FFI_CatalogListProvider` +- `FFI_CatalogProvider` +- `FFI_SchemaProvider` +- `FFI_TableProvider` +- `FFI_TableFunction` + +Each of these structs has a `new()` and a `new_with_ffi_codec()` method for +instantiation. For example, when you previously would write + +```rust,ignore + let table = Arc::new(MyTableProvider::new()); + let ffi_table = FFI_TableProvider::new(table, None); +``` + +Now you will need to provide a `TaskContextProvider`. The most common +implementation of this trait is `SessionContext`. + +```rust,ignore + let ctx = Arc::new(SessionContext::default()); + let table = Arc::new(MyTableProvider::new()); + let ffi_table = FFI_TableProvider::new(table, None, ctx, None); +``` + +The alternative function to create these structures may be more convenient +if you are doing many of these operations. A `FFI_LogicalExtensionCodec` will +store the `TaskContextProvider` as well. + +```rust,ignore + let codec = Arc::new(DefaultLogicalExtensionCodec {}); + let ctx = Arc::new(SessionContext::default()); + let ffi_codec = FFI_LogicalExtensionCodec::new(codec, None, ctx); + let table = Arc::new(MyTableProvider::new()); + let ffi_table = FFI_TableProvider::new_with_ffi_codec(table, None, ffi_codec); +``` + +Additional information about the usage of the `TaskContextProvider` can be +found in the crate README. + +Additionally, the FFI structure for Scalar UDF's no longer contains a +`return_type` call. This code was not used since the `ForeignScalarUDF` +struct implements the `return_field_from_args` instead. + +### Projection handling moved from FileScanConfig to FileSource + +Projection handling has been moved from `FileScanConfig` into `FileSource` implementations. This enables format-specific projection pushdown (e.g., Parquet can push down struct field access, Vortex can push down computed expressions into un-decoded data). + +**Who is affected:** + +- Users who have implemented custom `FileSource` implementations +- Users who use `FileScanConfigBuilder::with_projection_indices` directly + +**Breaking changes:** + +1. **`FileSource::with_projection` replaced with `try_pushdown_projection`:** + + The `with_projection(&self, config: &FileScanConfig) -> Arc` method has been removed and replaced with `try_pushdown_projection(&self, projection: &ProjectionExprs) -> Result>>`. + +2. **`FileScanConfig.projection_exprs` field removed:** + + Projections are now stored in the `FileSource` directly, not in `FileScanConfig`. + Various public helper methods that access projection information have been removed from `FileScanConfig`. + +3. **`FileScanConfigBuilder::with_projection_indices` now returns `Result`:** + + This method can now fail if the projection pushdown fails. + +4. **`FileSource::create_file_opener` now returns `Result>`:** + + Previously returned `Arc` directly. + Any `FileSource` implementation that may fail to create a `FileOpener` should now return an appropriate error. + +5. **`DataSource::try_swapping_with_projection` signature changed:** + + Parameter changed from `&[ProjectionExpr]` to `&ProjectionExprs`. + +**Migration guide:** + +If you have a custom `FileSource` implementation: + +**Before:** + +```rust,ignore +impl FileSource for MyCustomSource { + fn with_projection(&self, config: &FileScanConfig) -> Arc { + // Apply projection from config + Arc::new(Self { /* ... */ }) + } + + fn create_file_opener( + &self, + object_store: Arc, + base_config: &FileScanConfig, + partition: usize, + ) -> Arc { + Arc::new(MyOpener { /* ... */ }) + } +} +``` + +**After:** + +```rust,ignore +impl FileSource for MyCustomSource { + fn try_pushdown_projection( + &self, + projection: &ProjectionExprs, + ) -> Result>> { + // Return None if projection cannot be pushed down + // Return Some(new_source) with projection applied if it can + Ok(Some(Arc::new(Self { + projection: Some(projection.clone()), + /* ... */ + }))) + } + + fn projection(&self) -> Option<&ProjectionExprs> { + self.projection.as_ref() + } + + fn create_file_opener( + &self, + object_store: Arc, + base_config: &FileScanConfig, + partition: usize, + ) -> Result> { + Ok(Arc::new(MyOpener { /* ... */ })) + } +} +``` + +We recommend you look at [#18627](https://github.com/apache/datafusion/pull/18627) +that introduced these changes for more examples for how this was handled for the various built in file sources. + +We have added [`SplitProjection`](https://docs.rs/datafusion-datasource/latest/datafusion_datasource/projection/struct.SplitProjection.html) and [`ProjectionOpener`](https://docs.rs/datafusion-datasource/latest/datafusion_datasource/projection/struct.ProjectionOpener.html) helpers to make it easier to handle projections in your `FileSource` implementations. + +For file sources that can only handle simple column selections (not computed expressions), use the `SplitProjection` and `ProjectionOpener` helpers to split the projection into pushdownable and non-pushdownable parts: + +```rust,ignore +use datafusion_datasource::projection::{SplitProjection, ProjectionOpener}; + +// In try_pushdown_projection: +let split = SplitProjection::new(projection, self.table_schema())?; +// Use split.file_projection() for what to push down to the file format +// The ProjectionOpener wrapper will handle the rest +``` + +**For `FileScanConfigBuilder` users:** + +```diff +let config = FileScanConfigBuilder::new(url, source) +- .with_projection_indices(Some(vec![0, 2, 3])) ++ .with_projection_indices(Some(vec![0, 2, 3]))? + .build(); +``` + +### `SchemaAdapter` and `SchemaAdapterFactory` completely removed + +Following the deprecation announced in [DataFusion 49.0.0](49.0.0.md#deprecating-schemaadapterfactory-and-schemaadapter), `SchemaAdapterFactory` has been fully removed from Parquet scanning. This applies to both: + +The following symbols have been deprecated and will be removed in the next release: + +- `SchemaAdapter` trait +- `SchemaAdapterFactory` trait +- `SchemaMapper` trait +- `SchemaMapping` struct +- `DefaultSchemaAdapterFactory` struct + +These types were previously used to adapt record batch schemas during file reading. +This functionality has been replaced by `PhysicalExprAdapterFactory`, which rewrites expressions at planning time rather than transforming batches at runtime. +If you were using a custom `SchemaAdapterFactory` for schema adaptation (e.g., default column values, type coercion), you should now implement `PhysicalExprAdapterFactory` instead. +See the [default column values example](https://github.com/apache/datafusion/blob/main/datafusion-examples/examples/custom_data_source/default_column_values.rs) for how to implement a custom `PhysicalExprAdapterFactory`. + +**Migration guide:** + +If you implemented a custom `SchemaAdapterFactory`, migrate to `PhysicalExprAdapterFactory`. +See the [default column values example](https://github.com/apache/datafusion/blob/main/datafusion-examples/examples/custom_data_source/default_column_values.rs) for a complete implementation. diff --git a/versions/55.0.0/_sources/library-user-guide/upgrading/53.0.0.md.txt b/versions/55.0.0/_sources/library-user-guide/upgrading/53.0.0.md.txt new file mode 100644 index 0000000000000..a619862e2a153 --- /dev/null +++ b/versions/55.0.0/_sources/library-user-guide/upgrading/53.0.0.md.txt @@ -0,0 +1,551 @@ + + +# Upgrade Guides + +## DataFusion 53.0.0 + +### Upgrade arrow/parquet to 58.0.0 and object_store to 0.13.0 + +DataFusion 53.0.0 uses `arrow` and `parquet` 58.0.0, and `object_store` 0.13.0. +This may require updates to your Cargo.toml if you have direct dependencies on +these crates. + +See the [Arrow 58.0.0 release notes] and the [object_store 0.13.0 upgrade guide] for details on breaking changes in those versions. + +[arrow 58.0.0 release notes]: https://github.com/apache/arrow-rs/releases/tag/58.0.0 +[object_store 0.13.0 upgrade guide]: https://github.com/apache/arrow-rs-object-store/blob/v0.13.0/CHANGELOG.md + +### `ExecutionPlan::statistics` removed + +The deprecated `ExecutionPlan::statistics()` method has been removed. If you +implement custom `ExecutionPlan`s, remove that method from your impl and +implement `partition_statistics()` instead. + +**Before:** + +```rust,ignore +impl ExecutionPlan for MyExec { + // ... + + fn statistics(&self) -> Result { + Ok(Statistics::new_unknown(&self.schema())) + } +} +``` + +**After:** + +```rust,ignore +impl ExecutionPlan for MyExec { + // ... + + fn partition_statistics(&self, _partition: Option) -> Result { + Ok(Statistics::new_unknown(&self.schema())) + } +} +``` + +If you do not have partition-specific statistics, return the same value for +`None` and for any partition index. + +### `ExecutionPlan::properties` now returns `&Arc` + +Now `ExecutionPlan::properties()` returns `&Arc` instead of a +reference. This make it possible to cheaply clone properties and reuse them across multiple +`ExecutionPlans`. It also makes it possible to optimize [`ExecutionPlan::with_new_children`] +to reuse properties when the children plans have not changed, which can significantly reduce +planning time for complex queries. + +[`ExecutionPlan::with_new_children`](https://docs.rs/datafusion/latest/datafusion/physical_plan/trait.ExecutionPlan.html#tymethod.with_new_children) + +To migrate, in all `ExecutionPlan` implementations, you will likely need to wrap +stored `PlanProperties` in an `Arc`: + +```diff +- cache: PlanProperties, ++ cache: Arc, + +... + +- fn properties(&self) -> &PlanProperties { ++ fn properties(&self) -> &Arc { + &self.cache + } +``` + +To improve performance of `with_new_children` for custom `ExecutionPlan` +implementations, you can use the new macro: `check_if_same_properties`. For it +to work, you need to implement the function: +`with_new_children_and_same_properties` with semantics identical to +`with_new_children`, but operating under the assumption that the properties of +the children plans have not changed. + +An example of supporting this optimization for `ProjectionExec`: + +```diff + impl ProjectionExec { ++ fn with_new_children_and_same_properties( ++ &self, ++ mut children: Vec>, ++ ) -> Self { ++ Self { ++ input: children.swap_remove(0), ++ metrics: ExecutionPlanMetricsSet::new(), ++ ..Self::clone(self) ++ } ++ } + } + + impl ExecutionPlan for ProjectionExec { + fn with_new_children( + self: Arc, + mut children: Vec>, + ) -> Result> { ++ check_if_same_properties!(self, children); + ProjectionExec::try_new( + self.projector.projection().into_iter().cloned(), + children.swap_remove(0), + ) + .map(|p| Arc::new(p) as _) + } + } +``` + +### `PlannerContext` outer query schema API now uses a stack + +`PlannerContext` no longer stores a single `outer_query_schema`. It now tracks a +stack of outer relation schemas so nested subqueries can access non-adjacent +outer relations. + +**Before:** + +```rust,ignore +let old_outer_query_schema = + planner_context.set_outer_query_schema(Some(input_schema.clone().into())); +let sub_plan = self.query_to_plan(subquery, planner_context)?; +planner_context.set_outer_query_schema(old_outer_query_schema); +``` + +**After:** + +```rust,ignore +planner_context.append_outer_query_schema(input_schema.clone().into()); +let sub_plan = self.query_to_plan(subquery, planner_context)?; +planner_context.pop_outer_query_schema(); +``` + +### `HashJoinExec::try_new` adds `null_aware` + +`HashJoinExec::try_new` now takes an extra `null_aware: bool` argument. This +flag is used for null-aware anti joins, such as plans generated for `NOT IN` +subqueries. + +Most callers should pass `false`, the previous behavior. Pass `true` only for null-aware +`JoinType::LeftAnti` joins. + +### `FileSinkConfig` adds `file_output_mode` + +`FileSinkConfig` now includes a `file_output_mode: FileOutputMode` field to control +single-file vs directory output behavior. Any code constructing `FileSinkConfig` via struct +literals must initialize this field. + +The `FileOutputMode` enum has three variants: + +- `Automatic` (default): Infer output mode from the URL (extension/trailing `/` heuristic) +- `SingleFile`: Write to a single file at the exact output path +- `Directory`: Write to a directory with generated filenames + +**Before:** + +```rust,ignore +FileSinkConfig { + // ... + file_extension: "parquet".into(), +} +``` + +**After:** + +```rust,ignore +use datafusion_datasource::file_sink_config::FileOutputMode; + +FileSinkConfig { + // ... + file_extension: "parquet".into(), + file_output_mode: FileOutputMode::Automatic, +} +``` + +### `SimplifyInfo` trait removed, `SimplifyContext` now uses builder-style API + +The `SimplifyInfo` trait has been removed and replaced with the concrete `SimplifyContext` struct. This simplifies the expression simplification API and removes the need for trait objects. + +**Who is affected:** + +- Users who implemented custom `SimplifyInfo` implementations +- Users who implemented `ScalarUDFImpl::simplify()` for custom scalar functions +- Users who directly use `SimplifyContext` or `ExprSimplifier` + +**Breaking changes:** + +1. The `SimplifyInfo` trait has been removed entirely +2. `SimplifyContext` no longer takes `&ExecutionProps` - it now uses a builder-style API with direct fields +3. `ScalarUDFImpl::simplify()` now takes `&SimplifyContext` instead of `&dyn SimplifyInfo` +4. Time-dependent function simplification (e.g., `now()`) is now optional - if `query_execution_start_time` is `None`, these functions won't be simplified + +**Migration guide:** + +If you implemented a custom `SimplifyInfo`: + +**Before:** + +```rust,ignore +impl SimplifyInfo for MySimplifyInfo { + fn is_boolean_type(&self, expr: &Expr) -> Result { ... } + fn nullable(&self, expr: &Expr) -> Result { ... } + fn execution_props(&self) -> &ExecutionProps { ... } + fn get_data_type(&self, expr: &Expr) -> Result { ... } +} +``` + +**After:** + +Use `SimplifyContext` directly with the builder-style API: + +```rust,ignore +let context = SimplifyContext::default() + .with_schema(schema) + .with_config_options(config_options) + .with_query_execution_start_time(Some(Utc::now())); // or use .with_current_time() +``` + +If you implemented `ScalarUDFImpl::simplify()`: + +**Before:** + +```rust,ignore +fn simplify( + &self, + args: Vec, + info: &dyn SimplifyInfo, +) -> Result { + let now_ts = info.execution_props().query_execution_start_time; + // ... +} +``` + +**After:** + +```rust,ignore +fn simplify( + &self, + args: Vec, + info: &SimplifyContext, +) -> Result { + // query_execution_start_time is now Option> + // Return Original if time is not set (simplification skipped) + let Some(now_ts) = info.query_execution_start_time() else { + return Ok(ExprSimplifyResult::Original(args)); + }; + // ... +} +``` + +If you created `SimplifyContext` from `ExecutionProps`: + +**Before:** + +```rust,ignore +let props = ExecutionProps::new(); +let context = SimplifyContext::new(&props).with_schema(schema); +``` + +**After:** + +```rust,ignore +let context = SimplifyContext::default() + .with_schema(schema) + .with_config_options(config_options) + .with_current_time(); // Sets query_execution_start_time to Utc::now() +``` + +See [`SimplifyContext` documentation](https://docs.rs/datafusion-expr/latest/datafusion_expr/simplify/struct.SimplifyContext.html) for more details. + +### Struct Casting Now Requires Field Name Overlap + +DataFusion's struct casting mechanism previously allowed casting between structs with differing field names if the field counts matched. This "positional fallback" behavior could silently misalign fields and cause data corruption. + +**Breaking Change:** + +Starting with DataFusion 53.0.0, struct casts now require **at least one overlapping field name** between the source and target structs. Casts without field name overlap are rejected at plan time with a clear error message. + +**Who is affected:** + +- Applications that cast between structs with no overlapping field names +- Queries that rely on positional struct field mapping (e.g., casting `struct(x, y)` to `struct(a, b)` based solely on position) +- Code that constructs or transforms struct columns programmatically + +**Migration guide:** + +If you encounter an error like: + +```text +Cannot cast struct with 2 fields to 2 fields because there is no field name overlap +``` + +You must explicitly rename or map fields to ensure at least one field name matches. Here are common patterns: + +**Example 1: Source and target field names already match (Name-based casting)** + +**Success case (field names align):** + +```sql +-- source_col has schema: STRUCT +-- Casting to the same field names succeeds (no-op or type validation only) +SELECT CAST(source_col AS STRUCT) FROM table1; +``` + +**Example 2: Source and target field names differ (Migration scenario)** + +**What fails now (no field name overlap):** + +```sql +-- source_col has schema: STRUCT +-- This FAILS because there is no field name overlap: +-- ❌ SELECT CAST(source_col AS STRUCT) FROM table1; +-- Error: Cannot cast struct with 2 fields to 2 fields because there is no field name overlap +``` + +**Migration options (must align names):** + +**Option A: Use struct constructor for explicit field mapping** + +```sql +-- source_col has schema: STRUCT +-- Use STRUCT_CONSTRUCT with explicit field names +SELECT STRUCT_CONSTRUCT( + 'x', source_col.a, + 'y', source_col.b +) AS renamed_struct FROM table1; +``` + +**Option B: Rename in the cast target to match source names** + +```sql +-- source_col has schema: STRUCT +-- Cast to target with matching field names +SELECT CAST(source_col AS STRUCT) FROM table1; +``` + +**Example 3: Using struct constructors in Rust API** + +If you need to map fields programmatically, build the target struct explicitly: + +```rust,ignore +// Build the target struct with explicit field names +let target_struct_type = DataType::Struct(vec![ + FieldRef::new("x", DataType::Int32), + FieldRef::new("y", DataType::Utf8), +]); + +// Use struct constructors rather than casting for field mapping +// This makes the field mapping explicit and unambiguous +// Use struct builders or row constructors that preserve your mapping logic +``` + +**Why this change:** + +1. **Safety:** Field names are now the primary contract for struct compatibility +2. **Explicitness:** Prevents silent data misalignment caused by positional assumptions +3. **Consistency:** Matches DuckDB's behavior and aligns with other SQL engines that enforce name-based matching +4. **Debuggability:** Errors now appear at plan time rather than as silent data corruption + +See [Issue #19841](https://github.com/apache/datafusion/issues/19841) and [PR #19955](https://github.com/apache/datafusion/pull/19955) for more details. + +### `FilterExec` builder methods deprecated + +The following methods on `FilterExec` have been deprecated in favor of using `FilterExecBuilder`: + +- `with_projection()` +- `with_batch_size()` + +**Who is affected:** + +- Users who create `FilterExec` instances and use these methods to configure them + +**Migration guide:** + +Use `FilterExecBuilder` instead of chaining method calls on `FilterExec`: + +**Before:** + +```rust,ignore +let filter = FilterExec::try_new(predicate, input)? + .with_projection(Some(vec![0, 2]))? + .with_batch_size(8192)?; +``` + +**After:** + +```rust,ignore +let filter = FilterExecBuilder::new(predicate, input) + .with_projection(Some(vec![0, 2])) + .with_batch_size(8192) + .build()?; +``` + +The builder pattern is more efficient as it computes properties once during `build()` rather than recomputing them for each method call. + +Note: `with_default_selectivity()` is not deprecated as it simply updates a field value and does not require the overhead of the builder pattern. + +### Protobuf conversion trait added + +A new trait, `PhysicalProtoConverterExtension`, has been added to the `datafusion-proto` +crate. This is used for controlling the process of conversion of physical plans and +expressions to and from their protobuf equivalents. The methods for conversion now +require an additional parameter. + +The primary APIs for interacting with this crate have not been modified, so most users +should not need to make any changes. If you do require this trait, you can use the +`DefaultPhysicalProtoConverter` implementation. + +For example, to convert a sort expression protobuf node you can make the following +updates: + +**Before:** + +```rust,ignore +let sort_expr = parse_physical_sort_expr( + sort_proto, + ctx, + input_schema, + codec, +); +``` + +**After:** + +```rust,ignore +let converter = DefaultPhysicalProtoConverter {}; +let sort_expr = parse_physical_sort_expr( + sort_proto, + ctx, + input_schema, + codec, + &converter +); +``` + +Similarly to convert from a physical sort expression into a protobuf node: + +**Before:** + +```rust,ignore +let sort_proto = serialize_physical_sort_expr( + sort_expr, + codec, +); +``` + +**After:** + +```rust,ignore +let converter = DefaultPhysicalProtoConverter {}; +let sort_proto = serialize_physical_sort_expr( + sort_expr, + codec, + &converter, +); +``` + +### `generate_series` and `range` table functions changed + +The `generate_series` and `range` table functions now return an empty set when the interval is invalid, instead of an error. +This behavior is consistent with systems like PostgreSQL. + +Before: + +```sql +> select * from generate_series(0, -1); +Error during planning: Start is bigger than end, but increment is positive: Cannot generate infinite series + +> select * from range(0, -1); +Error during planning: Start is bigger than end, but increment is positive: Cannot generate infinite series +``` + +Now: + +```sql +> select * from generate_series(0, -1); ++-------+ +| value | ++-------+ ++-------+ +0 row(s) fetched. + +> select * from range(0, -1); ++-------+ +| value | ++-------+ ++-------+ +0 row(s) fetched. +``` + +### `array_remove`, `array_remove_n`, `array_remove_all` now return NULL when the element argument is NULL + +Previously, calling `array_remove(array, NULL)` would attempt to match and +remove NULL entries from the array, returning the array with NULLs stripped. +Now, passing NULL as the element to remove causes the function to return NULL, +which is consistent with standard SQL NULL propagation semantics. + +The same change applies to `array_remove_n` (aliases: `list_remove_n`) and +`array_remove_all` (aliases: `list_remove_all`). + +**Who is affected:** + +- Queries that call `array_remove`, `array_remove_n`, or `array_remove_all` + with a NULL element argument (literal or column-derived). + +**Behavioral changes:** + +| Expression | Old result | New result | +| ---------------------------------------------------- | ----------------- | ---------- | +| `array_remove(make_array(1, NULL, 2), NULL)` | `[1, 2]` | `NULL` | +| `array_remove(make_array(1, NULL, 2, NULL), NULL)` | `[1, 2, NULL]` | `NULL` | +| `array_remove_n(make_array(1, 2, 2, 1, 1), NULL, 2)` | `[1, 2, 2, 1, 1]` | `NULL` | +| `array_remove_all(make_array(1, 2, 2, 1, 1), NULL)` | `[1, 2, 2, 1, 1]` | `NULL` | + +**Migration guide:** + +If your queries relied on the old behavior to strip NULLs from arrays, use +`array_remove_all` with a non-NULL sentinel or use `array_filter` instead: + +```sql +-- Before (removed NULLs from array): +SELECT array_remove(make_array(1, NULL, 2), NULL); +-- Old result: [1, 2] + +-- After (returns NULL due to NULL propagation): +SELECT array_remove(make_array(1, NULL, 2), NULL); +-- New result: NULL +``` + +See [#21011](https://github.com/apache/datafusion/issues/21011) and +[PR #21013](https://github.com/apache/datafusion/pull/21013) for details. diff --git a/versions/55.0.0/_sources/library-user-guide/upgrading/54.0.0.md.txt b/versions/55.0.0/_sources/library-user-guide/upgrading/54.0.0.md.txt new file mode 100644 index 0000000000000..f8e7ac93c08d8 --- /dev/null +++ b/versions/55.0.0/_sources/library-user-guide/upgrading/54.0.0.md.txt @@ -0,0 +1,935 @@ + + +# Upgrade Guides + +## DataFusion 54.0.0 + +### `AggregateFunctionExpr::human_display()` now returns `Option<&str>` + +`datafusion_physical_expr::aggregate::AggregateFunctionExpr::human_display()` +now returns `Option<&str>` instead of `&str`. + +If your code read the display text directly, handle the `None` case and fall +back to `name()` when needed: + +```rust +let display = agg_expr.human_display().unwrap_or(agg_expr.name()); +``` + +### Aggregate logical-to-physical lowering helpers are deprecated + +`create_aggregate_expr_with_name_and_maybe_filter` and +`create_aggregate_expr_and_maybe_filter` are deprecated. Use +`datafusion_physical_expr::aggregate::LoweredAggregateBuilder` for new code that +lowers a logical aggregate `Expr` into an `AggregateFunctionExpr`, filter, and +order-by expressions. + +For example: + +```rust +let lowered = LoweredAggregateBuilder::new( + expr, + logical_input_schema, + physical_input_schema, + execution_props, +) +.build()?; +``` + +`LoweredAggregateBuilder` returns a `LoweredAggregate` containing the aggregate +physical expression, optional filter, and order-by expressions. + +### `Expr::unalias_nested()` preserves aliases with metadata + +`Expr::unalias_nested()` no longer removes aliases that carry non-empty +`FieldMetadata`. This preserves user-provided output field metadata. Code that +needs to remove all aliases, including aliases with metadata, should unwrap +`Expr::Alias` explicitly. + +### Physical aggregate proto display may contain encoded alias data + +`PhysicalAggregateExprNode.human_display` may now contain an internal encoded +prefix when an aggregate display has a separate output alias. DataFusion decodes +this when reading physical plans. Older readers that do not know this encoding +may show the prefix text directly in diagnostics. + +### Physical `EXPLAIN` now shows lowered aggregate execution forms + +Physical `EXPLAIN` output is intended for diagnostics and may change between +DataFusion versions. This release changes aggregate expression formatting in +physical plans to show the lowered expression executed by the engine while +keeping the visible output alias. + +Examples: + +- `count(*)` may now appear as `count(1) as count(*)` +- simplified aggregates may show the lowered implementation, such as + `min(...) as percentile_cont(...)` +- internal aggregate aliases may now show the underlying expression instead of + only the alias name + +Tests or diagnostics that compare physical `EXPLAIN` output exactly may need +to update their expected strings. + +### String/numeric comparison coercion now prefers numeric types + +Previously, comparing a numeric column with a string value (e.g., +`WHERE int_col > '100'`) coerced both sides to strings and performed a +lexicographic comparison. This produced surprising results — for example, +`5 > '100'` yielded `true` because `'5' > '1'` lexicographically, even +though `5 > 100` is `false` numerically. + +DataFusion now coerces the string side to the numeric type in comparison +contexts (`=`, `<`, `>`, `<=`, `>=`, `<>`, `IN`, `BETWEEN`, `CASE .. WHEN`, +`GREATEST`, `LEAST`). For example, `5 > '100'` will now yield `false`. + +**Who is affected:** + +- Queries that compare numeric values with string values +- Queries that use `IN` lists with mixed string and numeric types +- Queries that use `CASE expr WHEN` with mixed string and numeric types +- Queries that use `GREATEST` or `LEAST` with mixed string and numeric types + +**Behavioral changes:** + +| Expression | Old behavior | New behavior | +| ----------------------- | ------------------------------- | ------------------------------------------ | +| `int_col > '100'` | Lexicographic | Numeric | +| `float_col = '5'` | String `'5' != '5.0'` | Numeric `5.0 = 5.0` | +| `int_col = 'hello'` | String comparison, always false | Cast error | +| `str_col IN ('a', 1)` | Coerce to Utf8 | Cast error (`'a'` cannot be cast to Int64) | +| `float_col IN ('1.0')` | String `'1.0' != '1'` | Numeric `1.0 = 1.0` (correct) | +| `CASE str_col WHEN 1.0` | Coerce to Utf8 | Coerce to Float64 | +| `GREATEST(10, '9')` | Utf8 `'9'` (lexicographic) | Int64 `10` (numeric) | +| `LEAST(10, '9')` | Utf8 `10` (lexicographic) | Int64 `9` (numeric) | + +**Migration guide:** + +Most queries will produce more correct results with no changes needed. +However, queries that relied on the old string-comparison behavior may need +adjustment: + +- **Queries comparing numeric columns with non-numeric strings** (e.g., + `int_col = 'hello'` or `int_col > text_col` where `text_col` contains + non-numeric values) will now produce a cast error instead of silently + returning no rows. +- **Mixed-type `IN` lists** (e.g., `str_col IN ('a', 1)`) are now rejected. Use + consistent types for the `IN` list or add an explicit `CAST`. +- **Queries comparing integer columns with non-integer numeric string literals** (e.g., + `int_col = '99.99'`) will now produce a cast error because `'99.99'` + cannot be cast to an integer. Use a float column or adjust the literal. + +See [#15161](https://github.com/apache/datafusion/issues/15161) and +[PR #20426](https://github.com/apache/datafusion/pull/20426) for details. + +### `CastColumnExpr` removed in favor of field-aware `CastExpr` + +`datafusion_physical_expr::expressions::CastColumnExpr` has been removed; use +the field-aware `datafusion_physical_expr::expressions::CastExpr` instead. + +If your code downcasted to `CastColumnExpr`, downcast to `CastExpr` instead and +use `CastExpr::target_field()` for the output field metadata and +`CastExpr::expr()` for the input expression. To construct casts with explicit +field semantics, use `CastExpr::new_with_target_field(...)`. The type-only +`CastExpr::new(...)` and `cast(...)` helpers remain available for callers that +only have a `DataType`. + +### `comparison_coercion_numeric` removed, replaced by `comparison_coercion` + +The `comparison_coercion_numeric` function has been removed. Its behavior +(preferring numeric types for string/numeric comparisons) is now the default in +`comparison_coercion`. A new function, `type_union_coercion`, handles contexts +where string types are preferred (`UNION`, `CASE THEN/ELSE`, `NVL2`). + +**Who is affected:** + +- Crates that call `comparison_coercion_numeric` directly +- Crates that call `comparison_coercion` and relied on its old + string-preferring behavior +- Crates that call `get_coerce_type_for_case_expression` + +### `ExecutionPlan::partition_statistics` now returns `Arc` + +`ExecutionPlan::partition_statistics` now returns `Result>` instead of `Result`. This avoids cloning `Statistics` when it is shared across multiple consumers. + +**Before:** + +```rust,ignore +fn partition_statistics(&self, partition: Option) -> Result { + Ok(Statistics::new_unknown(&self.schema())) +} +``` + +**After:** + +```rust,ignore +fn partition_statistics(&self, partition: Option) -> Result> { + Ok(Arc::new(Statistics::new_unknown(&self.schema()))) +} +``` + +If you need an owned `Statistics` value (e.g. to mutate it), use `Arc::unwrap_or_clone`: + +```rust,ignore +// If you previously consumed the Statistics directly: +let stats = plan.partition_statistics(None)?; +stats.column_statistics[0].min_value = ...; + +// Now unwrap the Arc first: +let mut stats = Arc::unwrap_or_clone(plan.partition_statistics(None)?); +stats.column_statistics[0].min_value = ...; +``` + +### Remove `as_any` from `PhysicalExpr`, `ScalarUDFImpl`, `AggregateUDFImpl`, `WindowUDFImpl`, `ExecutionPlan`, `TableProvider`, `SchemaProvider`, `CatalogProvider`, `CatalogProviderList`, `TableSource`, `FileSource`, `FileFormat`, `FileFormatFactory`, `DataSource`, and `DataSink` + +Now that we have a more recent minimum version of Rust, we can take advantage of +trait upcasting. This reduces the amount of boilerplate code that +users need to implement. In your implementations of the traits listed above, +you can simply remove the `as_any` function. For example: + +```diff + impl PhysicalExpr for MyExpr { +- fn as_any(&self) -> &dyn Any { +- self +- } +- + fn data_type(&self, input_schema: &Schema) -> Result { + ... + } + + ... + } +``` + +The same change applies to all of the above traits — simply delete the +`as_any` method from each implementation. + +If you have code that is downcasting, you can drop the `.as_any()` call +and use `downcast_ref` / `is` directly on the trait object: + +```diff +-let exec = plan.as_any().downcast_ref::().unwrap(); ++let exec = plan.downcast_ref::().unwrap(); +``` + +These methods work correctly whether the value is a bare reference or +behind an `Arc` (Rust auto-derefs through the `Arc`). + +> **Warning:** Do not cast an `Arc` directly to `&dyn Any`. +> Writing `(&plan as &dyn Any)` gives you an `Any` reference to the +> **`Arc` itself**, not the underlying trait object, so the downcast will +> always return `None`. Use the `downcast_ref` method above instead, or +> dereference through the `Arc` first with `plan.as_ref() as &dyn Any`. + +### `PruningStatistics::row_counts` no longer takes a `column` parameter + +The `row_counts` method on the `PruningStatistics` trait no longer takes a +`&Column` argument, since row counts are a container-level property (the same +for every column). + +**Before:** + +```rust,ignore +fn row_counts(&self, column: &Column) -> Option { + // ... +} +``` + +**After:** + +```rust,ignore +fn row_counts(&self) -> Option { + // ... +} +``` + +**Who is affected:** + +- Users who implement the `PruningStatistics` trait + +**Migration guide:** + +Remove the `column: &Column` parameter from your `row_counts` implementation +and any corresponding call sites. If your implementation was using the column +argument, note that row counts are identical for all columns in a container, so +the parameter was unnecessary. + +See [PR #21369](https://github.com/apache/datafusion/pull/21369) for details. + +### Avro API and timestamp decoding changes + +DataFusion has switched to use `arrow-avro` (see [#17861]) when reading avro files +which results in a few changes: + +- `DataFusionError::AvroError` has been removed. +- `From for DataFusionError` has been removed. +- Avro crate re-export changed: + - Before: `datafusion::apache_avro` + - After: `datafusion::arrow_avro` +- **Avro timestamp logical type interpretation changed.** Notable effects: + - Avro `timestamp-*` logical types are read as UTC timezone-aware Arrow + timestamps (`Timestamp(..., Some("+00:00"))`) + - Avro `local-timestamp-*` logical types remain timezone-naive + (`Timestamp(..., None)`) + +**Who is affected:** + +- Users matching on `DataFusionError::AvroError` +- Users importing `datafusion::apache_avro` +- Users relying on previous Avro timestamp logical type behavior + +**Migration guide:** + +- Replace `datafusion::apache_avro` imports with `datafusion::arrow_avro`. +- Update error handling code that matches on `DataFusionError::AvroError` to use + the current error surface. +- Validate timestamp handling where timezone semantics matter: + `timestamp-*` is UTC timezone-aware, while `local-timestamp-*` is + timezone-naive. + +### `lpad`, `rpad`, and `translate` now operate on Unicode codepoints instead of grapheme clusters + +Previously, `lpad`, `rpad`, and `translate` used Unicode grapheme cluster +segmentation to measure and manipulate strings. They now use Unicode codepoints, +which is consistent with the SQL standard and most other SQL implementations. It +also matches the behavior of other string-related functions in DataFusion. + +The difference is only observable for strings containing combining characters +(e.g., U+0301 COMBINING ACUTE ACCENT) or other multi-codepoint grapheme +clusters (e.g., ZWJ emoji sequences). For ASCII and most common Unicode text, +behavior is unchanged. + +### Scalar subquery execution changes + +Uncorrelated scalar subqueries (e.g. `SELECT ... WHERE x > (SELECT max(v) FROM t)`) +are now executed by a dedicated physical operator rather than being rewritten to +a join. Correlated scalar subqueries are unchanged. + +This produces two user-visible changes: + +- **Subqueries that return multiple rows now fail at runtime.** An uncorrelated + scalar subquery that returns more than one row fails with `Execution error: Scalar subquery returned more than one row`. This matches the SQL standard and + the behavior of most other SQL implementations. The previous join-based + rewrite could silently produce multi-row output. Add a `LIMIT 1` or an + aggregate to the subquery to fix such queries. +- **Plan shape changes.** Uncorrelated `Expr::ScalarSubquery` nodes now survive + into the final logical plan instead of being replaced by a join; the + corresponding physical plan contains a new `ScalarSubqueryExec` node and a + `ScalarSubqueryExpr` expression. Code that walks or transforms `LogicalPlan` / + `ExecutionPlan` trees, as well as `EXPLAIN` output, may need updating. + +### Filter predicate evaluation order may differ from query text + +The logical optimizer now reorders filters so that cheap predicates (most binary +comparisons, `IS NULL`, `Between`, `InList`, etc.) evaluate before expensive +ones (`LIKE`, regex, scalar function calls, subqueries). For example, +`WHERE col LIKE '%foo%' AND col2 = 5` may evaluate `col2 = 5` before +`col LIKE '%foo%'`. + +**Evaluation order has never been guaranteed to match the order written in the +query.** The SQL standard explicitly allows implementations to evaluate operands +in any order; major engines (PostgreSQL, SQL Server, Oracle, MySQL) document the +same. Queries should not rely on left-to-right evaluation or short-circuit +semantics for `AND` or `OR`. Previous versions of DataFusion already reordered +predicates (e.g., as part of expression simplification or predicate pushdown); +the new reordering pass just increases the scenarios where the optimizer will +change predicate evaluation order. + +**Fallible-predicate patterns are particularly affected.** For example: + +```sql +WHERE s ~ '^[0-9]+$' AND CAST(s AS INT) > 0 +``` + +The intent is likely to filter non-numeric strings before the `CAST` runs, +but this depends on evaluation-order behavior the SQL standard does not +guaranteed. The new reorder makes this kind of pattern more likely to fail +at runtime if the optimizer moves the `CAST` ahead of the regex. To force +conditional evaluation, rewrite using `CASE`, which has standardized +short-circuit semantics: + +```sql +WHERE CASE WHEN s ~ '^[0-9]+$' THEN CAST(s AS INT) > 0 ELSE false END +``` + +Volatile expressions (`random()`, `now()`, etc.) are exempt — their position +in the conjunct list is preserved so the number of times they evaluate per +query does not change. + +### `datafusion-proto`: expression deserialization now takes a `TaskContext` + +`Serializeable::from_bytes_with_registry` is renamed to `from_bytes_with_ctx` +and takes a `&TaskContext` instead of a `&dyn FunctionRegistry`. `parse_expr`, +`parse_exprs`, and `parse_sorts` take the same change. `Expr::from_bytes` +(without a registry argument) is unchanged. + +```diff +-let expr = Expr::from_bytes_with_registry(&bytes, ®istry)?; ++let expr = Expr::from_bytes_with_ctx(&bytes, ctx.task_ctx().as_ref())?; +``` + +```diff +-let expr = parse_expr(&proto, ®istry, &codec)?; ++let expr = parse_expr(&proto, ctx.task_ctx().as_ref(), &codec)?; +``` + +### `datafusion-proto`: `PhysicalProtoConverterExtension` reshaped + +`PhysicalProtoConverterExtension` and the `parse_physical_*_with_converter` +helpers now take a single `&PhysicalPlanDecodeContext<'_>` that bundles the +`TaskContext` and the `PhysicalExtensionCodec`. Implementations update like +this: + +```diff + impl PhysicalProtoConverterExtension for MyConverter { + fn proto_to_execution_plan( + &self, +- ctx: &TaskContext, +- codec: &dyn PhysicalExtensionCodec, + proto: &protobuf::PhysicalPlanNode, ++ ctx: &PhysicalPlanDecodeContext<'_>, + ) -> Result> { +- proto.try_into_physical_plan_with_converter(ctx, codec, self) ++ self.default_proto_to_execution_plan(proto, ctx) + } + + fn proto_to_physical_expr( + &self, + proto: &PhysicalExprNode, +- ctx: &TaskContext, + input_schema: &Schema, +- codec: &dyn PhysicalExtensionCodec, ++ ctx: &PhysicalPlanDecodeContext<'_>, + ) -> Result> { +- parse_physical_expr_with_converter(proto, ctx, input_schema, codec, self) ++ self.default_proto_to_physical_expr(proto, input_schema, ctx) + } + } +``` + +Pull out the `TaskContext` or codec inside these methods with +`ctx.task_ctx()` and `ctx.codec()`. Construct a fresh context at an API +boundary with `PhysicalPlanDecodeContext::new(task_ctx, codec)`. + +### `ExecutionProps` has new fields + +`ExecutionProps` gained new public fields. Code that constructs it via a +struct literal, or pattern-matches it without `..`, no longer compiles. Use +`ExecutionProps::new()` and include `..` in exhaustive patterns. + +### Items in `datafusion_functions::strings` are no longer public + +`StringArrayBuilder`, `LargeStringArrayBuilder`, `StringViewArrayBuilder`, +`ColumnarValueRef`, and `append_view` have been reduced to `pub(crate)`. They +were only ever used to implement `concat` and `concat_ws` inside the crate. If +you were importing them externally, use Arrow's corresponding builders with a +caller-computed `NullBuffer`. + +[#17861]: https://github.com/apache/datafusion/pull/17861 + +### Conversion from `FileDecryptionProperties` to `ConfigFileDecryptionProperties` is now fallible + +Previously, `datafusion_common::config::ConfigFileDecryptionProperties` +implemented `From<&Arc>`. +If an error was encountered when retrieving the footer key without providing key metadata, +the error would be ignored and an empty footer key set in the result. +This could lead to obscure errors later. + +`ConfigFileDecryptionProperties` now instead implements `TryFrom<&Arc>`, +and errors retrieving the footer key will be propagated up. + +**Migration guide:** + +Replace calls to `ConfigFileDecryptionProperties::from` with `ConfigFileDecryptionProperties::try_from`, +and affected calls to `into` with `try_into`, with appropriate error handling added. + +**Before:** + +```rust,ignore +let config_decryption_properties: ConfigFileDecryptionProperties = (&decryption_properties).into(); +// or +let config_decryption_properties = ConfigFileDecryptionProperties::from(&decryption_properties); +``` + +(where `decryption_properties` is an `Arc`) + +**After:** + +```rust,ignore +let config_decryption_properties: ConfigFileDecryptionProperties = (&decryption_properties).try_into()?; +// or +let config_decryption_properties = ConfigFileDecryptionProperties::try_from(&decryption_properties)?; +``` + +See [#21602](https://github.com/apache/datafusion/issues/21602) and +[PR #21603](https://github.com/apache/datafusion/pull/21603) for details. + +### Conversion from `ConfigFileEncryptionProperties` / `ConfigFileDecryptionProperties` is now fallible + +Previously, `datafusion_common::config::ConfigFileEncryptionProperties` and +`datafusion_common::config::ConfigFileDecryptionProperties` implemented infallible +conversions into Parquet's encryption/decryption types (via `From` / `Into`). +These conversions may need to decode hex-encoded keys and other configuration values, which can fail. + +They now use `TryFrom` / `TryInto` and return a `Result`: + +- `impl TryFrom for parquet::encryption::encrypt::FileEncryptionProperties` +- `impl TryFrom for parquet::encryption::decrypt::FileDecryptionProperties` + +**Migration guide:** + +Replace `from()` / `into()` with `try_from()` / `try_into()` and handle the resulting `Result`. + +**Before:** + +```rust,ignore +let file_encryption_properties: FileEncryptionProperties = config_encryption_properties.into(); +// or +let file_decryption_properties = FileDecryptionProperties::from(config_decryption_properties); +``` + +( +where `config_encryption_properties` is a `ConfigFileEncryptionProperties` and +`config_decryption_properties` is a `ConfigFileDecryptionProperties` +) + +**After:** + +```rust,ignore +let file_encryption_properties: FileEncryptionProperties = + config_encryption_properties.try_into()?; +// or +let file_decryption_properties = + FileDecryptionProperties::try_from(config_decryption_properties)?; +``` + +See [#21974](https://github.com/apache/datafusion/issues/21974) and +[PR #21985](https://github.com/apache/datafusion/pull/21985) for details. + +### `approx_percentile_cont`, `approx_percentile_cont_with_weight`, `approx_median` now coerce to floats + +The type signatures of `approx_percentile_cont`, `approx_percentile_cont_with_weight`, and +`approx_median` now coerce integer input values to `Float64` before computing the approximation. +As a result, these functions always return a float, even when the input column is an integer type. + +**Who is affected:** + +- Queries or downstream code that relied on `approx_percentile_cont` / `approx_percentile_cont_with_weight` / + `approx_median` returning an integer type when given an integer column. + +**Migration guide:** + +If downstream code checks or relies on the return type being an integer, add an explicit +`CAST` back to the desired integer type, or update the type assertion: + +```sql +-- Before (returned Int64): +SELECT approx_percentile_cont(quantity, 0.5) FROM orders; + +-- After (returns Float64); cast if an integer result is required: +SELECT CAST(approx_percentile_cont(quantity, 0.5) AS BIGINT) FROM orders; +``` + +[#21074]: https://github.com/apache/datafusion/pull/21074 + +### `PartitionedFile::extensions` is now a type-keyed map + +`PartitionedFile.extensions` previously held a single +`Option>` slot, so two independent components +could not both attach data to the same file without colliding. The field +is now a `FileExtensions` (a re-export of +`datafusion_common::extensions::Extensions`), a map keyed by concrete Rust +type. Each type occupies its own slot, so multiple consumers (e.g. a +`ParquetAccessPlan` and a custom index entry) can coexist on a single +`PartitionedFile`. + +The previous `with_extensions(Arc)` builder is +deprecated (it still works, keyed by the value's dynamic `TypeId`) in +favor of a typed variant: + +```diff +-let pf = PartitionedFile::new(path, size) +- .with_extensions(Arc::new(access_plan)); ++let pf = PartitionedFile::new(path, size) ++ .with_extension(access_plan); +``` + +Reading an extension no longer requires a manual downcast: + +```diff +-let access_plan = partitioned_file +- .extensions +- .as_ref() +- .and_then(|ext| ext.downcast_ref::()); ++let access_plan = partitioned_file.extension::(); +``` + +The `FileExtensions` API is `insert` / `insert_arc` / `get` / `get_arc` +/ `contains` / `merge`, all generic over the concrete type `T`. Values +are stored as `Arc` so the map remains cheap to clone. + +**Who is affected:** + +- Code that constructs `PartitionedFile` and calls `.with_extensions(...)`. +- Custom `ParquetFileReaderFactory` implementations or other consumers that + read `partitioned_file.extensions` and downcast manually. + +### `arrays_zip` struct field names changed + +The `arrays_zip` (and its alias `list_zip`) scalar function now names its +output struct fields `"1"`, `"2"`, ..., `"n"` (1-indexed, matching DuckDB and +Spark) instead of `c0`, `c1`, ..., `c{n-1}`. + +**Who is affected:** + +- Queries or downstream code that references the output struct fields by name + (e.g. `arrays_zip(a, b)[1]['c0']`). Update field accessors to `'1'`, `'2'`, + etc. (e.g. `arrays_zip(a, b)[1]['1']`). + +See [PR #20886](https://github.com/apache/datafusion/pull/20886) for details. + +### `Box` and `Arc` `TreeNodeContainer` impls now require `C: Default` + +The generic `TreeNodeContainer` implementations for `Box` and `Arc` now +require `C: Default`. This change was necessary as part of optimizing tree +rewriting to reduce heap allocations. + +**Who is affected:** + +- Users that implement `TreeNodeContainer` on a custom type and wrap it in + `Box` or `Arc` when walking trees. + +**Migration guide:** + +Add a `Default` implementation to your type. The default value is used as a +temporary placeholder during query optimization, so when possible, pick a cheap, +allocation-free variant: + +```rust,ignore +impl Default for MyTreeNode { + fn default() -> Self { + MyTreeNode::Leaf // or whichever variant is cheapest to construct + } +} +``` + +### `MemoryPool` now requires `'static` (adds `Any` as a supertrait) + +To enable downcasting of `dyn MemoryPool` to concrete pool types (via +`is::()` / `downcast_ref::()`), the `MemoryPool` trait now has `Any` +as a supertrait: + +```rust,ignore +// Before +pub trait MemoryPool: Send + Sync + std::fmt::Debug + Display { ... } + +// After +pub trait MemoryPool: Any + Send + Sync + std::fmt::Debug + Display { ... } +``` + +Because `Any` is only implemented for `'static` types, this implicitly adds a +`'static` bound to every `MemoryPool` implementor. + +**Who is affected:** + +- Users who implement a custom `MemoryPool` whose type carries a lifetime + parameter or borrows state (e.g. `struct MyPool<'a> { inner: &'a State }`). + Existing implementations that are already `'static` (the common case) need + no changes. + +**Migration guide:** + +Replace borrowed references with owned handles so the pool type becomes +`'static`. The typical fix is to swap `&'a T` for `Arc` (or `Rc`, or an +owned value): + +```rust,ignore +// Before — not 'static, no longer compiles +struct MyPool<'a> { + inner: &'a SomeState, +} + +impl<'a> MemoryPool for MyPool<'a> { ... } + +// After — owned handle makes MyPool: 'static +struct MyPool { + inner: Arc, +} + +impl MemoryPool for MyPool { ... } +``` + +If the borrowed state truly cannot be made `'static`, you can wrap the +borrowed pool in a `'static` adapter that the pool consumer owns — for +example, store the underlying state in an `Arc` owned by the adapter, or +move the borrow behind an interior-mutability primitive such as `Arc>` +or `Arc>`. + +See [PR #21803](https://github.com/apache/datafusion/pull/21803) for details. + +[20047]: https://github.com/apache/datafusion/pull/20047 + +### File statistics cache is now memory-limited and managed by the `CacheManager` + +The file statistics cache used by `ListingTable` is now memory-limited and +centrally managed through the `CacheManager`. + +To configure the cache size use the `file_statistics_cache_limit` setting: + +```sql +SET datafusion.runtime.file_statistics_cache_limit = '10M' +``` + +To disable the file statistics cache, set the limit to 0. + +The file statistics cache is no longer created inside the `ListingTable`. +Instead, it is created within the `CacheManager` and must be passed to the `ListingTable`. + +**Who is affected:** + +- Users who want to limit the memory usage of the file statistics cache. +- Users who want to disable the file statistics cache. +- Users who want to create a `ListingTable` programmatically with a file statistics cache. + +**Migration guide:** + +Disable the cache by setting the configuration value to 0: + +```sql +SET datafusion.runtime.file_statistics_cache_limit = '0' +``` + +Use the file statistics cache provided by the `CacheManager` when initializing a new `ListingTable`: + +```rust,ignore +ListingTable::try_new(config)? + .with_cache(ctx.runtime_env().cache_manager.get_file_statistic_cache()) +``` + +### `UnionsToFilter` optimizer rule is now disabled by default + +The `datafusion.optimizer.enable_unions_to_filter` option now defaults to +`false`. When enabled, the rule rewrites `UNION DISTINCT` branches that read the +same source and differ only by filter predicates into a single scan with a +combined `OR` predicate: + +```sql +-- Before: two separate scans +SELECT * FROM t WHERE a = 1 +UNION +SELECT * FROM t WHERE a = 2 + +-- After: one scan +SELECT DISTINCT * FROM t WHERE a = 1 OR a = 2 +``` + +**Who is affected:** + +- Queries using `UNION` against the same table with different filter + conditions may benefit from enabling this rule. + +**Migration guide:** + +Enable the rule when your `UNION` queries scan the same large table +multiple times with different predicates. Avoid it when the data source handles individual equality predicates more efficiently than +a combined `OR` (e.g., index-backed sources). + +```sql +SET datafusion.optimizer.enable_unions_to_filter = true; +``` + +See [PR #21075](https://github.com/apache/datafusion/pull/21075) for more details + +### Higher-order functions and lambdas + +The changes below are related to the added support for higher-order functions +and lambdas. For more info, see [#14205], [PR #18921], [PR #21679] and +[EPIC #21172]. + +[#14205]: https://github.com/apache/datafusion/issues/14205 +[pr #18921]: https://github.com/apache/datafusion/pull/18921 +[pr #21679]: https://github.com/apache/datafusion/pull/21679 +[epic #21172]: https://github.com/apache/datafusion/issues/21172 + +#### `FunctionRegistry` exposes two additional methods + +`FunctionRegistry` exposes two additional methods, `higher_order_function` +which returns the registered higher-order function with the given name, if +any, and `higher_order_function_names` which exposes the set of registered +user defined higher-order function names. + +**Who is affected:** + +- Users who implement the `FunctionRegistry` trait + +**Migration guide:** + +Add `higher_order_function` and `higher_order_function_names` to your implementation. + +```diff +impl FunctionRegistry for FunctionRegistryImpl { + fn udfs(&self) -> HashSet { + self.scalar_functions.keys().cloned().collect() + } ++ ++ fn higher_order_function(&self, name: &str) -> Result> { ++ self.higher_order_functions ++ .get(name) ++ .cloned() ++ .ok_or_else(|| plan_datafusion_err!("Higher-order function {name} not found")) ++ } ++ ++ fn higher_order_function_names(&self) -> HashSet { ++ self.higher_order_functions.keys().cloned().collect() ++ } +} +``` + +#### `ContextProvider` exposes two additional methods + +`ContextProvider` exposes two additional methods, `get_higher_order_meta` +which returns the registered higher-order function with the given name, +if any, and `higher_order_function_names` which exposes the registered +user defined higher-order function names. + +**Who is affected:** + +- Users who implement the `ContextProvider` trait + +**Migration guide:** + +Add `get_higher_order_meta` and `higher_order_function_names` to your implementation. + +```diff +impl ContextProvider for ContextProviderImpl { + fn udfs(&self) -> HashSet { + self.scalar_functions.keys().cloned().collect() + } ++ ++ fn get_higher_order_meta(&self, name: &str) -> Option> { ++ self.higher_order_functions.get(name).cloned() ++ } ++ ++ fn higher_order_function_names(&self) -> Vec { ++ self.higher_order_functions.keys().cloned().collect() ++ } +} +``` + +#### Add `higher_order_functions()` method to `Session` + +The `higher_order_functions` method has been added to the `Session` trait, +which exposes the registered user defined higher-order functions. + +**Who is affected:** + +- Users who implement the `Session` trait + +**Migration guide:** + +Add `higher_order_functions` to your implementation. + +```diff +impl Session for MySession { + ... ++ fn higher_order_functions(&self) -> &HashMap> { ++ &self.higher_order_functions ++ } +} +``` + +#### New argument on `TaskContext::new` + +`TaskContext::new` expects a new argument, `higher_order_functions`, which is +a map of higher-order functions keyed by name. + +**Who is affected:** + +- Users who call `TaskContext::new` + +**Migration guide:** + +Provide the new argument to the function. An empty hash map is sufficient. + +```diff ++let higher_order_functions = HashMap::new(); + +TaskContext::new( + task_id, + session_id, + session_config, + scalar_functions, ++ higher_order_functions, + aggregate_functions, + window_functions, + runtime, +) +``` + +#### The `Expr` enum has three new variants + +- `HigherOrderFunction`: Call a higher-order function with a set of arguments +- `Lambda`: A Lambda expression with a set of parameters names and a body +- `LambdaVariable`: A named reference to a lambda parameter + +**Who is affected:** + +- Users who match on an `Expr` without a default branch `_ => {}` + +**Migration guide:** + +Add the new branches to the match with the logic applicable to the context. + +```diff +match expr { + Expr::Column(column) => ..., + ..., ++ Expr::HigherOrderFunction(func) => {}, ++ Expr::Lambda(lambda) => {}, ++ Expr::LambdaVariable(lambda_var) => {}, +``` + +#### The `RegisterFunction` enum has a new `HigherOrder` variant + +`RegisterFunction` now has a `HigherOrder(Arc)` variant +so user-defined higher-order functions can be registered + +**Who is affected:** + +- Users who match on a `RegisterFunction` without a default branch `_ => {}` + +**Migration guide:** + +Add the new branch to the match with the logic applicable to the context. + +```diff +match register_function { + RegisterFunction::Scalar(scalar) => {}, + RegisterFunction::Aggregate(aggregate) => {}, + RegisterFunction::Window(window) => {}, ++ RegisterFunction::HigherOrder(higher_order) => {}, + RegisterFunction::Table(name, table) => {}, +} +``` + +### New `Dialect::Spark` variant + +The `Dialect` enum in `datafusion_common::config` now includes a `Spark` variant. +If you match exhaustively on `Dialect`, add a `Dialect::Spark` arm. diff --git a/versions/55.0.0/_sources/library-user-guide/upgrading/55.0.0.md.txt b/versions/55.0.0/_sources/library-user-guide/upgrading/55.0.0.md.txt new file mode 100644 index 0000000000000..d64f287ea0b52 --- /dev/null +++ b/versions/55.0.0/_sources/library-user-guide/upgrading/55.0.0.md.txt @@ -0,0 +1,1293 @@ + + +# Upgrade Guides + +## DataFusion 55.0.0 + +**Note:** DataFusion `55.0.0` has not been released yet. The information provided +in this section pertains to features and changes that have already been merged +to the main branch and are awaiting release in this version. + +### `DataFrame::fill_null` now borrows its arguments + +`DataFrame::fill_null` previously took its arguments by value: + +```rust,ignore +// Before +pub fn fill_null( + &self, + value: ScalarValue, + columns: Vec, +) -> Result +``` + +It now borrows them, matching the signature of the newly added +`DataFrame::fill_nan`: + +```rust,ignore +// After +pub fn fill_null( + &self, + value: &ScalarValue, + columns: &[&str], +) -> Result +``` + +This lets callers pass a borrowed `ScalarValue` and slice literals (or +`&str` column names) without first allocating owned `String`s. + +**Migration guide:** + +Borrow the value and pass a slice of `&str` instead of an owned `Vec`: + +```rust,ignore +// Before +let df = df.fill_null(ScalarValue::from(0), vec!["a".to_owned(), "c".to_owned()])?; +let df = df.fill_null(ScalarValue::from(0), vec![])?; + +// After +let df = df.fill_null(&ScalarValue::from(0), &["a", "c"])?; +let df = df.fill_null(&ScalarValue::from(0), &[])?; +``` + +### `FileScanConfig::partitioned_by_file_group` removed + +`FileScanConfig::partitioned_by_file_group` and +`FileScanConfigBuilder::with_partitioned_by_file_group(...)` have been removed. +Use `FileScanConfig::output_partitioning` and +`FileScanConfigBuilder::with_output_partitioning(...)` instead. +The corresponding +`datafusion_proto::protobuf::FileScanExecConf::partitioned_by_file_group` +field has also been removed. + +**Who is affected:** + +- Users who accessed `FileScanConfig::partitioned_by_file_group` directly. +- Users who called + `FileScanConfigBuilder::with_partitioned_by_file_group(true)`. +- Users who constructed or accessed + `datafusion_proto::protobuf::FileScanExecConf::partitioned_by_file_group`. + +**Migration guide:** + +If your file groups are organized by table partition column values, declare hash +output partitioning over those partition columns: + +```rust,ignore +use datafusion_datasource::file_scan_config::{ + FileScanConfigBuilder, output_partitioning_from_partition_fields, +}; + +let output_partitioning = output_partitioning_from_partition_fields( + source.table_schema().table_schema(), + source.table_schema().table_partition_cols(), + file_groups.len(), +); + +let config = FileScanConfigBuilder::new(object_store_url, source) + .with_file_groups(file_groups) + .with_output_partitioning(output_partitioning) + .build(); +``` + +`output_partitioning_from_partition_fields` returns +`Some(Partitioning::Hash(...))` when partition columns are present and `None` +otherwise. If you construct the partitioning manually, pass +`Some(Partitioning::Hash(partition_exprs, partition_count))` to +`with_output_partitioning(...)`. + +When constructing `FileScanExecConf`, omit `partitioned_by_file_group` and set +`output_partitioning` instead. + +### User `SpillFile` traits instead of [`RefCountedTempFile`] + +Spill file APIs now use the `datafusion_execution::SpillFile` trait instead of +the concrete [`RefCountedTempFile`] type. [`DiskManager::create_tmp_file`] now +returns `Arc`. +This change was introduced in [PR #21882], which adds pluggable spill file +backends via `SpillFile` and `TempFileFactory`. + +If your code matched on [`DiskManagerMode`], add a `DiskManagerMode::Custom(_)` +arm. + +If your code wrote directly to a [`RefCountedTempFile`] or called +[`RefCountedTempFile::update_disk_usage`], open a spill writer instead: + +```diff +- temp_file.inner().as_file().write_all(bytes)?; +- temp_file.update_disk_usage()?; ++ temp_file.open_writer()?.write_all(bytes)?; +``` + +Use `temp_file.size()` instead of [`RefCountedTempFile::current_disk_usage`]. + +[`diskmanager::create_tmp_file`]: https://docs.rs/datafusion-execution/latest/datafusion_execution/disk_manager/struct.DiskManager.html#method.create_tmp_file +[`diskmanagermode`]: https://docs.rs/datafusion-execution/latest/datafusion_execution/disk_manager/enum.DiskManagerMode.html +[`pr #21882`]: https://github.com/apache/datafusion/pull/21882 +[`refcountedtempfile`]: https://docs.rs/datafusion-execution/latest/datafusion_execution/disk_manager/struct.RefCountedTempFile.html +[`refcountedtempfile::current_disk_usage`]: https://docs.rs/datafusion-execution/latest/datafusion_execution/disk_manager/struct.RefCountedTempFile.html#method.current_disk_usage +[`refcountedtempfile::update_disk_usage`]: https://docs.rs/datafusion-execution/latest/datafusion_execution/disk_manager/struct.RefCountedTempFile.html#method.update_disk_usage + +### `Dialect::AVAILABLE` replaced by `Dialect::available()` + +`datafusion_common::config::Dialect::AVAILABLE` has been removed. Use +`Dialect::available()` instead. + +### `spill_record_batch_by_size` removed + +`datafusion_physical_plan::spill::spill_record_batch_by_size` has been removed. +This function was deprecated in DataFusion `46.0.0`. + +Use `datafusion_physical_plan::spill::SpillManager::spill_record_batch_by_size` +instead. + +### `CreateExternalTable` supports multiple locations + +`CREATE EXTERNAL TABLE` now accepts multiple paths in a single `LOCATION` +clause, which are read together as one table: + +```sql +CREATE EXTERNAL TABLE hits +STORED AS PARQUET +LOCATION ('file_1.parquet', 'file_2.parquet'); +``` + +To support this, the `location` field of both +`datafusion_expr::CreateExternalTable` and +`datafusion_sql::parser::CreateExternalTable` changed from a `String` to a +`Vec` named `locations`: + +```rust +// Before (54.0.0) +let location: String = create_external_table.location; + +// After (55.0.0) +let locations: Vec = create_external_table.locations; +``` + +The `CreateExternalTable::builder(name, location, file_type, schema)` +constructor is unchanged and still takes a single location; use the new +`CreateExternalTableBuilder::with_locations(Vec)` to set more than one. +All listed locations must resolve to the same schema and reside on the same +object store. A plain string literal remains a single location, so paths that +contain commas continue to work, for example `LOCATION 'path/with,comma.csv'`. + +### Decimal scalar formatting uses human-readable values + +Decimal scalar literals in `EXPLAIN` output, expression display strings, and +auto-generated column names now format the decimal value using its scale while +still showing the precision and scale. For example, a `Decimal128` literal with +stored value `1`, precision `1`, and scale `1` is now rendered as +`Decimal128(0.1,1,1)` instead of `Decimal128(Some(1),1,1)`. When formatting a +`ScalarValue` directly, it now appears as `0.1` instead of `Some(1),1,1`. + +`NULL` decimal literals were previously shown as `Decimal128(None,10,2)`; they +will now appear as `Decimal128(NULL,10,2)`. + +Query result values already used human-readable decimal formatting and are +unchanged. + +### `Coercion` supports dictionary encoding preservation + +`datafusion_expr_common::signature::Coercion` now supports optional dictionary +encoding preservation. Typed coercions materialize dictionary inputs by +default, including both `TypeSignatureClass::Native(...)` and broader classes +such as `Integer`, `Numeric`, and `Binary`. When preservation is enabled, +DataFusion instead coerces dictionary inputs to +`Dictionary(original_key_type, coerced_value_type)` instead of materializing them +to the coerced value type. + +User-defined functions can opt in by setting dictionary encoding preservation on +the relevant coercion: + +```rust +Coercion::new_exact(TypeSignatureClass::Native(logical_string())) + .with_encoding_preservation(EncodingPreservation::dictionary()) +``` + +This changes the coerced argument type passed to the function. If a function +derives its return type from that coerced argument type, code that checks exact +result types may need to update its expectations or add an explicit cast to +materialize the result. + +This changes the previous behavior of typed non-native classes such as +`Integer` and `Binary`, which retained the physical dictionary type by default. +UDFs relying on that behavior must now explicitly enable dictionary +preservation. `TypeSignatureClass::Any` is unaffected. + +### `GroupsAccumulator::merge_batch` no longer takes `opt_filter` + +The `opt_filter` argument has been removed from +`datafusion_expr_common::groups_accumulator::GroupsAccumulator::merge_batch`: + +```diff + fn merge_batch( + &mut self, + values: &[ArrayRef], + group_indices: &[usize], +- opt_filter: Option<&BooleanArray>, + total_num_groups: usize, + ) -> Result<()>; +``` + +Aggregate `FILTER` clauses only apply to raw input rows during the partial +(update) phase, so by the time intermediate states are merged there is nothing +left to filter per row. In practice `opt_filter` was always `None` here, so +removing it makes the API self-explanatory and impossible to misuse. + +**Who is affected:** + +- Anyone with a custom `GroupsAccumulator` implementation. +- Anyone calling `merge_batch` directly. + +**Migration guide:** + +Drop the `opt_filter` argument from your `merge_batch` signature and from any +call sites: + +```diff + fn merge_batch( + &mut self, + values: &[ArrayRef], + group_indices: &[usize], +- opt_filter: Option<&BooleanArray>, + total_num_groups: usize, + ) -> Result<()> { + // ... + } +``` + +```diff +- acc.merge_batch(values, group_indices, None, total_num_groups)?; ++ acc.merge_batch(values, group_indices, total_num_groups)?; +``` + +If your implementation previously inspected `opt_filter` (for example asserting +it was `None`), that code can simply be deleted. + +See [issue #22775](https://github.com/apache/datafusion/issues/22775) for details. + +### `GroupsAccumulator::convert_to_state` is now required + +`datafusion_expr_common::groups_accumulator::GroupsAccumulator::convert_to_state` +no longer provides a default implementation, and the +`GroupsAccumulator::supports_convert_to_state` capability method has been +removed. All `GroupsAccumulator` implementations must now support converting +input batches directly to intermediate aggregate state. + +**Who is affected:** + +- Users with custom `GroupsAccumulator` implementations. +- FFI providers and consumers that use `FFI_GroupsAccumulator`. + +**Migration guide:** + +Custom `GroupsAccumulator` implementations must now provide their own +`convert_to_state` implementation. + +Delete `supports_convert_to_state` implementations because `convert_to_state` +is now required: + +```diff +- fn supports_convert_to_state(&self) -> bool { +- true +- } +``` + +The `supports_convert_to_state` field has also been removed from +`datafusion_ffi::udaf::groups_accumulator::FFI_GroupsAccumulator`, changing its +ABI layout. Rebuild both FFI providers and consumers against DataFusion 55, and +do not exchange this struct with libraries built against older major versions. + +See [issue #23081](https://github.com/apache/datafusion/issues/23081) for details. + +### `is_dynamic_physical_expr` is deprecated + +`datafusion_physical_expr_common::physical_expr::is_dynamic_physical_expr` is +deprecated. It was a thin wrapper over `snapshot_generation(expr) != 0` used to +ask "does this predicate contain a dynamic filter?". + +Prefer asking the question directly against the concrete type. For a one-off +check, downcast to `DynamicFilterPhysicalExpr`: + +```rust +use datafusion_physical_expr::expressions::DynamicFilterPhysicalExpr; +use datafusion_common::tree_node::{TreeNode, TreeNodeRecursion}; + +let mut is_dynamic = false; +predicate.apply(|e| { + if e.downcast_ref::().is_some() { + is_dynamic = true; + Ok(TreeNodeRecursion::Stop) + } else { + Ok(TreeNodeRecursion::Continue) + } +})?; +``` + +If you also need to know whether the dynamic filters can still change (and to be +notified when they do), use the new `DynamicFilterTracking` / +`DynamicFilterTracker` API in `datafusion_physical_expr`: + +```rust +use datafusion_physical_expr::DynamicFilterTracking; + +let tracking = DynamicFilterTracking::classify(&predicate); +if tracking.contains_dynamic_filter() { + // worth re-evaluating the predicate at runtime +} +``` + +### `PruningPredicate::try_new` is deprecated + +`datafusion_pruning::PruningPredicate::try_new` is deprecated. Use +`PruningPredicateBuilder` instead. The deprecated constructor remains available +in DataFusion 55 and preserves its existing behavior. + +```rust +// Before +let predicate = PruningPredicate::try_new(expr, schema)?; + +// After +let predicate = PruningPredicateBuilder::new() + .with_file_schema(schema) + .try_build(expr)?; +``` + +### `FilePruner::try_new` no longer builds a pruner for static predicates without statistics + +`datafusion_pruning::FilePruner::try_new` now returns `None` when the predicate +is purely static _and_ the file carries no usable column statistics, because +such a pruner can never prune anything beyond what planning already did. +Previously it returned `Some` whenever a statistics struct was present (the +"is this worth pruning?" decision lived in the Parquet opener). Files with column +statistics, and predicates that carry a dynamic filter, are unaffected. + +### `QueryPlanner` adds `Any` as a supertrait + +To enable downcasting of `dyn QueryPlanner` to concrete query planner types (via +`is::()` / `downcast_ref::()`), the `QueryPlanner` trait now has `Any` +as a supertrait: + +```diff +- pub trait QueryPlanner: Debug ++ pub trait QueryPlanner: Any + Debug +``` + +### `ExecutionPlan::partition_statistics` deprecated in favor of `statistics_from_inputs` + +`ExecutionPlan::partition_statistics` is deprecated. Statistics computation is +now split into two parts: + +- `StatisticsContext` owns the bottom-up plan-tree traversal and a per-walk + cache of memoized child statistics. Call `StatisticsContext::compute` to + obtain statistics for a plan. +- `ExecutionPlan::statistics_from_inputs` computes a node's statistics from its + children's already-resolved statistics, which the context passes in. The node + does not traverse the tree itself. + +Existing implementations of `partition_statistics` continue to work unchanged. +The default `statistics_from_inputs` delegates to the deprecated method, so no +migration is required until the deprecated method is removed. + +> **Warning:** The delegation is **one-way**: the default `statistics_from_inputs` +> calls `partition_statistics`, but the default `partition_statistics` does +> **not** call `statistics_from_inputs` — it returns `Statistics::new_unknown`. +> Nodes that override only `statistics_from_inputs` will silently return +> `Statistics::new_unknown` to any caller still using the deprecated +> `partition_statistics`. + +**Who is affected:** + +- Users who implement custom `ExecutionPlan` nodes (recommended to migrate) +- Users who call `partition_statistics` directly (recommended to switch to `StatisticsContext::compute`) + +**Migration guide:** + +For **implementations**, override `statistics_from_inputs` instead of +`partition_statistics`, plus `child_stats_requests` to declare which children to +resolve. Child statistics then arrive pre-computed in `input_stats` (one entry per +child, in `children()` order), so the node only expresses its local propagation +logic. Leaf nodes, and nodes that derive their statistics without reading children, +need neither override (the default `child_stats_requests` skips every child). + +```rust,ignore +// Before: +fn partition_statistics(&self, partition: Option) -> Result> { + let child_stats = self.input.partition_statistics(partition)?; + // ... transform child_stats ... +} + +// After: declare the child to resolve, then compute from its statistics. +fn child_stats_requests(&self, partition: Option) -> Vec { + vec![ChildStats::At(partition)] +} + +fn statistics_from_inputs( + &self, + input_stats: &[Arc], + args: &StatisticsArgs, +) -> Result> { + let child_stats = Arc::clone(&input_stats[0]); + // ... transform child_stats ... +} +``` + +> **Important:** the default `child_stats_requests` skips every child, so a node that +> reads `input_stats` must override it to declare the children it uses, or those slots +> are filled with `Statistics::new_unknown` placeholders. Request a child with +> `ChildStats::At(partition)` (`None` = overall) and omit one with `ChildStats::Skip`. +> For example, a partition-merging operator requests `ChildStats::At(None)`, and a +> broadcast join requests its build side at `None`. + +For **callers**, walk a plan through `StatisticsContext::compute`. The cache is +created with the context: + +```rust,ignore +use datafusion_physical_plan::{StatisticsArgs, StatisticsContext}; + +// Before: +let stats = plan.partition_statistics(None)?; + +// After: +let stats = StatisticsContext::new().compute(plan.as_ref(), &StatisticsArgs::new())?; +``` + +### `DdlStatement::CreateExternalTable` and `CreateFunction` are now boxed + +The two largest variants of `datafusion_expr::DdlStatement` are now +`Box`ed: + +```rust,ignore +// Before +pub enum DdlStatement { + CreateExternalTable(CreateExternalTable), + // ... + CreateFunction(CreateFunction), + // ... +} + +// After +pub enum DdlStatement { + CreateExternalTable(Box), + // ... + CreateFunction(Box), + // ... +} +``` + +`CreateExternalTable` is 312 bytes and `CreateFunction` is 288 bytes, so +without boxing they forced the entire `LogicalPlan` enum to 320 bytes +even on SELECT-only query paths that never instantiate them. Boxing +shrinks `LogicalPlan` from 320 → 176 bytes (−45%), making every +`mem::take` / `mem::swap` / `Arc` store on the planning +hot path move a smaller payload. + +**Who is affected:** + +- Users who construct `DdlStatement::CreateExternalTable(...)` or + `DdlStatement::CreateFunction(...)` from an owned struct. +- Users who pattern-match these variants and destructure the inner + struct in the same pattern (e.g. + `DdlStatement::CreateExternalTable(CreateExternalTable { name, .. })`). +- Code that consumes the inner struct out of these variants (e.g. to + pass `CreateExternalTable` by value to another function). + +**Migration guide:** + +When constructing the variants, wrap the inner struct in `Box::new`: + +```rust,ignore +// Before +let stmt = DdlStatement::CreateFunction(CreateFunction { name, args, .. }); + +// After +let stmt = DdlStatement::CreateFunction(Box::new(CreateFunction { + name, + args, + .. +})); +``` + +When pattern-matching, bind the boxed value and either access fields +through it (Rust auto-derefs the `Box`) or destructure via `.as_ref()`: + +```rust,ignore +// Before +match ddl { + DdlStatement::CreateExternalTable(CreateExternalTable { + name, location, .. + }) => { /* use name, location */ } +} + +// After — access fields through the box +match ddl { + DdlStatement::CreateExternalTable(ce) => { + let name = &ce.name; + let location = &ce.location; + /* ... */ + } +} + +// After — destructure the dereferenced struct +match ddl { + DdlStatement::CreateExternalTable(ce) => { + let CreateExternalTable { name, location, .. } = ce.as_ref(); + /* ... */ + } +} +``` + +When you need an owned `CreateExternalTable` / `CreateFunction` out of +the variant, dereference the box with `*`: + +```rust,ignore +// Before +match plan { + LogicalPlan::Ddl(DdlStatement::CreateExternalTable(cmd)) => Ok(cmd), + _ => { /* ... */ } +} + +// After +match plan { + LogicalPlan::Ddl(DdlStatement::CreateExternalTable(cmd)) => Ok(*cmd), + _ => { /* ... */ } +} +``` + +See [PR #22733](https://github.com/apache/datafusion/pull/22733) for +details, including the per-variant size breakdown and benchmark +results. + +### `ExecutionPlan::with_new_children` and `ExecutionPlan::with_new_children_and_same_properties` deprecated + +`with_new_children` and `with_new_children_and_same_properties` have been +deprecated. These methods are used to replace the child plans of an +`ExecutionPlan` while leaving the plan otherwise identical. + +`with_new_children_if_necessary` has also been deprecated in favor of +`replace_children_if_necessary` for consistency in naming. + +As noted [here](https://github.com/apache/datafusion/pull/23332#discussion_r3554897693), +while the addition of `with_new_children_and_same_properties` has the benefit +of skipping potentially expensive computation in the case that replacement children +have the same properties as the original children, it widens the API surface area +of `ExecutionPlan` in a way that could be confusing for users. + +Thus, to rectify this, we unify these methods by introducing `replace_children`. +`replace_children` solves this problem by taking `ReplaceChildrenOptions`, +which includes a `ChildrenPropertiesMode`. The mode has two variants, +`Keep` and `Recompute`, which tell `replace_children` whether plan +properties can be reused or need to be recomputed. + +This method is called from `replace_children_if_necessary`, which is the +standard entry point that should be used for replacing the children of a node. + +**Migration guide:** + +To migrate from `with_new_children` and `with_new_children_and_same_properties` +to `replace_children`, it is recommended to implement `replace_children` with +a `match` statement matching on the `ChildrenPropertiesMode`. In the case that +the properties match the children, `ChildrenPropertiesMode::Keep`, +follow the body of `with_new_children_and_same_properties`. In the case that +the properties do not match the children, `ChildrenPropertiesMode::Recompute`, +follow the body of `with_new_children`. + +For example, take a look at the implementation for `FilterExec`: + +``` + fn replace_children( + self: Arc, + mut children: Vec>, + options: ReplaceChildrenOptions, + ) -> Result> { + validate_child_count!(self, children); + match options.children_properties { + ChildrenPropertiesMode::Keep => Ok(Arc::new(Self { + input: children.swap_remove(0), + metrics: ExecutionPlanMetricsSet::new(), + ..Self::clone(&*self) + })), + ChildrenPropertiesMode::Recompute => { + let new_input = children.swap_remove(0); + FilterExecBuilder::from(&*self) + .with_input(new_input) + .build() + .map(|e| Arc::new(e) as _) + } + } + } +``` + +In the case that the options indicate the properties are the same, we can simply +swap the children without having to recompute the properties. In the other case, +we create a new node from scratch. + +To ensure that this works correctly, it is recommended that users also look +through their codebase and ensure that they use `replace_children_if_necessary` +for these changes — `replace_children_if_necessary` should be preferred over +manual use of `replace_children`, since `replace_children_if_necessary` will +call `replace_children` with the correct options filled in. + +See [PR #23903](https://github.com/apache/datafusion/pull/23903) for details. + +### `ListingOptions::target_partitions` and `collect_stat` removed + +The `target_partitions` and `collect_stat` fields on +`datafusion_catalog_listing::ListingOptions`, their builder methods +(`with_target_partitions`, `with_collect_stat`), and the +`with_session_config_options` helper have been removed. + +`ListingTable` now reads both values directly from the active `SessionConfig` +at scan time instead of from a copy snapshotted onto the table at construction +time. + +**Who is affected:** + +- Code that set `target_partitions` / `collect_stat` per table via + `ListingOptions`, or read those public fields. +- Code that relied on a `ListingTable` freezing these values at construction + time independently of the session config. The table now always reflects the + current `SessionConfig`. + +**Migration guide:** + +Configure these on the `SessionConfig` instead: + +```rust,ignore +// Before +let options = ListingOptions::new(format) + .with_target_partitions(8) + .with_collect_stat(true); + +// After +let config = SessionConfig::new() + .with_target_partitions(8) + .with_collect_statistics(true); +``` + +See [PR #22969](https://github.com/apache/datafusion/pull/22969) for details. + +### Spark map functions now reject duplicate keys by default + +The Spark-compatibility map-construction functions (`map_from_arrays`, +`map_from_entries`, `str_to_map`) now raise `[DUPLICATED_MAP_KEY]` at runtime +when constructing a map that contains duplicate keys. This matches the default +of Spark's [`spark.sql.mapKeyDedupPolicy`](https://github.com/apache/spark/blob/v4.0.0/sql/catalyst/src/main/scala/org/apache/spark/sql/internal/SQLConf.scala#L4502-L4511). + +A new config option, `datafusion.spark.map_key_dedup_policy`, controls the +behavior: + +- `EXCEPTION` (default): raise on any duplicate key. +- `LAST_WIN`: keep the last occurrence of each duplicate key. The key stays at + its first-seen position with the value from its last occurrence (matching + Spark's `ArrayBasedMapBuilder`). + +**Who is affected:** + +- Queries calling `map_from_arrays` or `str_to_map` on data that contains + duplicate keys. Previously these functions either tolerated duplicates + silently or raised a non-configurable error. + +**Migration guide:** + +To restore lenient duplicate-key handling, set the policy to `LAST_WIN`: + +```sql +SET datafusion.spark.map_key_dedup_policy = 'LAST_WIN'; +``` + +See [PR #21720](https://github.com/apache/datafusion/pull/21720) for details. + +### Unify LRU memory-limiting caches into one generic cache + +The caches `DefaultFileMetadataCache`, `DefaultListFilesCache` and `DefaultFileStatisticsCache` +are merged into one generic implementation `DefaultCache`. The corresponding traits are now +type aliases: + +```diff +- pub trait FileStatisticsCache: CacheAccessor +- pub trait ListFilesCache: CacheAccessor +- pub trait FileMetadataCache: CacheAccessor ++ pub type FileStatisticsCache = dyn Cache; ++ pub type ListFilesCache = dyn Cache; ++ pub type FileMetadataCache = dyn Cache; +``` + +**Who is affected:** + +- Users who introduced their own implementation of `FileMetadataCache`, `ListFilesCache` or `FileStatisticsCache`. + +**Migration guide:** + +Implement the newly introduced types for your custom cache implementation. + +See [PR #22613](https://github.com/apache/datafusion/pull/22613) for details. + +### `CachedFileMetadata` now validates file schema + +The file-statistics cache remains keyed by `TableScopedPath`, but +`CachedFileMetadata` now stores a `SchemaFingerprint` of the `file_schema` used +to compute the cached statistics. Cache hits are valid only when both the file +metadata and schema fingerprint match. + +**Who is affected:** + +- Users constructing `CachedFileMetadata` values directly. + +**Migration guide:** + +- Pass `Arc::new(SchemaFingerprint::from_schema(file_schema))` to + `CachedFileMetadata::new`. +- Pass the current schema fingerprint to `CachedFileMetadata::is_valid_for`. + +See [PR #23201](https://github.com/apache/datafusion/pull/23201) for details. + +### `EmptyExecNode` and `PlaceholderRowExecNode` gained a `partitions` field + +The generated protobuf structs `EmptyExecNode` and `PlaceholderRowExecNode` +encoded only a schema, so the partition count set by `EmptyExec::with_partitions` +was silently dropped when a physical plan was serialized and deserialized: a plan +that reported `n` partitions before encoding reported `1` after. Both messages now +carry a `partitions` field that round-trips the count. + +**Who is affected:** + +- Users constructing `EmptyExecNode` or `PlaceholderRowExecNode` with an + exhaustive struct literal. + +**Migration guide:** + +Set the new field, or fill it from `Default`: + +```rust,ignore +// Before +EmptyExecNode { schema: Some(schema) } + +// After +EmptyExecNode { schema: Some(schema), partitions: 4 } +// or +EmptyExecNode { schema: Some(schema), ..Default::default() } +``` + +The wire format stays compatible in both directions. Plans encoded before this +field existed decode as a single partition, the previous default, and plans +encoded after it add a field that older readers ignore. + +See [PR #23643](https://github.com/apache/datafusion/pull/23643) for details. + +### `time ± interval` now returns a `time` instead of an `interval` + +Adding or subtracting an `interval` to/from a `time` value now returns a `time` +that wraps within the 24-hour clock, matching PostgreSQL and DuckDB. Previously +DataFusion returned an `interval`. + +```sql +-- 55.0.0 onwards: returns a time +SELECT time '23:30:00' + interval '2 hours'; +-- 01:30:00 +``` + +Only the sub-day portion of the interval affects the result; whole days and +months are ignored, as in PostgreSQL. The result keeps the input time's unit +(mirroring `timestamp + interval`), and any interval precision finer than that +unit is truncated -- so `time(s) + interval '1 nanosecond'` is a no-op. + +See [PR #23279](https://github.com/apache/datafusion/pull/23279) for details. + +### Physical-planning state moved to an explicit `PhysicalPlanningContext` + +The `subquery_indexes` and `subquery_results` public fields on +`datafusion_expr::execution_props::ExecutionProps` have been removed. They were +added in `54.0.0` as the channel through which the physical planner passed +uncorrelated scalar-subquery state to functions that create physical +`Arc` values from logical `Expr` values. + +The `lambda_variable_qualifier` public field and the +`with_qualified_lambda_variables` method on `ExecutionProps` have been removed +for the same reason: they carried the qualifiers of the lambda variables in +scope while `create_physical_expr` descended into a lambda body. + +That state is now carried by a dedicated +`datafusion_expr::physical_planning_context::PhysicalPlanningContext` passed explicitly +through functions and planner traits. Unlike `ExecutionProps`, which applies +throughout the planning of an entire query, this context is scoped to the +logical plan subtree currently being converted. This removes the need for the +physical planner to clone and mutate a `SessionState`, is a prerequisite for +letting the planner take `&dyn Session`, and lets `ExtensionPlanner` +implementations create physical +expressions containing scalar subqueries against the same subquery state as the +rest of the plan. + +The following functions take a new trailing +`planning_ctx: &PhysicalPlanningContext` parameter: + +- `datafusion_physical_expr::create_physical_expr` / `create_physical_exprs` +- `datafusion_physical_expr::create_physical_sort_expr` / + `create_physical_sort_exprs` / `create_physical_partitioning` +- `datafusion::physical_planner::create_window_expr` / + `create_window_expr_with_name` +- `datafusion_physical_expr::aggregate::LoweredAggregateBuilder::new` + +The planner traits changed accordingly: + +- `PhysicalPlanner::create_physical_expr` takes + `planning_ctx: &PhysicalPlanningContext` +- `ExtensionPlanner::plan_extension` and `plan_table_scan` receive + `planning_ctx: &PhysicalPlanningContext` and should forward it to + `PhysicalPlanner::create_physical_expr` when creating physical expressions + +Convenience methods such as `SessionContext::create_physical_expr` and +`SessionState::create_physical_expr` are unchanged. + +**Who is affected:** + +- Code calling the functions above: pass + `&PhysicalPlanningContext::default()` unless you are creating physical + expressions as part of a physical plan that contains uncorrelated scalar + subqueries. +- Custom `PhysicalPlanner` or `ExtensionPlanner` implementations: add the new + parameter and forward it. +- Code that read or wrote `execution_props.subquery_indexes` / + `execution_props.subquery_results`: build a `PhysicalPlanningContext` instead. +- Code that read `execution_props.lambda_variable_qualifier` or called + `ExecutionProps::with_qualified_lambda_variables`: remove that usage. Callers + that only plan a `HigherOrderFunction` are not affected -- + `create_physical_expr` populates the lambda qualifiers itself as it descends + into lambda bodies. Code that needs to read or extend the lambda + scope should use the equivalents on `PhysicalPlanningContext`: + `PhysicalPlanningContext::lambda_variable_qualifier` and + `PhysicalPlanningContext::with_qualified_lambda_variables`. + +**Migration guide:** + +When creating a physical expression outside of physical planning, pass an empty +context: + +```rust,ignore +use datafusion_expr::physical_planning_context::PhysicalPlanningContext; +use datafusion_physical_expr::create_physical_expr; + +// Before +let phys = create_physical_expr(&expr, &schema, &props)?; + +// After +let phys = create_physical_expr( + &expr, + &schema, + &props, + &PhysicalPlanningContext::default(), +)?; +``` + +For `ExtensionPlanner` implementations, accept and forward the context: + +```rust,ignore +async fn plan_extension( + &self, + planner: &dyn PhysicalPlanner, + node: &dyn UserDefinedLogicalNode, + logical_inputs: &[&LogicalPlan], + physical_inputs: &[Arc], + session: &dyn Session, + planning_ctx: &PhysicalPlanningContext, // new parameter +) -> Result>> { + for expr in node.expressions() { + // Forward the context so scalar subqueries in this node's + // expressions resolve against the plan's subquery state + planner.create_physical_expr(&expr, node.schema(), session, planning_ctx)?; + } + // ... +} +``` + +See [PR #23649](https://github.com/apache/datafusion/pull/23649) and +[PR #23989](https://github.com/apache/datafusion/pull/23989) for details. + +### Catalog, planner, and optimizer contracts moved to `datafusion-session` + +The catalog, planner, and physical optimizer contract traits now live in the +`datafusion-session` crate. This makes them available through `Session` without +downcasting to `SessionState`, including across the FFI boundary. + +The moved catalog traits are `CatalogProviderList`, `CatalogProvider`, +`SchemaProvider`, `TableProvider`, `TableProviderFactory`, and +`TableFunctionImpl`. The related `TableFunction` struct also moved. The +`datafusion-catalog` crate re-exports these items from their new location, so +paths such as `datafusion::catalog::TableProvider` and +`datafusion_catalog::CatalogProvider` continue to work unchanged. + +The moved planning and optimization traits are `QueryPlanner`, +`PhysicalPlanner`, `ExtensionPlanner`, `PhysicalOptimizerRule`, and +`PhysicalOptimizerContext`. Their previous paths also continue to work through +re-exports: + +- `datafusion::execution::context::QueryPlanner` +- `datafusion::physical_planner::{PhysicalPlanner, ExtensionPlanner}` +- `datafusion_physical_optimizer::{PhysicalOptimizerRule, PhysicalOptimizerContext}` + +The session argument for methods on `QueryPlanner`, `PhysicalPlanner`, and +`ExtensionPlanner` changed from `&SessionState` to `&dyn Session`. Custom planner +implementations should update their signatures. Planner code should use methods +on `Session` instead of downcasting it to `SessionState`. + +The `Session` trait now requires a `catalog_list` method that returns the +catalogs registered with the session: + +```rust +fn catalog_list(&self) -> Arc; +``` + +Custom `Session` implementations must add this method. Implementations that do +not expose a catalog can return the new `EmptyCatalogProviderList`: + +```rust +use std::sync::Arc; +use datafusion_session::{CatalogProviderList, EmptyCatalogProviderList}; + +fn catalog_list(&self) -> Arc { + Arc::new(EmptyCatalogProviderList) +} +``` + +`Session` gains a `query_planner` method alongside `optimize`, +`physical_optimizers`, and `statistics_registry`. All four have default +implementations, so existing `Session` implementations that do not perform +physical planning require no changes: `query_planner` defaults to the new +`UnsupportedQueryPlanner`, `optimize` returns the plan unchanged, +`physical_optimizers` returns no rules, and `statistics_registry` returns +`None`. + +A custom session that drives planning through `DefaultQueryPlanner` or +`DefaultPhysicalPlanner` must override these methods to expose its planning and +optimization behavior; the defaults will otherwise produce unoptimized plans or +fail to plan at all. The simplest approach is to delegate to a `SessionState`: + +```rust +use std::sync::Arc; +use datafusion_session::{PhysicalOptimizerRule, QueryPlanner}; + +fn query_planner(&self) -> Arc { + self.inner.query_planner() +} + +fn optimize(&self, plan: &LogicalPlan) -> Result { + self.inner.optimize(plan) +} + +fn physical_optimizers(&self) -> &[Arc] { + self.inner.physical_optimizers() +} +``` + +`ForeignSession::create_physical_plan` runs the complete planning pipeline in the +library that owns the session. `ForeignSession::query_planner`, `optimize`, and +`physical_optimizers` forward to the owning session across the FFI boundary. A +foreign query planner can also be installed on a session through the new +`datafusion_ffi::query_planner::FFI_QueryPlanner`; see that module's +documentation for how plans and extension codecs cross the boundary. + +See [PR #23703](https://github.com/apache/datafusion/pull/23703) for details on +the catalog changes. + +### `FFI_LogicalExtensionCodec::task_ctx_provider` is now private + +The `task_ctx_provider` field on +`datafusion_ffi::proto::logical_extension_codec::FFI_LogicalExtensionCodec` was +`pub` and is now crate-private, matching `FFI_PhysicalExtensionCodec`. + +**Who is affected:** + +- Code that read or cloned `FFI_LogicalExtensionCodec::task_ctx_provider` + directly. Pass the task context provider to `FFI_LogicalExtensionCodec::new` + instead, and keep your own copy if you need it elsewhere. + +### Unused `async` removed from several public functions + +Public functions that were declared `async` but never awaited anything are now +synchronous: + +- `CsvFormat::read_to_delimited_chunks_from_stream` (in + `datafusion_datasource_csv`, re-exported as + `datafusion::datasource::file_format::csv::CsvFormat`) +- `datafusion_substrait::serializer::deserialize_bytes`, which now also borrows + its input as `&[u8]` instead of taking an owned `Vec` +- `datafusion::test_util::parquet::TestParquetFile::create_scan` + +**Migration guide:** + +Remove `.await` from call sites; the compiler flags each one, since `.await` +on a non-future value does not compile: + +```rust,ignore +// Before +let stream = csv_format + .read_to_delimited_chunks_from_stream(input) + .await; +let plan = deserialize_bytes(proto_bytes).await?; + +// After +let stream = csv_format.read_to_delimited_chunks_from_stream(input); +let plan = deserialize_bytes(&proto_bytes)?; +``` + +### `MovingMin` and `MovingMax` changed to `pub(crate)` + +`MovingMin` and `MovingMax` in `datafusion_functions_aggregate::min_max` have been changed from `pub` to `pub(crate)` visibility as they are internal helper data structures for DataFusion's sliding window aggregators. + +**Who is affected:** + +- Code that directly imported `MovingMin` or `MovingMax` from `datafusion_functions_aggregate`. Standard SQL window functions (`MIN(...) OVER (...)` / `MAX(...) OVER (...)`) are unaffected. + +See [PR #23827](https://github.com/apache/datafusion/pull/23827) for details. + +### `ExecutionPlan::apply_expressions` is now a required method + +`apply_expressions` has been added as a **required** method on the `ExecutionPlan`, `FileSource`, and `DataSource` traits. Any custom implementation of +these traits must now implement `apply_expressions`. See docs on `ExecutionPlan::apply_expressions` for migration details. + +### `WindowExpr::evaluate_stateful` now takes a `WindowEvalContext` + +`WindowExpr::evaluate_stateful` (and the provided +`AggregateWindowExpr::aggregate_evaluate_stateful` method) take a new +`WindowEvalContext` argument carrying stream-level information that is shared +by all partitions: + +```rust,ignore +// Before +fn evaluate_stateful( + &self, + partition_batches: &PartitionBatches, + window_agg_state: &mut PartitionWindowAggStates, +) -> Result<()> + +// After +fn evaluate_stateful( + &self, + partition_batches: &PartitionBatches, + window_agg_state: &mut PartitionWindowAggStates, + eval_ctx: &WindowEvalContext<'_>, +) -> Result<()> +``` + +`WindowEvalContext` currently carries the most recent input row, which +previously lived in each partition's `PartitionBatchState` (see the next +section). The struct is `#[non_exhaustive]` so that fields can be added +without further signature changes: construct it with +`WindowEvalContext::default()` and set fields through its builder methods. + +**Who is affected:** + +- Implementations of the `WindowExpr` trait that override `evaluate_stateful` + must add the new parameter. +- Callers of `evaluate_stateful` or `aggregate_evaluate_stateful` must pass a + context. + +**Migration guide:** + +```rust,ignore +use datafusion_physical_expr::window::WindowEvalContext; + +// Before +window_expr.evaluate_stateful(&partition_batches, &mut window_agg_state)?; + +// After +let eval_ctx = WindowEvalContext::default() + .with_most_recent_row(most_recent_row.as_ref()); +window_expr.evaluate_stateful( + &partition_batches, + &mut window_agg_state, + &eval_ctx, +)?; +``` + +Pass `WindowEvalContext::default()` when no most-recent-row watermark is +available (for example, when the input is sorted by the partition keys and +partition ends are detected directly). + +### `PartitionBatchState::most_recent_row` removed + +The `most_recent_row` field and the `set_most_recent_row` method have been +removed from `datafusion_expr::window_state::PartitionBatchState`. The most +recent input row is a property of the whole input stream rather than +per-partition state: every partition observed the same value. It is now +tracked once by the operator driving the evaluation and passed to window +expressions through the new `WindowEvalContext` argument of +`WindowExpr::evaluate_stateful` described above. + +**Who is affected:** + +- Code that read `PartitionBatchState::most_recent_row` or called + `set_most_recent_row`, such as custom streaming window operators. + +**Migration guide:** + +Track the most recent input row once per stream (for example, a one-row +slice of the last non-empty input batch) and pass it to window expressions +via `WindowEvalContext::with_most_recent_row` instead of copying it into +each partition's state. + +### `MSRV` updated to 1.94.0 + +The Minimum Supported Rust Version (MSRV) has been updated to [`1.94.0`]. + +[`1.94.0`]: https://releases.rs/docs/1.94.0/ + +### `CachedParquetFileReader` removed; `ParquetFileReader` fields are now private + +`CachedParquetFileReader` duplicated `ParquetFileReader` and has been removed; +`ParquetFileReader`'s fields are also now private, with +`file_metrics()` and `partitioned_file()` accessors added for the two that +were previously public. + +**Who is affected:** + +- Code that names the `CachedParquetFileReader` type. +- Code that constructs a `ParquetFileReader` directly via a struct literal, or + reads/writes its fields. + +**Migration guide:** + +`ParquetFileReader::new` is no longer public; build a reader through +`ParquetFileReaderFactory::create_reader` (via `DefaultParquetFileReaderFactory` +or `CachedParquetFileReaderFactory`) instead of constructing one directly: + +```rust,ignore +// Before +let inner = ParquetObjectReader::new(Arc::clone(&store), location).with_file_size(size); +let reader = CachedParquetFileReader::new( + file_metrics, + store, + inner, + partitioned_file, + metadata_cache, + metadata_size_hint, +); + +// After +let reader = CachedParquetFileReaderFactory::new(store, metadata_cache) + .create_reader(partition_index, partitioned_file, metadata_size_hint, &metrics)?; +``` + +Replace field access with the new accessor methods: + +```rust,ignore +// Before +let bytes_scanned = reader.file_metrics.bytes_scanned.value(); +let location = &reader.partitioned_file.object_meta.location; + +// After +let bytes_scanned = reader.file_metrics().bytes_scanned.value(); +let location = &reader.partitioned_file().object_meta.location; +``` + +### `array_distance` scalar function now rejects multidimensional arrays + +`array_distance` only supports one-dimensional arrays. Previously, when given +multidimensional arrays, it computed the distance using only the first +subarray and ignored the remaining subarrays. For example: + +```sql +SELECT array_distance( + [[1, 2], [100, 100]], + [[1, 4], [0, 0]] +); +``` + +Previously, this query returned `2.0`, the distance between `[1, 2]` and +`[1, 4]`. It now returns a planning error stating that `array_distance` only +supports one-dimensional arrays. + +### `ParquetObjectReader` / `ParquetObjectWriter` deprecated upstream + +The [`parquet` crate] deprecated [`ParquetObjectReader`] +and [`ParquetObjectWriter`] in favor of implementing +[`AsyncFileReader`] directly (see the example on the [`AsyncFileReader`] trait and +[`parquet/examples/object_store.rs`] in `arrow-rs`) or passing an +[`BufWriter`] straight to [`AsyncArrowWriter`]. + +**Who is affected:** + +- Custom [`ParquetFileReaderFactory`] implementations that construct a + [`ParquetObjectReader`] directly and now see a deprecation warning after + upgrading the `parquet` dependency. + +**Migration guide:** + +If your [`AsyncFileReader`] implementation exists mainly to read from an +[`ObjectStore`] and track metrics, consider using DataFusion's +[`ParquetFileReader`] instead of wrapping a [`ParquetObjectReader`]: + +```rust,ignore +// Before +let inner = ParquetObjectReader::new(store, location).with_file_size(size); +Ok(Box::new(MyReader { inner, file_metrics, partitioned_file })) + +// After +Ok(Box::new(ParquetFileReader { + file_metrics, + store, + metadata_size_hint, + partitioned_file, +})) +``` + +If you need custom behavior (I/O coalescing, byte caching, a dedicated I/O +runtime), implement `AsyncFileReader` directly against your `ObjectStore`, +following the pattern in [`parquet/examples/object_store.rs`] + +See [PR #24030](https://github.com/apache/datafusion/pull/24030) for details. + +[`parquet` crate]: https://crates.io/crates/parquet +[`parquetobjectreader`]: https://docs.rs/parquet/59.1.0/parquet/arrow/async_reader/struct.ParquetObjectReader.html +[`parquetobjectwriter`]: https://docs.rs/parquet/59.1.0/parquet/arrow/async_writer/struct.ParquetObjectWriter.html +[`parquetfilereader`]: https://docs.rs/datafusion/latest/datafusion/datasource/physical_plan/parquet/struct.ParquetFileReader.html +[`parquetfilereaderfactory`]: https://docs.rs/datafusion/latest/datafusion/datasource/physical_plan/parquet/trait.ParquetFileReaderFactory.html +[`asyncfilereader`]: https://docs.rs/parquet/59.1.0/parquet/arrow/async_reader/trait.AsyncFileReader.html +[`objectstore`]: https://docs.rs/object_store/latest/object_store/trait.ObjectStore.html +[`bufwriter`]: https://docs.rs/tokio/latest/tokio/io/struct.BufWriter.html +[`asyncarrowwriter`]: https://docs.rs/parquet/59.1.0/parquet/arrow/async_writer/struct.AsyncArrowWriter.html +[`parquet/examples/object_store.rs`]: https://github.com/apache/arrow-rs/blob/main/parquet/examples/object_store.rs + +### `datafusion-proto`: parquet options conversions are fallible + +`protobuf::ParquetOptions` and `protobuf::TableParquetOptions` validate +`writer_version` when converting into their `datafusion-common` counterparts, so +those conversions are `TryFrom` rather than `From`. + +Every other `From` / `TryFrom` conversion between DataFusion types and +`datafusion_proto::protobuf` messages is unchanged. Several impls moved to the +crate that owns their DataFusion type, but trait impls are global, so +`X::try_from(&proto)` and `proto.try_into()` still resolve with no import +changes. + +**Migration guide:** + +```rust,ignore +// Before +let opts = ParquetOptions::from(&proto_opts); +let table_opts = TableParquetOptions::from(&proto_table_opts); + +// After +let opts = ParquetOptions::try_from(&proto_opts)?; +let table_opts = TableParquetOptions::try_from(&proto_table_opts)?; +``` + +See [issue #24019](https://github.com/apache/datafusion/issues/24019) for details. diff --git a/versions/55.0.0/_sources/library-user-guide/upgrading/index.rst.txt b/versions/55.0.0/_sources/library-user-guide/upgrading/index.rst.txt new file mode 100644 index 0000000000000..51c7f1413172b --- /dev/null +++ b/versions/55.0.0/_sources/library-user-guide/upgrading/index.rst.txt @@ -0,0 +1,34 @@ +.. 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. + +Upgrade Guides +============== + +.. toctree:: + :maxdepth: 1 + + DataFusion 55.0.0 <55.0.0> + DataFusion 54.0.0 <54.0.0> + DataFusion 53.0.0 <53.0.0> + DataFusion 52.0.0 <52.0.0> + DataFusion 51.0.0 <51.0.0> + DataFusion 50.0.0 <50.0.0> + DataFusion 49.0.0 <49.0.0> + DataFusion 48.0.1 <48.0.1> + DataFusion 48.0.0 <48.0.0> + DataFusion 47.0.0 <47.0.0> + DataFusion 46.0.0 <46.0.0> diff --git a/versions/55.0.0/_sources/library-user-guide/using-the-dataframe-api.md.txt b/versions/55.0.0/_sources/library-user-guide/using-the-dataframe-api.md.txt new file mode 100644 index 0000000000000..024eff5d20834 --- /dev/null +++ b/versions/55.0.0/_sources/library-user-guide/using-the-dataframe-api.md.txt @@ -0,0 +1,286 @@ + + +# Using the DataFrame API + +The [Users Guide] introduces the [`DataFrame`] API and this section describes +that API in more depth. + +## What is a DataFrame? + +As described in the [Users Guide], DataFusion [`DataFrame`]s are modeled after +the [Pandas DataFrame] interface, and are implemented as thin wrapper over a +[`LogicalPlan`] that adds functionality for building and executing those plans. + +The simplest possible dataframe is one that scans a table and that table can be +in a file or in memory. + +## How to generate a DataFrame + +You can construct [`DataFrame`]s programmatically using the API, similarly to +other DataFrame APIs. For example, you can read an in memory `RecordBatch` into +a `DataFrame`: + +```rust +use std::sync::Arc; +use datafusion::prelude::*; +use datafusion::arrow::array::{ArrayRef, Int32Array}; +use datafusion::arrow::record_batch::RecordBatch; +use datafusion::error::Result; + +#[tokio::main] +async fn main() -> Result<()> { + let ctx = SessionContext::new(); + // Register an in-memory table containing the following data + // id | bank_account + // ---|------------- + // 1 | 9000 + // 2 | 8000 + // 3 | 7000 + let data = RecordBatch::try_from_iter(vec![ + ("id", Arc::new(Int32Array::from(vec![1, 2, 3])) as ArrayRef), + ("bank_account", Arc::new(Int32Array::from(vec![9000, 8000, 7000]))), + ])?; + // Create a DataFrame that scans the user table, and finds + // all users with a bank account at least 8000 + // and sorts the results by bank account in descending order + let dataframe = ctx + .read_batch(data)? + .filter(col("bank_account").gt_eq(lit(8000)))? // bank_account >= 8000 + .sort(vec![col("bank_account").sort(false, true)])?; // ORDER BY bank_account DESC + + Ok(()) +} +``` + +You can _also_ generate a `DataFrame` from a SQL query and use the DataFrame's APIs +to manipulate the output of the query. + +```rust +use std::sync::Arc; +use datafusion::prelude::*; +use datafusion::assert_batches_eq; +use datafusion::arrow::array::{ArrayRef, Int32Array}; +use datafusion::arrow::record_batch::RecordBatch; +use datafusion::error::Result; + +#[tokio::main] +async fn main() -> Result<()> { + let ctx = SessionContext::new(); + // Register the same in-memory table as the previous example + let data = RecordBatch::try_from_iter(vec![ + ("id", Arc::new(Int32Array::from(vec![1, 2, 3])) as ArrayRef), + ("bank_account", Arc::new(Int32Array::from(vec![9000, 8000, 7000]))), + ])?; + ctx.register_batch("users", data)?; + // Create a DataFrame using SQL + let dataframe = ctx.sql("SELECT * FROM users;") + .await? + // Note we can filter the output of the query using the DataFrame API + .filter(col("bank_account").gt_eq(lit(8000)))?; // bank_account >= 8000 + + let results = &dataframe.collect().await?; + + // use the `assert_batches_eq` macro to show the output + assert_batches_eq!( + vec![ + "+----+--------------+", + "| id | bank_account |", + "+----+--------------+", + "| 1 | 9000 |", + "| 2 | 8000 |", + "+----+--------------+", + ], + &results + ); + Ok(()) +} +``` + +## Collect / Streaming Exec + +DataFusion [`DataFrame`]s are "lazy", meaning they do no processing until +they are executed, which allows for additional optimizations. + +You can run a `DataFrame` in one of three ways: + +1. `collect`: executes the query and buffers all the output into a `Vec` +2. `execute_stream`: begins executions and returns a `SendableRecordBatchStream` which incrementally computes output on each call to `next()` +3. `cache`: executes the query and buffers the output into a new in memory `DataFrame.` + +To collect all outputs into a memory buffer, use the `collect` method: + +```rust +use datafusion::prelude::*; +use datafusion::error::Result; + +#[tokio::main] +async fn main() -> Result<()> { + let ctx = SessionContext::new(); + // read the contents of a CSV file into a DataFrame + let df = ctx.read_csv("tests/data/example.csv", CsvReadOptions::new()).await?; + // execute the query and collect the results as a Vec + let batches = df.collect().await?; + for record_batch in batches { + println!("{record_batch:?}"); + } + Ok(()) +} +``` + +Use `execute_stream` to incrementally generate output one `RecordBatch` at a time: + +```rust +use datafusion::prelude::*; +use datafusion::error::Result; +use futures::stream::StreamExt; + +#[tokio::main] +async fn main() -> Result<()> { + let ctx = SessionContext::new(); + // read example.csv file into a DataFrame + let df = ctx.read_csv("tests/data/example.csv", CsvReadOptions::new()).await?; + // begin execution (returns quickly, does not compute results) + let mut stream = df.execute_stream().await?; + // results are returned incrementally as they are computed + while let Some(record_batch) = stream.next().await { + println!("{record_batch:?}"); + } + Ok(()) +} +``` + +# Write DataFrame to Files + +You can also write the contents of a `DataFrame` to a file. When writing a file, +DataFusion executes the `DataFrame` and streams the results to the output. +DataFusion comes with support for writing `csv`, `json` `arrow` `avro`, and +`parquet` files, and supports writing custom file formats via API (see +[`custom_file_format.rs`] for an example) + +For example, to read a CSV file and write it to a parquet file, use the +[`DataFrame::write_parquet`] method + +```rust +use datafusion::prelude::*; +use datafusion::error::Result; +use datafusion::dataframe::DataFrameWriteOptions; + +#[tokio::main] +async fn main() -> Result<()> { + let ctx = SessionContext::new(); + // read example.csv file into a DataFrame + let df = ctx.read_csv("tests/data/example.csv", CsvReadOptions::new()).await?; + // stream the contents of the DataFrame to the `example.parquet` file + let target_path = tempfile::tempdir()?.path().join("example.parquet"); + df.write_parquet( + target_path.to_str().unwrap(), + DataFrameWriteOptions::new(), + None, // writer_options + ).await; + Ok(()) +} +``` + +[`custom_file_format.rs`]: https://github.com/apache/datafusion/blob/main/datafusion-examples/examples/custom_data_source/custom_file_format.rs + +The output file will look like (Example Output): + +```sql +> select * from '../datafusion/core/example.parquet'; ++---+---+---+ +| a | b | c | ++---+---+---+ +| 1 | 2 | 3 | ++---+---+---+ +``` + +## Relationship between `LogicalPlan`s and `DataFrame`s + +The `DataFrame` struct is defined like this: + +```rust +use datafusion::execution::session_state::SessionState; +use datafusion::logical_expr::LogicalPlan; +pub struct DataFrame { + // state required to execute a LogicalPlan + session_state: Box, + // LogicalPlan that describes the computation to perform + plan: LogicalPlan, +} +``` + +As shown above, `DataFrame` is a thin wrapper of `LogicalPlan`, so you can +easily go back and forth between them. + +```rust +use datafusion::prelude::*; +use datafusion::error::Result; +use datafusion::logical_expr::LogicalPlanBuilder; + +#[tokio::main] +async fn main() -> Result<()>{ + let ctx = SessionContext::new(); + // read example.csv file into a DataFrame + let df = ctx.read_csv("tests/data/example.csv", CsvReadOptions::new()).await?; + // You can easily get the LogicalPlan from the DataFrame + let (_state, plan) = df.into_parts(); + // Just combine LogicalPlan with SessionContext and you get a DataFrame + // get LogicalPlan in dataframe + let new_df = DataFrame::new(ctx.state(), plan); + Ok(()) +} +``` + +In fact, using the [`DataFrame`]s methods you can create the same +[`LogicalPlan`]s as when using [`LogicalPlanBuilder`]: + +```rust +use datafusion::prelude::*; +use datafusion::error::Result; +use datafusion::logical_expr::LogicalPlanBuilder; + +#[tokio::main] +async fn main() -> Result<()>{ + let ctx = SessionContext::new(); + // read example.csv file into a DataFrame + let df = ctx.read_csv("tests/data/example.csv", CsvReadOptions::new()).await?; + // Create a new DataFrame sorted by `id`, `bank_account` + let new_df = df.select(vec![col("a"), col("b")])? + .sort_by(vec![col("a")])?; + // Build the same plan using the LogicalPlanBuilder + // Similar to `SELECT a, b FROM example.csv ORDER BY a` + let df = ctx.read_csv("tests/data/example.csv", CsvReadOptions::new()).await?; + let (_state, plan) = df.into_parts(); // get the DataFrame's LogicalPlan + let plan = LogicalPlanBuilder::from(plan) + .project(vec![col("a"), col("b")])? + .sort_by(vec![col("a")])? + .build()?; + // prove they are the same + assert_eq!(new_df.logical_plan(), &plan); + Ok(()) +} +``` + +[users guide]: ../user-guide/dataframe.md +[pandas dataframe]: https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.html +[`dataframe`]: https://docs.rs/datafusion/latest/datafusion/dataframe/struct.DataFrame.html +[`logicalplan`]: https://docs.rs/datafusion/latest/datafusion/logical_expr/enum.LogicalPlan.html +[`logicalplanbuilder`]: https://docs.rs/datafusion/latest/datafusion/logical_expr/struct.LogicalPlanBuilder.html +[`dataframe::write_parquet`]: https://docs.rs/datafusion/latest/datafusion/dataframe/struct.DataFrame.html#method.write_parquet diff --git a/versions/55.0.0/_sources/library-user-guide/using-the-sql-api.md.txt b/versions/55.0.0/_sources/library-user-guide/using-the-sql-api.md.txt new file mode 100644 index 0000000000000..8b8ba2a3716a3 --- /dev/null +++ b/versions/55.0.0/_sources/library-user-guide/using-the-sql-api.md.txt @@ -0,0 +1,225 @@ + + +# Using the SQL API + +DataFusion has a full SQL API that allows you to interact with DataFusion using +SQL query strings. The simplest way to use the SQL API is to use the +[`SessionContext`] struct which provides the highest-level API for executing SQL +queries. + +To use SQL, you first register your data as a table and then run queries +using the [`SessionContext::sql`] method. For lower level control such as +preventing DDL, you can use [`SessionContext::sql_with_options`] or the +[`SessionState`] APIs + +## Registering Data Sources using `SessionContext::register*` + +The `SessionContext::register*` methods tell DataFusion the name of +the source and how to read data. Once registered, you can execute SQL queries +using the [`SessionContext::sql`] method referring to your data source as a table. + +The [`SessionContext::sql`] method returns a `DataFrame` for ease of +use. See the ["Using the DataFrame API"] section for more information on how to +work with DataFrames. + +### Read a CSV File + +```rust +use datafusion::error::Result; +use datafusion::prelude::*; +use arrow::record_batch::RecordBatch; + +#[tokio::main] +async fn main() -> Result<()> { + let ctx = SessionContext::new(); + // register the "example" table + ctx.register_csv("example", "tests/data/example.csv", CsvReadOptions::new()).await?; + // create a plan to run a SQL query + let df = ctx.sql("SELECT a, min(b) FROM example WHERE a <= b GROUP BY a LIMIT 100").await?; + // execute the plan and collect the results as Vec + let results: Vec = df.collect().await?; + // Use the assert_batches_eq macro to compare the results with expected output + datafusion::assert_batches_eq!(vec![ + "+---+----------------+", + "| a | min(example.b) |", + "+---+----------------+", + "| 1 | 2 |", + "+---+----------------+", + ], + &results + ); + Ok(()) +} +``` + +### Read an Apache Parquet file + +Similarly to CSV, you can register a Parquet file as a table using the `register_parquet` method. + +```rust +use datafusion::error::Result; +use datafusion::prelude::*; +#[tokio::main] +async fn main() -> Result<()> { + // create local session context + let ctx = SessionContext::new(); + let testdata = datafusion::test_util::parquet_test_data(); + + // register parquet file with the execution context + ctx.register_parquet( + "alltypes_plain", + &format!("{testdata}/alltypes_plain.parquet"), + ParquetReadOptions::default(), + ) + .await?; + + // execute the query + let df = ctx.sql( + "SELECT int_col, double_col, CAST(date_string_col as VARCHAR) \ + FROM alltypes_plain \ + WHERE id > 1 AND tinyint_col < double_col", + ).await?; + + // execute the plan, and compare to the expected results + let results = df.collect().await?; + datafusion::assert_batches_eq!(vec![ + "+---------+------------+--------------------------------+", + "| int_col | double_col | alltypes_plain.date_string_col |", + "+---------+------------+--------------------------------+", + "| 1 | 10.1 | 03/01/09 |", + "| 1 | 10.1 | 04/01/09 |", + "| 1 | 10.1 | 02/01/09 |", + "+---------+------------+--------------------------------+", + ], + &results + ); + Ok(()) +} +``` + +### Read an Apache Avro file + +DataFusion can also read Avro files using the `register_avro` method. + +```rust +# #[cfg(feature = "avro")] +{ +use datafusion::arrow::util::pretty; +use datafusion::error::Result; +use datafusion::prelude::*; + +#[tokio::main] +async fn main() -> Result<()> { + let ctx = SessionContext::new(); + // find the path to the avro test files + let testdata = datafusion::test_util::arrow_test_data(); + // register avro file with the execution context + let avro_file = &format!("{testdata}/avro/alltypes_plain.avro"); + ctx.register_avro("alltypes_plain", avro_file, AvroReadOptions::default()).await?; + + // execute the query + let df = ctx.sql( + "SELECT int_col, double_col, CAST(date_string_col as VARCHAR) \ + FROM alltypes_plain \ + WHERE id > 1 AND tinyint_col < double_col" + ).await?; + + // execute the plan, and compare to the expected results + let results = df.collect().await?; + datafusion::assert_batches_eq!(vec![ + "+---------+------------+--------------------------------+", + "| int_col | double_col | alltypes_plain.date_string_col |", + "+---------+------------+--------------------------------+", + "| 1 | 10.1 | 03/01/09 |", + "| 1 | 10.1 | 04/01/09 |", + "| 1 | 10.1 | 02/01/09 |", + "+---------+------------+--------------------------------+", + ], + &results + ); + Ok(()) +} +} +``` + +## Reading Multiple Files as a table + +It is also possible to read multiple files as a single table. This is done +with the ListingTableProvider which takes a list of file paths and reads them +as a single table, matching schemas as appropriate + +Coming Soon + +```rust + +``` + +## Using `CREATE EXTERNAL TABLE` to register data sources via SQL + +You can also register files using SQL using the [`CREATE EXTERNAL TABLE`] +statement. + +[`create external table`]: ../user-guide/sql/ddl.md#create-external-table + +```rust +use datafusion::error::Result; +use datafusion::prelude::*; +#[tokio::main] +async fn main() -> Result<()> { + // create local session context + let ctx = SessionContext::new(); + let testdata = datafusion::test_util::parquet_test_data(); + + // register parquet file using SQL + let ddl = format!( + "CREATE EXTERNAL TABLE alltypes_plain \ + STORED AS PARQUET LOCATION '{testdata}/alltypes_plain.parquet'" + ); + ctx.sql(&ddl).await?; + + // execute the query referring to the alltypes_plain table we just registered + let df = ctx.sql( + "SELECT int_col, double_col, CAST(date_string_col as VARCHAR) \ + FROM alltypes_plain \ + WHERE id > 1 AND tinyint_col < double_col", + ).await?; + + // execute the plan, and compare to the expected results + let results = df.collect().await?; + datafusion::assert_batches_eq!(vec![ + "+---------+------------+--------------------------------+", + "| int_col | double_col | alltypes_plain.date_string_col |", + "+---------+------------+--------------------------------+", + "| 1 | 10.1 | 03/01/09 |", + "| 1 | 10.1 | 04/01/09 |", + "| 1 | 10.1 | 02/01/09 |", + "+---------+------------+--------------------------------+", + ], + &results + ); + Ok(()) +} +``` + +[`sessioncontext`]: https://docs.rs/datafusion/latest/datafusion/execution/context/struct.SessionContext.html +[`sessioncontext::sql`]: https://docs.rs/datafusion/latest/datafusion/execution/context/struct.SessionContext.html#method.sql +[`sessioncontext::sql_with_options`]: https://docs.rs/datafusion/latest/datafusion/execution/context/struct.SessionContext.html#method.sql_with_options +[`sessionstate`]: https://docs.rs/datafusion/latest/datafusion/execution/session_state/struct.SessionState.html +["using the dataframe api"]: ../library-user-guide/using-the-dataframe-api.md diff --git a/versions/55.0.0/_sources/library-user-guide/working-with-exprs.md.txt b/versions/55.0.0/_sources/library-user-guide/working-with-exprs.md.txt new file mode 100644 index 0000000000000..2f15fa90610d9 --- /dev/null +++ b/versions/55.0.0/_sources/library-user-guide/working-with-exprs.md.txt @@ -0,0 +1,369 @@ + + +# Working with `Expr`s + + + +`Expr` is short for "expression". It is a core abstraction in DataFusion for representing a computation, and follows the standard "expression tree" abstraction found in most compilers and databases. + +For example, the SQL expression `a + b` would be represented as an `Expr` with a `BinaryExpr` variant. A `BinaryExpr` has a left and right `Expr` and an operator. + +As another example, the SQL expression `a + b * c` would be represented as an `Expr` with a `BinaryExpr` variant. The left `Expr` would be `a` and the right `Expr` would be another `BinaryExpr` with a left `Expr` of `b` and a right `Expr` of `c`. As a classic expression tree, this would look like: + +```text + ┌────────────────────┐ + │ BinaryExpr │ + │ op: + │ + └────────────────────┘ + ▲ ▲ + ┌───────┘ └────────────────┐ + │ │ +┌────────────────────┐ ┌────────────────────┐ +│ Expr::Col │ │ BinaryExpr │ +│ col: a │ │ op: * │ +└────────────────────┘ └────────────────────┘ + ▲ ▲ + ┌────────┘ └─────────┐ + │ │ + ┌────────────────────┐ ┌────────────────────┐ + │ Expr::Col │ │ Expr::Col │ + │ col: b │ │ col: c │ + └────────────────────┘ └────────────────────┘ +``` + +As the writer of a library, you can use `Expr`s to represent computations that you want to perform. This guide will walk you through how to make your own scalar UDF as an `Expr` and how to rewrite `Expr`s to inline the simple UDF. + +## Arrow Schema and DataFusion DFSchema + +Apache Arrow `Schema` provides a lightweight structure for defining data, and Apache Datafusion `DFSchema` extends it with extra information such as column qualifiers and functional dependencies. Column qualifiers are multi part path to the table e.g table, schema, catalog. Functional Dependency is the relationship between attributes(characteristics) of a table related to each other. + +### Difference between Schema and DFSchema + +- Schema: A fundamental component of Apache Arrow, `Schema` defines a dataset's structure, specifying column names and their data types. + + > Please see [Struct Schema](https://docs.rs/arrow-schema/latest/arrow_schema/struct.Schema.html) for a detailed document of Arrow Schema. + +- DFSchema: Extending `Schema`, `DFSchema` incorporates qualifiers such as table names, enabling it to carry additional context when required. This is particularly valuable for managing queries across multiple tables. + > Please see [Struct DFSchema](https://docs.rs/datafusion/latest/datafusion/common/struct.DFSchema.html) for a detailed document of DFSchema. + +### How to convert between Schema and DFSchema + +From Schema to DFSchema: Use `DFSchema::try_from_qualified_schema` with a table name and original schema, for detailed code example please see [creating-qualified-schemas](https://docs.rs/datafusion/latest/datafusion/common/struct.DFSchema.html#creating-qualified-schemas). + +From DFSchema to Schema: Since the `Into` trait has been implemented for DFSchema to convert it into an Arrow Schema, for detailed code example please see [converting-back-to-arrow-schema](https://docs.rs/datafusion/latest/datafusion/common/struct.DFSchema.html#converting-back-to-arrow-schema). + +## Creating and Evaluating `Expr`s + +Please see [expr_api.rs](https://github.com/apache/datafusion/blob/main/datafusion-examples/examples/query_planning/expr_api.rs) for well commented code for creating, evaluating, simplifying, and analyzing `Expr`s. + +## A Scalar UDF Example + +We'll use a `ScalarUDF` expression as our example. This necessitates implementing an actual UDF, and for ease we'll use the same example from the [adding UDFs](functions/adding-udfs.md) guide. + +So assuming you've written that function, you can use it to create an `Expr`: + +```rust +# use std::sync::Arc; +# use datafusion::arrow::array::{ArrayRef, Int64Array}; +# use datafusion::common::cast::as_int64_array; +# use datafusion::common::Result; +# use datafusion::logical_expr::ColumnarValue; +# +# pub fn add_one(args: &[ColumnarValue]) -> Result { +# // Error handling omitted for brevity +# let args = ColumnarValue::values_to_arrays(args)?; +# let i64s = as_int64_array(&args[0])?; +# +# let new_array = i64s +# .iter() +# .map(|array_elem| array_elem.map(|value| value + 1)) +# .collect::(); +# +# Ok(ColumnarValue::from(Arc::new(new_array) as ArrayRef)) +# } +use datafusion::logical_expr::{Volatility, create_udf}; +use datafusion::arrow::datatypes::DataType; +use datafusion::logical_expr::{col, lit}; + +let add_one_udf = create_udf( + "add_one", + vec![DataType::Int64], + DataType::Int64, + Volatility::Immutable, + Arc::new(add_one), +); + +// make the expr `add_one(5)` +let expr = add_one_udf.call(vec![lit(5)]); + +// make the expr `add_one(my_column)` +let expr = add_one_udf.call(vec![col("my_column")]); +``` + +If you'd like to learn more about `Expr`s, before we get into the details of creating and rewriting them, you can read the [expression user-guide](./../user-guide/expressions.md). + +## Rewriting `Expr`s + +There are several examples of rewriting and working with `Expr`s: + +- [expr_api.rs](https://github.com/apache/datafusion/blob/main/datafusion-examples/examples/query_planning/expr_api.rs) +- [analyzer_rule.rs](https://github.com/apache/datafusion/blob/main/datafusion-examples/examples/query_planning/analyzer_rule.rs) +- [optimizer_rule.rs](https://github.com/apache/datafusion/blob/main/datafusion-examples/examples/query_planning/optimizer_rule.rs) + +Rewriting Expressions is the process of taking an `Expr` and transforming it into another `Expr`. This is useful for a number of reasons, including: + +- Simplifying `Expr`s to make them easier to evaluate +- Optimizing `Expr`s to make them faster to evaluate +- Converting `Expr`s to other forms, e.g. converting a `BinaryExpr` to a `CastExpr` + +In our example, we'll use rewriting to update our `add_one` UDF, to be rewritten as a `BinaryExpr` with a `Literal` of 1. We're effectively inlining the UDF. + +### Rewriting with `transform` + +To implement the inlining, we'll need to write a function that takes an `Expr` and returns a `Result`. If the expression is _not_ to be rewritten `Transformed::no` is used to wrap the original `Expr`. If the expression _is_ to be rewritten, `Transformed::yes` is used to wrap the new `Expr`. + +```rust +use datafusion::common::Result; +use datafusion::common::tree_node::{Transformed, TreeNode}; +use datafusion::logical_expr::{col, lit, Expr}; +use datafusion::logical_expr::{ScalarUDF}; + +fn rewrite_add_one(expr: Expr) -> Result> { + expr.transform(&|expr| { + Ok(match expr { + Expr::ScalarFunction(scalar_func) if scalar_func.func.inner().name() == "add_one" => { + let input_arg = scalar_func.args[0].clone(); + let new_expression = input_arg + lit(1i64); + + Transformed::yes(new_expression) + } + _ => Transformed::no(expr), + }) + }) +} +``` + +### Creating an `OptimizerRule` + +In DataFusion, an `OptimizerRule` is a trait that supports rewriting `Expr`s that appear in various parts of the `LogicalPlan`. It follows DataFusion's general mantra of trait implementations to drive behavior. + +We'll call our rule `AddOneInliner` and implement the `OptimizerRule` trait. The `OptimizerRule` trait has two methods: + +- `name` - returns the name of the rule +- `rewrite` - takes a `LogicalPlan` and `&dyn OptimizerConfig`, and returns a `Result>`. If the rule is able to optimize the plan, it returns `Transformed::yes` with the optimized plan. If the rule is not able to optimize the plan, it returns `Transformed::no`. + +```rust +use std::sync::Arc; +use datafusion::common::Result; +use datafusion::common::tree_node::{Transformed, TreeNode}; +use datafusion::logical_expr::{col, lit, Expr, LogicalPlan, LogicalPlanBuilder}; +use datafusion::optimizer::{OptimizerRule, OptimizerConfig, OptimizerContext, Optimizer}; + +# fn rewrite_add_one(expr: Expr) -> Result> { +# expr.transform(&|expr| { +# Ok(match expr { +# Expr::ScalarFunction(scalar_func) if scalar_func.func.inner().name() == "add_one" => { +# let input_arg = scalar_func.args[0].clone(); +# let new_expression = input_arg + lit(1i64); +# +# Transformed::yes(new_expression) +# } +# _ => Transformed::no(expr), +# }) +# }) +# } + +#[derive(Default, Debug)] +struct AddOneInliner {} + +impl OptimizerRule for AddOneInliner { + fn name(&self) -> &str { + "add_one_inliner" + } + + fn rewrite( + &self, + plan: LogicalPlan, + _config: &dyn OptimizerConfig, + ) -> Result> { + // Map over the expressions and rewrite them + let new_expressions: Vec = plan + .expressions() + .into_iter() + .map(|expr| rewrite_add_one(expr)) + .collect::>>()? // returns Vec> + .into_iter() + .map(|transformed| transformed.data) + .collect(); + + let inputs = plan.inputs().into_iter().cloned().collect::>(); + + let plan: Result = plan.with_new_exprs(new_expressions, inputs); + + plan.map(|p| Transformed::yes(p)) + } +} +``` + +Note the use of `rewrite_add_one` which is mapped over `plan.expressions()` to rewrite the expressions, then `plan.with_new_exprs` is used to create a new `LogicalPlan` with the rewritten expressions. + +We're almost there. Let's just test our rule works properly. + +## Testing the Rule + +Testing the rule is fairly simple, we can create a SessionState with our rule and then create a DataFrame and run a query. The logical plan will be optimized by our rule. + +```rust +# use std::sync::Arc; +# use datafusion::common::Result; +# use datafusion::common::tree_node::{Transformed, TreeNode}; +# use datafusion::logical_expr::{col, lit, Expr, LogicalPlan, LogicalPlanBuilder}; +# use datafusion::optimizer::{OptimizerRule, OptimizerConfig, OptimizerContext, Optimizer}; +# use datafusion::arrow::array::{ArrayRef, Int64Array}; +# use datafusion::common::cast::as_int64_array; +# use datafusion::logical_expr::ColumnarValue; +# use datafusion::logical_expr::{Volatility, create_udf}; +# use datafusion::arrow::datatypes::DataType; +# +# fn rewrite_add_one(expr: Expr) -> Result> { +# expr.transform(&|expr| { +# Ok(match expr { +# Expr::ScalarFunction(scalar_func) if scalar_func.func.inner().name() == "add_one" => { +# let input_arg = scalar_func.args[0].clone(); +# let new_expression = input_arg + lit(1i64); +# +# Transformed::yes(new_expression) +# } +# _ => Transformed::no(expr), +# }) +# }) +# } +# +# #[derive(Default, Debug)] +# struct AddOneInliner {} +# +# impl OptimizerRule for AddOneInliner { +# fn name(&self) -> &str { +# "add_one_inliner" +# } +# +# fn rewrite( +# &self, +# plan: LogicalPlan, +# _config: &dyn OptimizerConfig, +# ) -> Result> { +# // Map over the expressions and rewrite them +# let new_expressions: Vec = plan +# .expressions() +# .into_iter() +# .map(|expr| rewrite_add_one(expr)) +# .collect::>>()? // returns Vec> +# .into_iter() +# .map(|transformed| transformed.data) +# .collect(); +# +# let inputs = plan.inputs().into_iter().cloned().collect::>(); +# +# let plan: Result = plan.with_new_exprs(new_expressions, inputs); +# +# plan.map(|p| Transformed::yes(p)) +# } +# } +# +# pub fn add_one(args: &[ColumnarValue]) -> Result { +# // Error handling omitted for brevity +# let args = ColumnarValue::values_to_arrays(args)?; +# let i64s = as_int64_array(&args[0])?; +# +# let new_array = i64s +# .iter() +# .map(|array_elem| array_elem.map(|value| value + 1)) +# .collect::(); +# +# Ok(ColumnarValue::from(Arc::new(new_array) as ArrayRef)) +# } + +use datafusion::execution::context::SessionContext; + +#[tokio::main] +async fn main() -> Result<()> { + + let ctx = SessionContext::new(); + // ctx.add_optimizer_rule(Arc::new(AddOneInliner {})); + + let add_one_udf = create_udf( + "add_one", + vec![DataType::Int64], + DataType::Int64, + Volatility::Immutable, + Arc::new(add_one), + ); + ctx.register_udf(add_one_udf); + + let sql = "SELECT add_one(5) AS added_one"; + // let plan = ctx.sql(sql).await?.into_unoptimized_plan().clone(); + let plan = ctx.sql(sql).await?.into_optimized_plan()?.clone(); + + let expected = r#"Projection: Int64(6) AS added_one + EmptyRelation: rows=1"#; + + assert_eq!(plan.to_string(), expected); + + Ok(()) +} +``` + +This plan is optimized as: + +```text +Projection: add_one(Int64(5)) AS added_one + -> Projection: Int64(5) + Int64(1) AS added_one + -> Projection: Int64(6) AS added_one +``` + +I.e. the `add_one` UDF has been inlined into the projection. + +## Getting the data type of the expression + +The `arrow::datatypes::DataType` of the expression can be obtained by calling the `get_type` given something that implements `Expr::Schemable`, for example a `DFschema` object: + +```rust +use arrow::datatypes::{DataType, Field}; +use datafusion::common::DFSchema; +use datafusion::logical_expr::{col, ExprSchemable}; +use std::collections::HashMap; + +// Get the type of an expression that adds 2 columns. Adding an Int32 +// and Float32 results in Float32 type +let expr = col("c1") + col("c2"); +let schema = DFSchema::from_unqualified_fields( + vec![ + Field::new("c1", DataType::Int32, true), + Field::new("c2", DataType::Float32, true), + ] + .into(), + HashMap::new(), +).unwrap(); +assert_eq!("Float32", format!("{}", expr.get_type(&schema).unwrap())); +``` + +## Conclusion + +In this guide, we've seen how to create `Expr`s programmatically and how to rewrite them. This is useful for simplifying and optimizing `Expr`s. We've also seen how to test our rule to ensure it works properly. diff --git a/versions/55.0.0/_sources/user-guide/arrow-introduction.md.txt b/versions/55.0.0/_sources/user-guide/arrow-introduction.md.txt new file mode 100644 index 0000000000000..5a225782adfdb --- /dev/null +++ b/versions/55.0.0/_sources/user-guide/arrow-introduction.md.txt @@ -0,0 +1,256 @@ + + +# Gentle Arrow Introduction + +```{contents} +:local: +:depth: 2 +``` + +## Overview + +DataFusion uses [Apache Arrow] as its native in-memory format, so anyone using DataFusion will likely interact with Arrow at some point. This guide introduces the key Arrow concepts you need to know to effectively use DataFusion. + +Apache Arrow defines a standardized columnar representation for in-memory data. This enables different systems and languages (e.g., Rust and Python) to share data with zero-copy interchange, avoiding serialization overhead. In addition to zero copy interchange, Arrow also standardizes best practice columnar data representation enabling high performance analytical processing through vectorized execution. + +## Columnar Layout + +Quick visual: row-major (left) vs Arrow's columnar layout (right). For a deeper primer, see the [arrow2 guide]. + +```text +Traditional Row Storage: Arrow Columnar Storage: +┌──────────────────┐ ┌─────────┬─────────┬──────────┐ +│ id │ name │ age │ │ id │ name │ age │ +├────┼──────┼──────┤ ├─────────┼─────────┼──────────┤ +│ 1 │ A │ 30 │ │ [1,2,3] │ [A,B,C] │[30,25,35]│ +│ 2 │ B │ 25 │ └─────────┴─────────┴──────────┘ +│ 3 │ C │ 35 │ ↑ ↑ ↑ +└──────────────────┘ Int32Array StringArray Int32Array +(read entire rows) (process entire columns at once) +``` + +## `RecordBatch` + +Arrow's standard unit for packaging data is the **[`RecordBatch`]**. + +A **[`RecordBatch`]** represents a horizontal slice of a table—a collection of equal-length columnar arrays that conform to a defined schema. Each column within the slice is a contiguous Arrow array, and all columns have the same number of rows (length). This chunked, immutable unit enables efficient streaming and parallel execution. + +Think of it as having two perspectives: + +- **Columnar inside**: Each column (`id`, `name`, `age`) is a contiguous array optimized for vectorized operations +- **Row-chunked externally**: The batch represents a chunk of rows (e.g., rows 1-1000), making it a manageable unit for streaming + +RecordBatches are **immutable snapshots**—once created, they cannot be modified. Any transformation produces a _new_ RecordBatch, enabling safe parallel processing without locks or coordination overhead. + +This design allows DataFusion to process streams of row-based chunks while gaining maximum performance from the columnar layout. + +## Streaming Through the Engine + +DataFusion processes queries as pull-based pipelines where operators request batches from their inputs. This streaming approach enables early result production, bounds memory usage (spilling to disk only when necessary), and naturally supports parallel execution across multiple CPU cores. + +For example, given the following query: + +```sql +SELECT name FROM 'data.parquet' WHERE id > 10 +``` + +The DataFusion Pipeline looks like this: + +```text + +┌─────────────┐ ┌──────────────┐ ┌────────────────┐ ┌──────────────────┐ ┌──────────┐ +│ Parquet │───▶│ Scan │───▶│ Filter │───▶│ Projection │───▶│ Results │ +│ File │ │ Operator │ │ Operator │ │ Operator │ │ │ +└─────────────┘ └──────────────┘ └────────────────┘ └──────────────────┘ └──────────┘ + (reads data) (id > 10) (keeps "name" col) + RecordBatch ───▶ RecordBatch ────▶ RecordBatch ────▶ RecordBatch +``` + +In this pipeline, [`RecordBatch`]es are the "packages" of columnar data that flow between the different stages of query execution. Each operator processes batches incrementally, enabling the system to produce results before reading the entire input. + +## Creating `ArrayRef` and `RecordBatch`es + +Sometimes you need to create Arrow data programmatically rather than reading from files. + +The first thing needed is creating an Arrow Array, for each column. [arrow-rs] provides array builders and `From` impls to create arrays from Rust vectors. + +```rust +use arrow::array::{StringArray, Int32Array}; +// Create an Int32Array from a vector of i32 values +let ids = Int32Array::from(vec![1, 2, 3]); +// There are similar constructors for other array types, e.g., StringArray, Float64Array, etc. +let names = StringArray::from(vec![Some("alice"), None, Some("carol")]); +``` + +Every element in an Arrow array can be "null" (aka missing). Often, arrays are +created from `Option` values to indicate nullability (e.g., `Some("alice")` +vs `None` above). + +Note: You'll see [`Arc`] used frequently in the code—Arrow arrays are wrapped in +[`Arc`] (atomically reference-counted pointers) to enable cheap, thread-safe +sharing across operators and tasks. [`ArrayRef`] is simply a type alias for +`Arc`. To create an `ArrayRef`, wrap your array in `Arc::new(...)` as shown below. + +```rust +use std::sync::Arc; +# use arrow::array::{ArrayRef, Int32Array, StringArray}; +// To get an ArrayRef, wrap the Int32Array in an Arc. +// (note you will often have to explicitly type annotate to ArrayRef) +let arr: ArrayRef = Arc::new(Int32Array::from(vec![1, 2, 3])); + +// you can also store Strings and other types in ArrayRefs +let arr: ArrayRef = Arc::new( + StringArray::from(vec![Some("alice"), None, Some("carol")]) +); +``` + +To create a [`RecordBatch`], you need to define its [`Schema`] (the column names and types) and provide the corresponding columns as [`ArrayRef`]s as shown below: + +```rust +# use std::sync::Arc; +# use arrow_schema::ArrowError; +# use arrow::array::{ArrayRef, Int32Array, StringArray, RecordBatch}; +use arrow_schema::{DataType, Field, Schema}; + +// Create the columns as Arrow arrays +let ids = Int32Array::from(vec![1, 2, 3]); +let names = StringArray::from(vec![Some("alice"), None, Some("carol")]); +// Create the schema +let schema = Arc::new(Schema::new(vec![ + Field::new("id", DataType::Int32, false), // false means non-nullable + Field::new("name", DataType::Utf8, true), // true means nullable +])); +// Assemble the columns +let cols: Vec = vec![ + Arc::new(ids), + Arc::new(names) +]; +// Finally, create the RecordBatch +RecordBatch::try_new(schema, cols).expect("Failed to create RecordBatch"); +``` + +## Working with `ArrayRef` and `RecordBatch` + +Most DataFusion APIs are in terms of [`ArrayRef`] and [`RecordBatch`]. To work with the +underlying data, you typically downcast the [`ArrayRef`] to its concrete type +(e.g., [`Int32Array`]). + +To do so either use the `as_any().downcast_ref::()` method or the +`as_::()` helper method from the [AsArray] trait. + +[asarray]: https://docs.rs/arrow-array/latest/arrow_array/cast/trait.AsArray.html + +```rust +# use std::sync::Arc; +# use arrow::datatypes::{DataType, Int32Type}; +# use arrow::array::{AsArray, ArrayRef, Int32Array, RecordBatch}; +# let arr: ArrayRef = Arc::new(Int32Array::from(vec![1, 2, 3])); +// First check the data type of the array +match arr.data_type() { + &DataType::Int32 => { + // Downcast to Int32Array + let int_array = arr.as_primitive::(); + // Now you can access Int32Array methods + for i in 0..int_array.len() { + println!("Value at index {}: {}", i, int_array.value(i)); + } + } + _ => { + println ! ("Array is not of type Int32"); + } +} +``` + +The following two downcasting methods are equivalent: + +```rust +# use std::sync::Arc; +# use arrow::datatypes::{DataType, Int32Type}; +# use arrow::array::{AsArray, ArrayRef, Int32Array, RecordBatch}; +# let arr: ArrayRef = Arc::new(Int32Array::from(vec![1, 2, 3])); +// Downcast to Int32Array using as_any +let int_array1 = arr.as_any().downcast_ref::().unwrap(); +// This is the same as using the as_::() helper +let int_array2 = arr.as_primitive::(); +assert_eq!(int_array1, int_array2); +``` + +## Common Pitfalls + +When working with Arrow and RecordBatches, watch out for these common issues: + +- **Schema consistency**: All batches in a stream must share the exact same [`Schema`]. For example, you can't have one batch where a column is [`Int32`] and the next where it's [`Int64`], even if the values would fit +- **Immutability**: Arrays are immutable—to "modify" data, you must build new arrays or new RecordBatches. For instance, to change a value in an array, you'd create a new array with the updated value +- **Row by Row Processing**: Avoid iterating over Arrays element by element when possible, and use Arrow's built-in [compute kernels] instead +- **Type mismatches**: Mixed input types across files may require explicit casts. For example, a string column `"123"` from a CSV file won't automatically join with an integer column `123` from a Parquet file—you'll need to cast one to match the other. Use Arrow's [`cast`] kernel where appropriate +- **Batch size assumptions**: Don't assume a particular batch size; always iterate until the stream ends. One file might produce 8192-row batches while another produces 1024-row batches + +[compute kernels]: https://docs.rs/arrow/latest/arrow/compute/index.html + +## Further reading + +**Arrow Documentation:** + +- [Arrow Format Introduction](https://arrow.apache.org/docs/format/Intro.html) - Understand the Arrow specification and why it enables zero-copy data sharing +- [Arrow Columnar Format](https://arrow.apache.org/docs/format/Columnar.html) - Deep dive into memory layout for performance optimization +- [Arrow Rust Documentation](https://docs.rs/arrow/latest/arrow/) - Complete API reference for the Rust implementation + +**Key API References:** + +- [RecordBatch](https://docs.rs/arrow-array/latest/arrow_array/struct.RecordBatch.html) - The fundamental data structure for columnar data (a table slice) +- [ArrayRef](https://docs.rs/arrow-array/latest/arrow_array/array/type.ArrayRef.html) - Represents a reference-counted Arrow array (single column) +- [DataType](https://docs.rs/arrow-schema/latest/arrow_schema/enum.DataType.html) - Enum of all supported Arrow data types (e.g., Int32, Utf8) +- [Schema](https://docs.rs/arrow-schema/latest/arrow_schema/struct.Schema.html) - Describes the structure of a RecordBatch (column names and types) + +[apache arrow]: https://arrow.apache.org/docs/index.html +[arrow-rs]: https://github.com/apache/arrow-rs +[`arc`]: https://doc.rust-lang.org/std/sync/struct.Arc.html +[`arrayref`]: https://docs.rs/arrow-array/latest/arrow_array/array/type.ArrayRef.html +[`cast`]: https://docs.rs/arrow/latest/arrow/compute/fn.cast.html +[`field`]: https://docs.rs/arrow-schema/latest/arrow_schema/struct.Field.html +[`schema`]: https://docs.rs/arrow-schema/latest/arrow_schema/struct.Schema.html +[`datatype`]: https://docs.rs/arrow-schema/latest/arrow_schema/enum.DataType.html +[`int32array`]: https://docs.rs/arrow/latest/arrow/array/type.Int32Array.html +[`stringarray`]: https://docs.rs/arrow/latest/arrow/array/type.StringArray.html +[`int32`]: https://docs.rs/arrow-schema/latest/arrow_schema/enum.DataType.html#variant.Int32 +[`int64`]: https://docs.rs/arrow-schema/latest/arrow_schema/enum.DataType.html#variant.Int64 +[extension points]: ../library-user-guide/extensions.md +[`tableprovider`]: https://docs.rs/datafusion/latest/datafusion/datasource/trait.TableProvider.html +[custom table providers guide]: ../library-user-guide/custom-table-providers.md +[user-defined functions (udfs)]: ../library-user-guide/functions/adding-udfs.md +[custom optimizer rules and physical operators]: ../library-user-guide/extending-operators.md +[`executionplan`]: https://docs.rs/datafusion/latest/datafusion/physical_plan/trait.ExecutionPlan.html +[`.register_table()`]: https://docs.rs/datafusion/latest/datafusion/execution/context/struct.SessionContext.html#method.register_table +[`.sql()`]: https://docs.rs/datafusion/latest/datafusion/execution/context/struct.SessionContext.html#method.sql +[`.show()`]: https://docs.rs/datafusion/latest/datafusion/dataframe/struct.DataFrame.html#method.show +[`memtable`]: https://docs.rs/datafusion/latest/datafusion/datasource/struct.MemTable.html +[`sessioncontext`]: https://docs.rs/datafusion/latest/datafusion/execution/context/struct.SessionContext.html +[`csvreadoptions`]: https://docs.rs/datafusion/latest/datafusion/datasource/file_format/options/struct.CsvReadOptions.html +[`parquetreadoptions`]: https://docs.rs/datafusion/latest/datafusion/datasource/file_format/options/struct.ParquetReadOptions.html +[`recordbatch`]: https://docs.rs/arrow-array/latest/arrow_array/struct.RecordBatch.html +[`read_csv`]: https://docs.rs/datafusion/latest/datafusion/execution/context/struct.SessionContext.html#method.read_csv +[`read_parquet`]: https://docs.rs/datafusion/latest/datafusion/execution/context/struct.SessionContext.html#method.read_parquet +[`read_json`]: https://docs.rs/datafusion/latest/datafusion/execution/context/struct.SessionContext.html#method.read_json +[`read_avro`]: https://docs.rs/datafusion/latest/datafusion/execution/context/struct.SessionContext.html#method.read_avro +[`dataframe`]: https://docs.rs/datafusion/latest/datafusion/dataframe/struct.DataFrame.html +[`.collect()`]: https://docs.rs/datafusion/latest/datafusion/dataframe/struct.DataFrame.html#method.collect +[arrow2 guide]: https://jorgecarleitao.github.io/arrow2/main/guide/arrow.html#what-is-apache-arrow +[configuration settings]: configs.md +[`datafusion.execution.batch_size`]: configs.md#setting-configuration-options diff --git a/versions/55.0.0/_sources/user-guide/cli/datasources.md.txt b/versions/55.0.0/_sources/user-guide/cli/datasources.md.txt new file mode 100644 index 0000000000000..59a6b0aa43284 --- /dev/null +++ b/versions/55.0.0/_sources/user-guide/cli/datasources.md.txt @@ -0,0 +1,492 @@ + + +# Local Files / Directories + +Files can be queried directly by enclosing the file, directory name +or a remote location in single `'` quotes as shown in the examples. + +Create a CSV file to query. + +```shell +$ echo "a,b" > data.csv +$ echo "1,2" >> data.csv +``` + +Query that single file (the CLI also supports parquet, compressed csv, avro, json and more) + +```shell +$ datafusion-cli +DataFusion CLI v17.0.0 +> select * from 'data.csv'; ++---+---+ +| a | b | ++---+---+ +| 1 | 2 | ++---+---+ +1 row in set. Query took 0.007 seconds. +``` + +You can also query directories of files with compatible schemas: + +```shell +$ ls data_dir/ +data.csv data2.csv +``` + +```shell +$ datafusion-cli +DataFusion CLI v16.0.0 +> select * from 'data_dir'; ++---+---+ +| a | b | ++---+---+ +| 3 | 4 | +| 1 | 2 | ++---+---+ +2 rows in set. Query took 0.007 seconds. +``` + +# Remote Files / Directories + +You can also query directly any remote location supported by DataFusion without +registering the location as a table. +For example, to read from a remote parquet file via HTTP(S) you can use the following: + +```sql +select count(*) from 'https://datasets.clickhouse.com/hits_compatible/athena_partitioned/hits_1.parquet' ++----------+ +| COUNT(*) | ++----------+ +| 1000000 | ++----------+ +1 row in set. Query took 0.595 seconds. +``` + +To read from an AWS S3 or GCS, use `s3` or `gs` as a protocol prefix. For +example, to read a file in an S3 bucket named `my-data-bucket` use the URL +`s3://my-data-bucket`and set the relevant access credentials as environmental +variables (e.g. for AWS S3 you can use `AWS_ACCESS_KEY_ID` and +`AWS_SECRET_ACCESS_KEY`). + +```sql +> select count(*) from 's3://altinity-clickhouse-data/nyc_taxi_rides/data/tripdata_parquet/'; ++------------+ +| count(*) | ++------------+ +| 1310903963 | ++------------+ +``` + +See the [`CREATE EXTERNAL TABLE`](#create-external-table) section below for +additional configuration options. + +# `CREATE EXTERNAL TABLE` + +It is also possible to create a table backed by files or remote locations via +`CREATE EXTERNAL TABLE` as shown below. Note that DataFusion does not support +wildcards (e.g. `*`) in file paths; instead, specify the directory path directly +to read all compatible files in that directory. + +For example, to create a table `hits` backed by a local parquet file named `hits.parquet`: + +```sql +CREATE EXTERNAL TABLE hits +STORED AS PARQUET +LOCATION 'hits.parquet'; +``` + +To create a table `hits` backed by a remote parquet file via HTTP(S): + +```sql +CREATE EXTERNAL TABLE hits +STORED AS PARQUET +LOCATION 'https://datasets.clickhouse.com/hits_compatible/athena_partitioned/hits_1.parquet'; +``` + +In both cases, `hits` now can be queried as a regular table: + +```sql +select count(*) from hits; ++----------+ +| COUNT(*) | ++----------+ +| 1000000 | ++----------+ +1 row in set. Query took 0.344 seconds. +``` + +## Reading from standard input + +On Unix-like systems you can pipe data into the CLI and query it by pointing the +`LOCATION` at the `/dev/stdin` pseudo-file: + +```console +$ cat hits.csv | datafusion-cli -c " +CREATE EXTERNAL TABLE hits STORED AS CSV LOCATION '/dev/stdin' OPTIONS ('format.has_header' 'true'); +SELECT count(*) FROM hits;" +``` + +This works for CSV, JSON, and Parquet. Because standard input is not seekable +(and Parquet stores its metadata at the end of the file), the CLI buffers the +entire input into memory before querying it, so the data must fit in memory. +Standard input is read only once: the buffered contents are reused for any +further tables backed by `/dev/stdin` in the same session. Those tables must +declare the same `STORED AS` format as the first one; a differing format is +rejected with an error. + +The SQL must be passed with `-c`/`--command` or `-f`/`--file` so that standard +input is free to carry the data. In the interactive shell (and when SQL is +piped to the CLI without `-c`/`-f`) standard input carries the SQL itself, and +`LOCATION '/dev/stdin'` returns an error. + +**Why Wildcards Are Not Supported** + +Although wildcards (e.g., _.parquet or \*\*/_.parquet) may work for local +filesystems in some cases, they are not supported by DataFusion CLI. This +is because wildcards are not universally applicable across all storage backends +(e.g., S3, GCS). Instead, DataFusion expects the user to specify the directory +path, and it will automatically read all compatible files within that directory. + +For example, the following usage is not supported: + +```sql +CREATE EXTERNAL TABLE test ( + message TEXT, + day DATE +) +STORED AS PARQUET +LOCATION 'gs://bucket/*.parquet'; +``` + +Instead, you should use: + +```sql +CREATE EXTERNAL TABLE test ( + message TEXT, + day DATE +) +STORED AS PARQUET +LOCATION 'gs://bucket/my_table/'; +``` + +When specifying a directory path that has a Hive compliant partition structure, by default, DataFusion CLI will +automatically parse and incorporate the Hive columns and their values into the table's schema and data. Given the +following remote object paths: + +```console +gs://bucket/my_table/a=1/b=100/file1.parquet +gs://bucket/my_table/a=2/b=200/file2.parquet +``` + +`my_table` can be queried and filtered on the Hive columns: + +```sql +CREATE EXTERNAL TABLE my_table +STORED AS PARQUET +LOCATION 'gs://bucket/my_table/'; + +SELECT count(*) FROM my_table WHERE b=200; ++----------+ +| count(*) | ++----------+ +| 1 | ++----------+ +``` + +# Formats + +## Parquet + +The schema information for parquet will be derived automatically. + +Register a single file parquet datasource + +```sql +CREATE EXTERNAL TABLE taxi +STORED AS PARQUET +LOCATION '/mnt/nyctaxi/tripdata.parquet'; +``` + +Register a single folder parquet datasource. Note: All files inside must be valid +parquet files and have compatible schemas + +:::{note} +Paths must end in Slash `/` +: The path must end in `/` otherwise DataFusion will treat the path as a file and not a directory +::: + +```sql +CREATE EXTERNAL TABLE taxi +STORED AS PARQUET +LOCATION '/mnt/nyctaxi/'; +``` + +### Parquet Specific Options + +You can specify additional options for parquet files using the `OPTIONS` clause. +For example, to read and write a parquet directory with encryption settings you could use: + +```sql +CREATE EXTERNAL TABLE encrypted_parquet_table +( +double_field double, +float_field float +) +STORED AS PARQUET LOCATION 'pq/' OPTIONS ( + -- encryption + 'format.crypto.file_encryption.encrypt_footer' 'true', + 'format.crypto.file_encryption.footer_key_as_hex' '30313233343536373839303132333435', -- b"0123456789012345" + 'format.crypto.file_encryption.column_key_as_hex::double_field' '31323334353637383930313233343530', -- b"1234567890123450" + 'format.crypto.file_encryption.column_key_as_hex::float_field' '31323334353637383930313233343531', -- b"1234567890123451" + -- decryption + 'format.crypto.file_decryption.footer_key_as_hex' '30313233343536373839303132333435', -- b"0123456789012345" + 'format.crypto.file_decryption.column_key_as_hex::double_field' '31323334353637383930313233343530', -- b"1234567890123450" + 'format.crypto.file_decryption.column_key_as_hex::float_field' '31323334353637383930313233343531', -- b"1234567890123451" +); +``` + +Here the keys are specified in hexadecimal format because they are binary data. These can be encoded in SQL using: + +```sql +select encode('0123456789012345', 'hex'); +/* ++----------------------------------------------+ +| encode(Utf8("0123456789012345"),Utf8("hex")) | ++----------------------------------------------+ +| 30313233343536373839303132333435 | ++----------------------------------------------+ +*/ +``` + +For more details on the available options, refer to the Rust +[TableParquetOptions](https://docs.rs/datafusion/latest/datafusion/common/config/struct.TableParquetOptions.html) +documentation in DataFusion. + +## CSV + +DataFusion will infer the CSV schema automatically or you can provide it explicitly. + +Register a single file csv datasource with a header row: + +```sql +CREATE EXTERNAL TABLE test +STORED AS CSV +LOCATION '/path/to/aggregate_test_100.csv' +OPTIONS ('has_header' 'true'); +``` + +Register a single file csv datasource with explicitly defined schema: + +```sql +CREATE EXTERNAL TABLE test ( + c1 VARCHAR NOT NULL, + c2 INT NOT NULL, + c3 SMALLINT NOT NULL, + c4 SMALLINT NOT NULL, + c5 INT NOT NULL, + c6 BIGINT NOT NULL, + c7 SMALLINT NOT NULL, + c8 INT NOT NULL, + c9 BIGINT NOT NULL, + c10 VARCHAR NOT NULL, + c11 FLOAT NOT NULL, + c12 DOUBLE NOT NULL, + c13 VARCHAR NOT NULL +) +STORED AS CSV +LOCATION '/path/to/aggregate_test_100.csv'; +``` + +# Locations + +## HTTP(s) + +To read from a remote parquet file via HTTP(S): + +```sql +CREATE EXTERNAL TABLE hits +STORED AS PARQUET +LOCATION 'https://datasets.clickhouse.com/hits_compatible/athena_partitioned/hits_1.parquet'; +``` + +## S3 + +DataFusion CLI supports configuring [AWS S3](https://aws.amazon.com/s3/) via the +`CREATE EXTERNAL TABLE` statement and standard AWS configuration methods (via the +[`aws-config`] AWS SDK crate). + +To create an external table from a file in an S3 bucket with explicit +credentials: + +```sql +CREATE EXTERNAL TABLE test +STORED AS PARQUET +OPTIONS( + 'aws.access_key_id' '******', + 'aws.secret_access_key' '******', + 'aws.region' 'us-east-2' +) +LOCATION 's3://bucket/path/file.parquet'; +``` + +To create an external table using environment variables: + +```bash +$ export AWS_DEFAULT_REGION=us-east-2 +$ export AWS_SECRET_ACCESS_KEY=****** +$ export AWS_ACCESS_KEY_ID=****** + +$ datafusion-cli +`datafusion-cli v21.0.0 +> create CREATE TABLE test STORED AS PARQUET LOCATION 's3://bucket/path/file.parquet'; +0 rows in set. Query took 0.374 seconds. +> select * from test; ++----------+----------+ +| column_1 | column_2 | ++----------+----------+ +| 1 | 2 | ++----------+----------+ +1 row in set. Query took 0.171 seconds. +``` + +To read from a public S3 bucket without signatures, use the +`aws.SKIP_SIGNATURE` option: + +```sql +CREATE EXTERNAL TABLE nyc_taxi_rides +STORED AS PARQUET LOCATION 's3://altinity-clickhouse-data/nyc_taxi_rides/data/tripdata_parquet/' +OPTIONS(aws.SKIP_SIGNATURE true); +``` + +Credentials are taken in this order of precedence: + +1. Explicitly specified in the `OPTIONS` clause of the `CREATE EXTERNAL TABLE` statement. +2. Determined by [`aws-config`] crate (standard environment variables such as `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` as well as other AWS specific features). + +If no credentials are specified, DataFusion CLI will use unsigned requests to S3, +which allows reading from public buckets. + +Supported configuration options are: + +| Environment Variable | Configuration Option | Description | +| ---------------------------------------- | ----------------------- | ---------------------------------------------- | +| `AWS_ACCESS_KEY_ID` | `aws.access_key_id` | | +| `AWS_SECRET_ACCESS_KEY` | `aws.secret_access_key` | | +| `AWS_DEFAULT_REGION` | `aws.region` | | +| `AWS_ENDPOINT` | `aws.endpoint` | | +| `AWS_SESSION_TOKEN` | `aws.token` | | +| `AWS_CONTAINER_CREDENTIALS_RELATIVE_URI` | | See [IAM Roles] | +| `AWS_ALLOW_HTTP` | | If "true", permit HTTP connections without TLS | +| `AWS_SKIP_SIGNATURE` | `aws.skip_signature` | If "true", does not sign requests | +| | `aws.nosign` | Alias for `skip_signature` | + +[iam roles]: https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task-iam-roles.html +[`aws-config`]: https://docs.rs/aws-config/latest/aws_config/ + +## OSS + +[Alibaba cloud OSS](https://www.alibabacloud.com/product/object-storage-service) data sources must have connection credentials configured + +```sql +CREATE EXTERNAL TABLE test +STORED AS PARQUET +OPTIONS( + 'aws.access_key_id' '******', + 'aws.secret_access_key' '******', + 'aws.oss.endpoint' 'https://bucket.oss-cn-hangzhou.aliyuncs.com' +) +LOCATION 'oss://bucket/path/file.parquet'; +``` + +The supported OPTIONS are + +- access_key_id +- secret_access_key +- endpoint + +Note that the `endpoint` format of oss needs to be: `https://{bucket}.{oss-region-endpoint}` + +## COS + +[Tencent cloud COS](https://cloud.tencent.com/product/cos) data sources data sources must have connection credentials configured + +```sql +CREATE EXTERNAL TABLE test +STORED AS PARQUET +OPTIONS( + 'aws.access_key_id' '******', + 'aws.secret_access_key' '******', + 'aws.cos.endpoint' 'https://cos.ap-singapore.myqcloud.com' +) +LOCATION 'cos://bucket/path/file.parquet'; +``` + +The supported OPTIONS are: + +- access_key_id +- secret_access_key +- endpoint + +Note that the `endpoint` format of urls must be: `https://cos.{cos-region-endpoint}` + +## GCS + +[Google Cloud Storage](https://cloud.google.com/storage) data sources must have connection credentials configured + +For example, to create an external table from a file in a GCS bucket + +```sql +CREATE EXTERNAL TABLE test +STORED AS PARQUET +OPTIONS( + 'gcp.service_account_path' '/tmp/gcs.json', +) +LOCATION 'gs://bucket/path/file.parquet'; +``` + +It is also possible to specify the access information using environment variables: + +```bash +$ export GOOGLE_SERVICE_ACCOUNT=/tmp/gcs.json + +$ datafusion-cli +DataFusion CLI v21.0.0 +> create external table test stored as parquet location 'gs://bucket/path/file.parquet'; +0 rows in set. Query took 0.374 seconds. +> select * from test; ++----------+----------+ +| column_1 | column_2 | ++----------+----------+ +| 1 | 2 | ++----------+----------+ +1 row in set. Query took 0.171 seconds. +``` + +Supported configuration options are: + +| Environment Variable | Configuration Option | Description | +| -------------------------------- | ---------------------------------- | ---------------------------------------- | +| `GOOGLE_SERVICE_ACCOUNT` | `gcp.service_account_path` | location of service account file | +| `GOOGLE_SERVICE_ACCOUNT_PATH` | `gcp.service_account_path` | (alias) location of service account file | +| `SERVICE_ACCOUNT` | `gcp.service_account_path` | (alias) location of service account file | +| `GOOGLE_SERVICE_ACCOUNT_KEY` | `gcp.service_account_key` | JSON serialized service account key | +| `GOOGLE_APPLICATION_CREDENTIALS` | `gcp.application_credentials_path` | location of application credentials file | +| `GOOGLE_BUCKET` | | bucket name | +| `GOOGLE_BUCKET_NAME` | | (alias) bucket name | diff --git a/versions/55.0.0/_sources/user-guide/cli/functions.md.txt b/versions/55.0.0/_sources/user-guide/cli/functions.md.txt new file mode 100644 index 0000000000000..baf054ef5a12c --- /dev/null +++ b/versions/55.0.0/_sources/user-guide/cli/functions.md.txt @@ -0,0 +1,227 @@ + + +# CLI Specific Functions + +`datafusion-cli` comes with build-in functions that are not included in the +DataFusion SQL engine by default. These functions are: + +## `parquet_metadata` + +The `parquet_metadata` table function can be used to inspect detailed metadata +about a parquet file such as statistics, sizes, and other information. This can +be helpful to understand how parquet files are structured. + +For example, to see information about the `"WatchID"` column in the +`hits.parquet` file, you can use: + +```sql +SELECT path_in_schema, row_group_id, row_group_num_rows, stats_min, stats_max, total_compressed_size +FROM parquet_metadata('hits.parquet') +WHERE path_in_schema = '"WatchID"' +LIMIT 3; + ++----------------+--------------+--------------------+---------------------+---------------------+-----------------------+ +| path_in_schema | row_group_id | row_group_num_rows | stats_min | stats_max | total_compressed_size | ++----------------+--------------+--------------------+---------------------+---------------------+-----------------------+ +| "WatchID" | 0 | 450560 | 4611687214012840539 | 9223369186199968220 | 3883759 | +| "WatchID" | 1 | 612174 | 4611689135232456464 | 9223371478009085789 | 5176803 | +| "WatchID" | 2 | 344064 | 4611692774829951781 | 9223363791697310021 | 3031680 | ++----------------+--------------+--------------------+---------------------+---------------------+-----------------------+ +3 rows in set. Query took 0.053 seconds. +``` + +The returned table has the following columns for each row for each column chunk +in the file. Please refer to the [Parquet Documentation] for more information in +the meaning of these fields. + +[parquet documentation]: https://parquet.apache.org/ + +| column_name | data_type | Description | +| ----------------------- | --------- | --------------------------------------------------------------------------------------------------- | +| filename | Utf8 | Name of the file | +| row_group_id | Int64 | Row group index the column chunk belongs to | +| row_group_num_rows | Int64 | Count of rows stored in the row group | +| row_group_num_columns | Int64 | Total number of columns in the row group (same for all row groups) | +| row_group_bytes | Int64 | Number of bytes used to store the row group (not including metadata) | +| column_id | Int64 | ID of the column | +| file_offset | Int64 | Offset within the file that this column chunk's data begins | +| num_values | Int64 | Total number of values in this column chunk | +| path_in_schema | Utf8 | "Path" (column name) of the column chunk in the schema | +| type | Utf8 | Parquet data type of the column chunk | +| stats_min | Utf8 | The minimum value for this column chunk, if stored in the statistics, cast to a string | +| stats_max | Utf8 | The maximum value for this column chunk, if stored in the statistics, cast to a string | +| stats_null_count | Int64 | Number of null values in this column chunk, if stored in the statistics | +| stats_distinct_count | Int64 | Number of distinct values in this column chunk, if stored in the statistics | +| stats_min_value | Utf8 | Same as `stats_min` | +| stats_max_value | Utf8 | Same as `stats_max` | +| compression | Utf8 | Block level compression (e.g. `SNAPPY`) used for this column chunk | +| encodings | Utf8 | All block level encodings (e.g. `[PLAIN_DICTIONARY, PLAIN, RLE]`) used for this column chunk | +| index_page_offset | Int64 | Offset in the file of the [`page index`], if any | +| dictionary_page_offset | Int64 | Offset in the file of the dictionary page, if any | +| data_page_offset | Int64 | Offset in the file of the first data page, if any | +| total_compressed_size | Int64 | Number of bytes the column chunk's data after encoding and compression (what is stored in the file) | +| total_uncompressed_size | Int64 | Number of bytes the column chunk's data after encoding | + +[`page index`]: https://github.com/apache/parquet-format/blob/master/PageIndex.md + +## `metadata_cache` + +The `metadata_cache` function shows information about the default File Metadata Cache that is used by the +[`ListingTable`] implementation in DataFusion. This cache is used to speed up +reading metadata from files when scanning directories with many files. + +For example, after creating a table with the [CREATE EXTERNAL TABLE](../sql/ddl.md#create-external-table) +command: + +```sql +> create external table hits + stored as parquet + location 's3://clickhouse-public-datasets/hits_compatible/athena_partitioned/'; +``` + +You can inspect the metadata cache by querying the `metadata_cache` function: + +```sql +> select * from metadata_cache(); ++----------------------------------------------------+---------------------+-----------------+---------------------------------------+---------+---------------------+------+------------------+ +| path | file_modified | file_size_bytes | e_tag | version | metadata_size_bytes | hits | extra | ++----------------------------------------------------+---------------------+-----------------+---------------------------------------+---------+---------------------+------+------------------+ +| hits_compatible/athena_partitioned/hits_61.parquet | 2022-07-03T15:40:34 | 117270944 | "5db11cad1ca0d80d748fc92c914b010a-6" | NULL | 212949 | 0 | page_index=false | +| hits_compatible/athena_partitioned/hits_32.parquet | 2022-07-03T15:37:17 | 94506004 | "2f7db49a9fe242179590b615b94a39d2-5" | NULL | 278157 | 0 | page_index=false | +| hits_compatible/athena_partitioned/hits_40.parquet | 2022-07-03T15:38:07 | 142508647 | "9e5852b45a469d5a05bf270a286eab8a-8" | NULL | 212917 | 0 | page_index=false | +| hits_compatible/athena_partitioned/hits_93.parquet | 2022-07-03T15:44:07 | 127987774 | "751100bf0dac7d489b9836abf3108b99-7" | NULL | 278318 | 0 | page_index=false | +| . | ++----------------------------------------------------+---------------------+-----------------+---------------------------------------+---------+---------------------+------+------------------+ +``` + +Since `metadata_cache` is a normal table function, you can use it in most places you can use +a table reference. + +For example, to get the total size consumed by the cached entries: + +```sql +> select sum(metadata_size_bytes) from metadata_cache(); ++-------------------------------------------+ +| sum(metadata_cache().metadata_size_bytes) | ++-------------------------------------------+ +| 22972345 | ++-------------------------------------------+ +``` + +The columns of the returned table are: + +| column_name | data_type | Description | +| ------------------- | --------- | ----------------------------------------------------------------------------------------- | +| path | Utf8 | File path relative to the object store / filesystem root | +| file_modified | Timestamp | Last modified time of the file | +| file_size_bytes | UInt64 | Size of the file in bytes | +| e_tag | Utf8 | [Entity Tag] (ETag) of the file if available | +| version | Utf8 | Version of the file if available (for object stores that support versioning) | +| metadata_size_bytes | UInt64 | Size of the cached metadata in memory (not its thrift encoded form) | +| hits | UInt64 | Number of times the cached metadata has been accessed | +| extra | Utf8 | Extra information about the cached metadata (e.g., if page index information is included) | + +## `statistics_cache` + +Similarly to the `metadata_cache`, the `statistics_cache` function can be used to show information +about the File Statistics Cache that is used by the [`ListingTable`] implementation in DataFusion. +For the statistics to be collected, the config `datafusion.execution.collect_statistics` must be +enabled. + +You can inspect the statistics cache by querying the `statistics_cache` function. For example: + +```sql +> select * from statistics_cache(); ++------------------+---------------------+-----------------+------------------------+---------+-----------------+-------------+--------------------+-----------------------+ +| path | file_modified | file_size_bytes | e_tag | version | num_rows | num_columns | table_size_bytes | statistics_size_bytes | ++------------------+---------------------+-----------------+------------------------+---------+-----------------+-------------+--------------------+-----------------------+ +| .../hits.parquet | 2022-06-25T22:22:22 | 14779976446 | 0-5e24d1ee16380-370f48 | NULL | Exact(99997497) | 105 | Exact(36445943240) | 0 | ++------------------+---------------------+-----------------+------------------------+---------+-----------------+-------------+--------------------+-----------------------+ +``` + +The columns of the returned table are: + +| column_name | data_type | Description | +| --------------------- | --------- | ---------------------------------------------------------------------------- | +| path | Utf8 | File path relative to the object store / filesystem root | +| file_modified | Timestamp | Last modified time of the file | +| file_size_bytes | UInt64 | Size of the file in bytes | +| e_tag | Utf8 | [Entity Tag] (ETag) of the file if available | +| version | Utf8 | Version of the file if available (for object stores that support versioning) | +| num_rows | Utf8 | Number of rows in the table | +| num_columns | UInt64 | Number of columns in the table | +| table_size_bytes | Utf8 | Size of the table, in bytes | +| hits | UInt64 | Number of times the cached file statistics has been accessed | +| statistics_size_bytes | UInt64 | Size of the cached statistics in memory | + +## `list_files_cache` + +The `list_files_cache` function shows information about the `ListFilesCache` that is used by the [`ListingTable`] implementation in DataFusion. When creating a [`ListingTable`], DataFusion lists the files in the table's location and caches results in the `ListFilesCache`. Subsequent queries against the same table can reuse this cached information instead of re-listing the files. Cache entries are scoped to tables. + +You can inspect the cache by querying the `list_files_cache` function. For example, + +```sql +> set datafusion.runtime.list_files_cache_ttl = "30s"; +> create external table overturemaps +stored as parquet +location 's3://overturemaps-us-west-2/release/2025-12-17.0/theme=base/type=infrastructure'; +0 row(s) fetched. +> select table, path, metadata_size_bytes, expires_in, unnest(metadata_list)['file_size_bytes'] as file_size_bytes, unnest(metadata_list)['e_tag'] as e_tag from list_files_cache() limit 10; ++--------------+-----------------------------------------------------+---------------------+-----------------------------------+-----------------+---------------------------------------+ +| table | path | metadata_size_bytes | expires_in | file_size_bytes | e_tag | ++--------------+-----------------------------------------------------+---------------------+-----------------------------------+-----------------+---------------------------------------+ +| overturemaps | release/2025-12-17.0/theme=base/type=infrastructure | 2750 | 0 days 0 hours 0 mins 25.264 secs | 999055952 | "35fc8fbe8400960b54c66fbb408c48e8-60" | +| overturemaps | release/2025-12-17.0/theme=base/type=infrastructure | 2750 | 0 days 0 hours 0 mins 25.264 secs | 975592768 | "8a16e10b722681cdc00242564b502965-59" | +| overturemaps | release/2025-12-17.0/theme=base/type=infrastructure | 2750 | 0 days 0 hours 0 mins 25.264 secs | 1082925747 | "24cd13ddb5e0e438952d2499f5dabe06-65" | +| overturemaps | release/2025-12-17.0/theme=base/type=infrastructure | 2750 | 0 days 0 hours 0 mins 25.264 secs | 1008425557 | "37663e31c7c64d4ef355882bcd47e361-61" | +| overturemaps | release/2025-12-17.0/theme=base/type=infrastructure | 2750 | 0 days 0 hours 0 mins 25.264 secs | 1065561905 | "4e7c50d2d1b3c5ed7b82b4898f5ac332-64" | +| overturemaps | release/2025-12-17.0/theme=base/type=infrastructure | 2750 | 0 days 0 hours 0 mins 25.264 secs | 1045655427 | "8fff7e6a72d375eba668727c55d4f103-63" | +| overturemaps | release/2025-12-17.0/theme=base/type=infrastructure | 2750 | 0 days 0 hours 0 mins 25.264 secs | 1086822683 | "b67167d8022d778936c330a52a5f1922-65" | +| overturemaps | release/2025-12-17.0/theme=base/type=infrastructure | 2750 | 0 days 0 hours 0 mins 25.264 secs | 1016732378 | "6d70857a0473ed9ed3fc6e149814168b-61" | +| overturemaps | release/2025-12-17.0/theme=base/type=infrastructure | 2750 | 0 days 0 hours 0 mins 25.264 secs | 991363784 | "c9cafb42fcbb413f851691c895dd7c2b-60" | +| overturemaps | release/2025-12-17.0/theme=base/type=infrastructure | 2750 | 0 days 0 hours 0 mins 25.264 secs | 1032469715 | "7540252d0d67158297a67038a3365e0f-62" | ++--------------+-----------------------------------------------------+---------------------+-----------------------------------+-----------------+---------------------------------------+ +``` + +The columns of the returned table are: + +| column_name | data_type | Description | +| ------------------- | ------------ | ------------------------------------------------------------------- | +| table | Utf8 | Name of the table | +| path | Utf8 | File path relative to the object store / filesystem root | +| metadata_size_bytes | UInt64 | Size of the cached metadata in memory (not its thrift encoded form) | +| expires_in | Duration(ms) | Last modified time of the file | +| hits | UInt64 | Number of times the cached metadata has been accessed | +| metadata_list | List(Struct) | List of metadatas, one for each file under the path. | + +A metadata struct in the metadata_list contains the following fields: + +```text +{ + "file_path": "release/2025-12-17.0/theme=base/type=infrastructure/part-00000-d556e455-e0c5-4940-b367-daff3287a952-c000.zstd.parquet", + "file_modified": "2025-12-17T22:20:29", + "file_size_bytes": 999055952, + "e_tag": "35fc8fbe8400960b54c66fbb408c48e8-60", + "version": null +} +``` + +[`listingtable`]: https://docs.rs/datafusion/latest/datafusion/datasource/listing/struct.ListingTable.html +[entity tag]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag diff --git a/versions/55.0.0/_sources/user-guide/cli/index.rst.txt b/versions/55.0.0/_sources/user-guide/cli/index.rst.txt new file mode 100644 index 0000000000000..325b0dce3fb19 --- /dev/null +++ b/versions/55.0.0/_sources/user-guide/cli/index.rst.txt @@ -0,0 +1,28 @@ +.. 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. + +DataFusion CLI +============== + +.. toctree:: + :maxdepth: 3 + + overview + installation + usage + datasources + functions diff --git a/versions/55.0.0/_sources/user-guide/cli/installation.md.txt b/versions/55.0.0/_sources/user-guide/cli/installation.md.txt new file mode 100644 index 0000000000000..a3dc4bd2bdb49 --- /dev/null +++ b/versions/55.0.0/_sources/user-guide/cli/installation.md.txt @@ -0,0 +1,64 @@ + + +# Installation + +## Install and run using Cargo + +To build and install the latest release of `datafusion-cli` from source, do: + +```shell +cargo install datafusion-cli +# Updating crates.io index +# Installing datafusion-cli v37.0.0 +# Updating crates.io index +# ... +``` + +## Install and run using Homebrew (on MacOS) + +`datafusion-cli` can also be installed via [Homebrew] (on MacOS) like this: + +[homebrew]: https://docs.brew.sh/Installation + +```bash +brew install datafusion +# ... +# ==> Pouring datafusion--37.0.0.arm64_sonoma.bottle.tar.gz +# 🍺 /opt/homebrew/Cellar/datafusion/37.0.0: 9 files, 63.0MB +# ==> Running `brew cleanup datafusion`... +``` + +## Run using Docker + +There is no officially published Docker image for the DataFusion CLI, so it is necessary to build from source +instead. + +Use the following commands to clone this repository and build a Docker image containing the CLI tool. Note +that there is `.dockerignore` file in the root of the repository that may need to be deleted in order for +this to work. + +```bash +git clone https://github.com/apache/datafusion +cd datafusion +# Note: the build can take a while +docker build -f datafusion-cli/Dockerfile . --tag datafusion-cli +# You can also bind persistent storage with `-v /path/to/data:/data` +docker run --rm -it datafusion-cli +``` diff --git a/versions/55.0.0/_sources/user-guide/cli/overview.md.txt b/versions/55.0.0/_sources/user-guide/cli/overview.md.txt new file mode 100644 index 0000000000000..e0228d3ea00e4 --- /dev/null +++ b/versions/55.0.0/_sources/user-guide/cli/overview.md.txt @@ -0,0 +1,45 @@ + + +# Overview + +DataFusion CLI (`datafusion-cli`) is an interactive command-line utility for executing +SQL queries against any supported data files. + +While intended as an example of how to use DataFusion, `datafusion-cli` offers a +full range of SQL and support reading and writing CSV, Parquet, JSON, Arrow and +Avro, from local files, directories, or remote locations such as S3. + +Here is an example of how to run a SQL query against a local file, `hits.parquet`: + +```shell +$ datafusion-cli +DataFusion CLI v37.0.0 +> select count(distinct "URL") from 'hits.parquet'; ++----------------------------------+ +| COUNT(DISTINCT hits.parquet.URL) | ++----------------------------------+ +| 18342019 | ++----------------------------------+ +1 row(s) fetched. +Elapsed 1.969 seconds. +``` + +For more information, see the [Installation](installation.md), [Usage Guide](usage.md) +and [Data Sources](datasources.md) sections. diff --git a/versions/55.0.0/_sources/user-guide/cli/usage.md.txt b/versions/55.0.0/_sources/user-guide/cli/usage.md.txt new file mode 100644 index 0000000000000..75c6698f007a5 --- /dev/null +++ b/versions/55.0.0/_sources/user-guide/cli/usage.md.txt @@ -0,0 +1,257 @@ + + +# Usage + +See the current usage using `datafusion-cli --help`: + +```bash +Command Line Client for DataFusion query engine. + +Usage: datafusion-cli [OPTIONS] + +Options: + -p, --data-path + Path to your data, default to current directory + -b, --batch-size + The batch size of each query, or use DataFusion default + -c, --command [...] + Execute the given command string(s), then exit. Commands are expected to be non empty. + -m, --memory-limit + The memory pool limitation (e.g. '10g'), default to None (no limit) + -f, --file [...] + Execute commands from file(s), then exit + -r, --rc [...] + Run the provided files on startup instead of ~/.datafusionrc + --format + [default: automatic] [possible values: csv, tsv, table, json, nd-json, automatic] + -q, --quiet + Reduce printing other than the results and work quietly + --mem-pool-type + Specify the memory pool type 'greedy' or 'fair' [default: greedy] + --top-memory-consumers + The number of top memory consumers to display when query fails due to memory exhaustion. To disable memory consumer tracking, set this value to 0 [default: 3] + --maxrows + The max number of rows to display for 'Table' format + [possible values: numbers(0/10/...), inf(no limit)] [default: 40] + --color + Enables console syntax highlighting + -d, --disk-limit + Available disk space for spilling queries (e.g. '10g'), default to None (uses DataFusion's default value of '100g') + --object-store-profiling + Specify the default object_store_profiling mode, defaults to 'disabled'. + [possible values: disabled, summary, trace] [default: Disabled] + -h, --help + Print help + -V, --version + Print version +``` + +## Commands + +Available commands inside DataFusion CLI are: + +- Quit + +```bash +> \q +``` + +- Help + +```bash +> \? +``` + +- ListTables + +```bash +> \d +``` + +- DescribeTable + +```bash +> \d table_name +``` + +- QuietMode + +```bash +> \quiet [true|false] +``` + +- list function + +```bash +> \h +``` + +- Search and describe function + +```bash +> \h function +``` + +- Object Store Profiling Mode + +```bash +> \object_store_profiling [disabled|summary|trace] +``` + +When enabled, prints detailed information about object store (I/O) operations +performed during query execution to STDOUT. + +```sql +> \object_store_profiling trace +ObjectStore Profile mode set to Trace +> select count(*) from 'https://datasets.clickhouse.com/hits_compatible/athena_partitioned/hits_1.parquet'; ++----------+ +| count(*) | ++----------+ +| 1000000 | ++----------+ +1 row(s) fetched. +Elapsed 0.552 seconds. + +Object Store Profiling +Instrumented Object Store: instrument_mode: Trace, inner: HttpStore +2025-10-17T18:08:48.457992+00:00 operation=Get duration=0.043592s size=8 range: bytes=174965036-174965043 path=hits_compatible/athena_partitioned/hits_1.parquet +2025-10-17T18:08:48.501878+00:00 operation=Get duration=0.031542s size=34322 range: bytes=174930714-174965035 path=hits_compatible/athena_partitioned/hits_1.parquet + +Summaries: ++-----------+----------+-----------+-----------+-----------+-----------+-------+ +| Operation | Metric | min | max | avg | sum | count | ++-----------+----------+-----------+-----------+-----------+-----------+-------+ +| Get | duration | 0.031542s | 0.043592s | 0.037567s | 0.075133s | 2 | +| Get | size | 8 B | 34322 B | 17165 B | 34330 B | 2 | ++-----------+----------+-----------+-----------+-----------+-----------+-------+ +``` + +## Supported SQL + +In addition to the normal [SQL supported in DataFusion], `datafusion-cli` also +supports additional statements and commands: + +[sql supported in datafusion]: ../sql/index.rst + +### `SHOW ALL [VERBOSE]` + +Show configuration options + +```sql +> show all; + ++-------------------------------------------------+---------+ +| name | value | ++-------------------------------------------------+---------+ +| datafusion.execution.batch_size | 8192 | +| datafusion.execution.coalesce_batches | true | +| datafusion.execution.time_zone | UTC | +| datafusion.explain.logical_plan_only | false | +| datafusion.explain.physical_plan_only | false | +| datafusion.optimizer.filter_null_join_keys | false | +| datafusion.optimizer.skip_failed_rules | true | ++-------------------------------------------------+---------+ + +``` + +### `SHOW