From c53c86fdb4655040442e74a329df9282f932e681 Mon Sep 17 00:00:00 2001 From: Chelsea Lin Date: Mon, 23 Jun 2025 11:55:45 -0700 Subject: [PATCH 01/28] chore: add compile_filter (#1817) --- bigframes/core/compile/sqlglot/compiler.py | 7 ++++++ .../core/compile/sqlglot/scalar_compiler.py | 7 ++++++ bigframes/core/compile/sqlglot/sqlglot_ir.py | 10 ++++++++ .../test_compile_filter/out.sql | 25 +++++++++++++++++++ .../compile/sqlglot/test_compile_filter.py | 25 +++++++++++++++++++ 5 files changed, 74 insertions(+) create mode 100644 tests/unit/core/compile/sqlglot/snapshots/test_compile_filter/test_compile_filter/out.sql create mode 100644 tests/unit/core/compile/sqlglot/test_compile_filter.py diff --git a/bigframes/core/compile/sqlglot/compiler.py b/bigframes/core/compile/sqlglot/compiler.py index 3b7abd8463..a38078b687 100644 --- a/bigframes/core/compile/sqlglot/compiler.py +++ b/bigframes/core/compile/sqlglot/compiler.py @@ -211,6 +211,13 @@ def compile_projection( ) return child.project(projected_cols) + @_compile_node.register + def compile_filter( + self, node: nodes.FilterNode, child: ir.SQLGlotIR + ) -> ir.SQLGlotIR: + condition = scalar_compiler.compile_scalar_expression(node.predicate) + return child.filter(condition) + @_compile_node.register def compile_concat( self, node: nodes.ConcatNode, *children: ir.SQLGlotIR diff --git a/bigframes/core/compile/sqlglot/scalar_compiler.py b/bigframes/core/compile/sqlglot/scalar_compiler.py index 00ec892620..59deb9c8f3 100644 --- a/bigframes/core/compile/sqlglot/scalar_compiler.py +++ b/bigframes/core/compile/sqlglot/scalar_compiler.py @@ -99,3 +99,10 @@ def compile_addop(op: ops.AddOp, left: TypedExpr, right: TypedExpr) -> sge.Expre # Numerical addition return sge.Add(this=left.expr, expression=right.expr) + + +def compile_ge( + op: ops.ge_op, left: TypedExpr, right: TypedExpr # type: ignore[valid-type] +) -> sge.Expression: + + return sge.GTE(this=left.expr, expression=right.expr) diff --git a/bigframes/core/compile/sqlglot/sqlglot_ir.py b/bigframes/core/compile/sqlglot/sqlglot_ir.py index 47dab209d0..3766ab3266 100644 --- a/bigframes/core/compile/sqlglot/sqlglot_ir.py +++ b/bigframes/core/compile/sqlglot/sqlglot_ir.py @@ -250,6 +250,16 @@ def project( new_expr = self._encapsulate_as_cte().select(*projected_cols_expr, append=True) return SQLGlotIR(expr=new_expr, uid_gen=self.uid_gen) + def filter( + self, + condition: sge.Expression, + ) -> SQLGlotIR: + """Filters the query with the given condition.""" + new_expr = self._encapsulate_as_cte() + return SQLGlotIR( + expr=new_expr.where(condition, append=False), uid_gen=self.uid_gen + ) + def insert( self, destination: bigquery.TableReference, diff --git a/tests/unit/core/compile/sqlglot/snapshots/test_compile_filter/test_compile_filter/out.sql b/tests/unit/core/compile/sqlglot/snapshots/test_compile_filter/test_compile_filter/out.sql new file mode 100644 index 0000000000..9ca7fb6a74 --- /dev/null +++ b/tests/unit/core/compile/sqlglot/snapshots/test_compile_filter/test_compile_filter/out.sql @@ -0,0 +1,25 @@ +WITH `bfcte_0` AS ( + SELECT + `int64_col` AS `bfcol_0`, + `rowindex` AS `bfcol_1` + FROM `bigframes-dev`.`sqlglot_test`.`scalar_types` +), `bfcte_1` AS ( + SELECT + *, + `bfcol_1` AS `bfcol_5`, + `bfcol_1` AS `bfcol_6`, + `bfcol_0` AS `bfcol_7`, + `bfcol_1` >= 1 AS `bfcol_8` + FROM `bfcte_0` +), `bfcte_2` AS ( + SELECT + * + FROM `bfcte_1` + WHERE + `bfcol_8` +) +SELECT + `bfcol_5` AS `rowindex`, + `bfcol_6` AS `rowindex_1`, + `bfcol_7` AS `int64_col` +FROM `bfcte_2` \ No newline at end of file diff --git a/tests/unit/core/compile/sqlglot/test_compile_filter.py b/tests/unit/core/compile/sqlglot/test_compile_filter.py new file mode 100644 index 0000000000..03b54f289a --- /dev/null +++ b/tests/unit/core/compile/sqlglot/test_compile_filter.py @@ -0,0 +1,25 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import pytest + +import bigframes.pandas as bpd + +pytest.importorskip("pytest_snapshot") + + +def test_compile_filter(scalars_types_df: bpd.DataFrame, snapshot): + bf_df = scalars_types_df[["rowindex", "int64_col"]] + bf_filter = bf_df[bf_df["rowindex"] >= 1] + snapshot.assert_match(bf_filter.sql, "out.sql") From 7a83224cbf38d995321d222830671103cff48607 Mon Sep 17 00:00:00 2001 From: TrevorBergeron Date: Mon, 23 Jun 2025 12:26:57 -0700 Subject: [PATCH 02/28] test: Add slice op cross validation testing (#1837) --- bigframes/session/polars_executor.py | 1 + tests/system/small/engines/test_slicing.py | 56 ++++++++++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 tests/system/small/engines/test_slicing.py diff --git a/bigframes/session/polars_executor.py b/bigframes/session/polars_executor.py index 6e3e15499d..b2f7f5ccd6 100644 --- a/bigframes/session/polars_executor.py +++ b/bigframes/session/polars_executor.py @@ -31,6 +31,7 @@ nodes.OrderByNode, nodes.ReversedNode, nodes.SelectionNode, + nodes.SliceNode, ) _COMPATIBLE_SCALAR_OPS = () diff --git a/tests/system/small/engines/test_slicing.py b/tests/system/small/engines/test_slicing.py new file mode 100644 index 0000000000..7340ff145b --- /dev/null +++ b/tests/system/small/engines/test_slicing.py @@ -0,0 +1,56 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import pytest + +from bigframes.core import array_value, nodes +from bigframes.session import polars_executor +from bigframes.testing.engine_utils import assert_equivalence_execution + +pytest.importorskip("polars") + +# Polars used as reference as its fast and local. Generally though, prefer gbq engine where they disagree. +REFERENCE_ENGINE = polars_executor.PolarsExecutor() + + +@pytest.mark.parametrize("engine", ["polars", "bq"], indirect=True) +@pytest.mark.parametrize( + ("start", "stop", "step"), + [ + (1, None, None), + (None, 4, None), + (None, None, 2), + (None, 50_000_000_000, 1), + (5, 4, None), + (3, None, 2), + (1, 7, 2), + (1, 7, 50_000_000_000), + (-1, -7, -2), + (None, -7, -2), + (-1, None, -2), + (-7, -1, 2), + (-7, -1, None), + (-7, 7, None), + (7, -7, -2), + ], +) +def test_engines_slice( + scalars_array_value: array_value.ArrayValue, + engine, + start, + stop, + step, +): + node = nodes.SliceNode(scalars_array_value.node, start, stop, step) + assert_equivalence_execution(node, REFERENCE_ENGINE, engine) From 186353888db537b561ee994256f998df361b4071 Mon Sep 17 00:00:00 2001 From: Garrett Wu <6505921+GarrettWu@users.noreply.github.com> Date: Mon, 23 Jun 2025 19:26:15 -0700 Subject: [PATCH 03/28] docs: update ai.forecast notebook (#1844) --- .../bq_dataframes_ai_forecast.ipynb | 1691 ++++++++--------- 1 file changed, 838 insertions(+), 853 deletions(-) diff --git a/notebooks/generative_ai/bq_dataframes_ai_forecast.ipynb b/notebooks/generative_ai/bq_dataframes_ai_forecast.ipynb index 05e75b37f0..7734bd815d 100644 --- a/notebooks/generative_ai/bq_dataframes_ai_forecast.ipynb +++ b/notebooks/generative_ai/bq_dataframes_ai_forecast.ipynb @@ -71,10 +71,14 @@ "source": [ "PROJECT = \"bigframes-dev\" # replace with your project\n", "\n", + "import warnings\n", + "warnings.filterwarnings(\"ignore\") \n", + "\n", "import bigframes\n", "# Setup project\n", "bigframes.options.bigquery.project = PROJECT\n", "bigframes.options.display.progress_bar = None\n", + "bigframes.options.bigquery.ordering_mode = \"partial\" # Optional: partial ordering mode can accelerate executions and save costs\n", "\n", "import bigframes.pandas as bpd" ] @@ -138,603 +142,603 @@ " \n", " \n", " 0\n", - " 1304531\n", - " 597\n", - " 2016-08-05 10:55:00+00:00\n", - " San Francisco Caltrain 2 (330 Townsend)\n", - " 69\n", - " 2016-08-05 11:05:00+00:00\n", - " Powell Street BART\n", - " 39\n", - " 214\n", - " 95121\n", - " ...\n", - " Subscriber\n", - " <NA>\n", - " <NA>\n", - " <NA>\n", - " <NA>\n", - " <NA>\n", + " 201802092135083596\n", + " 788\n", + " 2018-02-09 21:35:08+00:00\n", + " 10th Ave at E 15th St\n", + " 222\n", + " 2018-02-09 21:48:17+00:00\n", + " 10th Ave at E 15th St\n", + " 222\n", + " 3596\n", " <NA>\n", + " ...\n", " <NA>\n", - " None\n", - " None\n", + " 37.792714\n", + " -122.24878\n", + " 37.792714\n", + " -122.24878\n", + " 1984\n", + " Male\n", + " Yes\n", + " POINT (-122.24878 37.79271)\n", + " POINT (-122.24878 37.79271)\n", " \n", " \n", " 1\n", - " 184870\n", - " 403\n", - " 2014-02-14 14:50:00+00:00\n", - " Howard at 2nd\n", - " 63\n", - " 2014-02-14 14:56:00+00:00\n", - " Commercial at Montgomery\n", - " 45\n", - " 342\n", - " 94122\n", - " ...\n", - " Subscriber\n", - " <NA>\n", - " <NA>\n", - " <NA>\n", - " <NA>\n", + " 20171217135737144\n", + " 1072\n", + " 2017-12-17 13:57:37+00:00\n", + " 10th Ave at E 15th St\n", + " 222\n", + " 2017-12-17 14:15:30+00:00\n", + " 10th Ave at E 15th St\n", + " 222\n", + " 144\n", " <NA>\n", + " ...\n", " <NA>\n", + " 37.792714\n", + " -122.24878\n", + " 37.792714\n", + " -122.24878\n", + " 1984\n", + " Male\n", " <NA>\n", - " None\n", - " None\n", + " POINT (-122.24878 37.79271)\n", + " POINT (-122.24878 37.79271)\n", " \n", " \n", " 2\n", - " 20170702115603836\n", - " 16695\n", - " 2017-07-02 11:56:03+00:00\n", - " Union Square (Powell St at Post St)\n", - " 324\n", - " 2017-07-02 16:34:19+00:00\n", - " Union Square (Powell St at Post St)\n", - " 324\n", - " 836\n", + " 201803261642393539\n", + " 486\n", + " 2018-03-26 16:42:39+00:00\n", + " 10th St at Fallon St\n", + " 201\n", + " 2018-03-26 16:50:46+00:00\n", + " 10th Ave at E 15th St\n", + " 222\n", + " 3539\n", " <NA>\n", " ...\n", " <NA>\n", - " 37.7883\n", - " -122.408531\n", - " 37.7883\n", - " -122.408531\n", - " <NA>\n", - " <NA>\n", - " <NA>\n", - " POINT (-122.40853 37.7883)\n", - " POINT (-122.40853 37.7883)\n", + " 37.797673\n", + " -122.262997\n", + " 37.792714\n", + " -122.24878\n", + " 1984\n", + " Male\n", + " Yes\n", + " POINT (-122.263 37.79767)\n", + " POINT (-122.24878 37.79271)\n", " \n", " \n", " 3\n", - " 1066810\n", - " 953\n", - " 2016-01-21 08:24:00+00:00\n", - " Civic Center BART (7th at Market)\n", - " 72\n", - " 2016-01-21 08:40:00+00:00\n", - " Embarcadero at Sansome\n", - " 60\n", - " 212\n", - " 94103\n", - " ...\n", - " Subscriber\n", - " <NA>\n", - " <NA>\n", - " <NA>\n", - " <NA>\n", - " <NA>\n", + " 201802281657253632\n", + " 560\n", + " 2018-02-28 16:57:25+00:00\n", + " 10th St at Fallon St\n", + " 201\n", + " 2018-02-28 17:06:46+00:00\n", + " 10th Ave at E 15th St\n", + " 222\n", + " 3632\n", " <NA>\n", + " ...\n", " <NA>\n", - " None\n", - " None\n", + " 37.797673\n", + " -122.262997\n", + " 37.792714\n", + " -122.24878\n", + " 1984\n", + " Male\n", + " Yes\n", + " POINT (-122.263 37.79767)\n", + " POINT (-122.24878 37.79271)\n", " \n", " \n", " 4\n", - " 220481\n", - " 679\n", - " 2014-03-19 19:20:00+00:00\n", - " San Francisco Caltrain 2 (330 Townsend)\n", - " 69\n", - " 2014-03-19 19:31:00+00:00\n", - " Civic Center BART (7th at Market)\n", - " 72\n", - " 478\n", - " 94107\n", - " ...\n", - " Subscriber\n", - " <NA>\n", - " <NA>\n", + " 201708152357422491\n", + " 965\n", + " 2017-08-15 23:57:42+00:00\n", + " 10th St at Fallon St\n", + " 201\n", + " 2017-08-16 00:13:48+00:00\n", + " 10th Ave at E 15th St\n", + " 222\n", + " 2491\n", " <NA>\n", + " ...\n", " <NA>\n", + " 37.797673\n", + " -122.262997\n", + " 37.792714\n", + " -122.24878\n", " <NA>\n", " <NA>\n", " <NA>\n", - " None\n", - " None\n", + " POINT (-122.263 37.79767)\n", + " POINT (-122.24878 37.79271)\n", " \n", " \n", " 5\n", - " 738474\n", - " 358\n", - " 2015-04-23 16:45:00+00:00\n", - " 2nd at Folsom\n", - " 62\n", - " 2015-04-23 16:51:00+00:00\n", - " Steuart at Market\n", - " 74\n", - " 443\n", - " 94105\n", - " ...\n", - " Subscriber\n", - " <NA>\n", - " <NA>\n", - " <NA>\n", - " <NA>\n", - " <NA>\n", + " 201801161800473291\n", + " 489\n", + " 2018-01-16 18:00:47+00:00\n", + " 10th St at Fallon St\n", + " 201\n", + " 2018-01-16 18:08:56+00:00\n", + " 10th Ave at E 15th St\n", + " 222\n", + " 3291\n", " <NA>\n", + " ...\n", " <NA>\n", - " None\n", - " None\n", + " 37.797673\n", + " -122.262997\n", + " 37.792714\n", + " -122.24878\n", + " 1984\n", + " Male\n", + " Yes\n", + " POINT (-122.263 37.79767)\n", + " POINT (-122.24878 37.79271)\n", " \n", " \n", " 6\n", - " 229264\n", - " 286\n", - " 2014-03-27 17:56:00+00:00\n", - " Embarcadero at Sansome\n", - " 60\n", - " 2014-03-27 18:01:00+00:00\n", - " Davis at Jackson\n", - " 42\n", - " 342\n", - " 94133\n", - " ...\n", - " Subscriber\n", - " <NA>\n", - " <NA>\n", - " <NA>\n", - " <NA>\n", - " <NA>\n", + " 201802201913231257\n", + " 596\n", + " 2018-02-20 19:13:23+00:00\n", + " 10th St at Fallon St\n", + " 201\n", + " 2018-02-20 19:23:19+00:00\n", + " 10th Ave at E 15th St\n", + " 222\n", + " 1257\n", " <NA>\n", + " ...\n", " <NA>\n", - " None\n", - " None\n", + " 37.797673\n", + " -122.262997\n", + " 37.792714\n", + " -122.24878\n", + " 1984\n", + " Male\n", + " Yes\n", + " POINT (-122.263 37.79767)\n", + " POINT (-122.24878 37.79271)\n", " \n", " \n", " 7\n", - " 352010\n", - " 3621\n", - " 2014-07-06 13:55:00+00:00\n", - " Embarcadero at Sansome\n", - " 60\n", - " 2014-07-06 14:55:00+00:00\n", - " Embarcadero at Sansome\n", - " 60\n", - " 390\n", - " 4038\n", - " ...\n", - " Customer\n", - " <NA>\n", - " <NA>\n", - " <NA>\n", - " <NA>\n", + " 201708242325001279\n", + " 1341\n", + " 2017-08-24 23:25:00+00:00\n", + " 10th St at Fallon St\n", + " 201\n", + " 2017-08-24 23:47:22+00:00\n", + " 10th Ave at E 15th St\n", + " 222\n", + " 1279\n", " <NA>\n", + " ...\n", " <NA>\n", + " 37.797673\n", + " -122.262997\n", + " 37.792714\n", + " -122.24878\n", + " 1969\n", + " Male\n", " <NA>\n", - " None\n", - " None\n", + " POINT (-122.263 37.79767)\n", + " POINT (-122.24878 37.79271)\n", " \n", " \n", " 8\n", - " 156255\n", - " 416\n", - " 2014-01-16 18:06:00+00:00\n", - " Embarcadero at Bryant\n", - " 54\n", - " 2014-01-16 18:13:00+00:00\n", - " San Francisco Caltrain (Townsend at 4th)\n", - " 70\n", - " 510\n", - " 94107\n", - " ...\n", - " Subscriber\n", - " <NA>\n", - " <NA>\n", - " <NA>\n", - " <NA>\n", + " 20170913210653295\n", + " 367\n", + " 2017-09-13 21:06:53+00:00\n", + " 10th St at Fallon St\n", + " 201\n", + " 2017-09-13 21:13:00+00:00\n", + " 10th Ave at E 15th St\n", + " 222\n", + " 295\n", " <NA>\n", + " ...\n", " <NA>\n", + " 37.797673\n", + " -122.262997\n", + " 37.792714\n", + " -122.24878\n", + " 1987\n", + " Male\n", " <NA>\n", - " None\n", - " None\n", + " POINT (-122.263 37.79767)\n", + " POINT (-122.24878 37.79271)\n", " \n", " \n", " 9\n", - " 1040197\n", - " 1054\n", - " 2015-12-15 18:05:00+00:00\n", - " Steuart at Market\n", - " 74\n", - " 2015-12-15 18:22:00+00:00\n", - " San Francisco Caltrain (Townsend at 4th)\n", - " 70\n", - " 700\n", - " 94111\n", - " ...\n", - " Subscriber\n", - " <NA>\n", - " <NA>\n", + " 201708192053311490\n", + " 743\n", + " 2017-08-19 20:53:31+00:00\n", + " 2nd Ave at E 18th St\n", + " 200\n", + " 2017-08-19 21:05:54+00:00\n", + " 10th Ave at E 15th St\n", + " 222\n", + " 1490\n", " <NA>\n", + " ...\n", " <NA>\n", + " 37.800214\n", + " -122.25381\n", + " 37.792714\n", + " -122.24878\n", " <NA>\n", " <NA>\n", " <NA>\n", - " None\n", - " None\n", + " POINT (-122.25381 37.80021)\n", + " POINT (-122.24878 37.79271)\n", " \n", " \n", " 10\n", - " 1152693\n", - " 562\n", - " 2016-04-07 08:18:00+00:00\n", - " San Francisco Caltrain (Townsend at 4th)\n", - " 70\n", - " 2016-04-07 08:27:00+00:00\n", - " Steuart at Market\n", - " 74\n", - " 419\n", - " 94158\n", - " ...\n", - " Subscriber\n", - " <NA>\n", - " <NA>\n", + " 20170810204454839\n", + " 1256\n", + " 2017-08-10 20:44:54+00:00\n", + " 2nd Ave at E 18th St\n", + " 200\n", + " 2017-08-10 21:05:50+00:00\n", + " 10th Ave at E 15th St\n", + " 222\n", + " 839\n", " <NA>\n", + " ...\n", " <NA>\n", + " 37.800214\n", + " -122.25381\n", + " 37.792714\n", + " -122.24878\n", " <NA>\n", " <NA>\n", " <NA>\n", - " None\n", - " None\n", + " POINT (-122.25381 37.80021)\n", + " POINT (-122.24878 37.79271)\n", " \n", " \n", " 11\n", - " 201804191735183401\n", - " 887\n", - " 2018-04-19 17:35:18+00:00\n", - " Montgomery St BART Station (Market St at 2nd St)\n", - " 21\n", - " 2018-04-19 17:50:06+00:00\n", - " Civic Center/UN Plaza BART Station (Market St ...\n", - " 44\n", - " 3401\n", + " 201711181823281960\n", + " 353\n", + " 2017-11-18 18:23:28+00:00\n", + " 2nd Ave at E 18th St\n", + " 200\n", + " 2017-11-18 18:29:22+00:00\n", + " 10th Ave at E 15th St\n", + " 222\n", + " 1960\n", " <NA>\n", " ...\n", " <NA>\n", - " 37.789625\n", - " -122.400811\n", - " 37.781074\n", - " -122.411738\n", - " 1979\n", + " 37.800214\n", + " -122.25381\n", + " 37.792714\n", + " -122.24878\n", + " 1988\n", " Male\n", - " No\n", - " POINT (-122.40081 37.78963)\n", - " POINT (-122.41174 37.78107)\n", + " <NA>\n", + " POINT (-122.25381 37.80021)\n", + " POINT (-122.24878 37.79271)\n", " \n", " \n", " 12\n", - " 209283\n", - " 943\n", - " 2014-03-11 09:01:00+00:00\n", - " South Van Ness at Market\n", - " 66\n", - " 2014-03-11 09:16:00+00:00\n", - " Temporary Transbay Terminal (Howard at Beale)\n", - " 55\n", - " 532\n", - " 94105\n", - " ...\n", - " Subscriber\n", - " <NA>\n", - " <NA>\n", - " <NA>\n", - " <NA>\n", - " <NA>\n", + " 201801111613101305\n", + " 858\n", + " 2018-01-11 16:13:10+00:00\n", + " Frank H Ogawa Plaza\n", + " 7\n", + " 2018-01-11 16:27:28+00:00\n", + " 10th Ave at E 15th St\n", + " 222\n", + " 1305\n", " <NA>\n", + " ...\n", " <NA>\n", - " None\n", - " None\n", + " 37.804562\n", + " -122.271738\n", + " 37.792714\n", + " -122.24878\n", + " 1984\n", + " Male\n", + " Yes\n", + " POINT (-122.27174 37.80456)\n", + " POINT (-122.24878 37.79271)\n", " \n", " \n", " 13\n", - " 201708281404312530\n", - " 389\n", - " 2017-08-28 14:04:31+00:00\n", - " 16th St at Prosper St\n", - " 105\n", - " 2017-08-28 14:11:00+00:00\n", - " Mission Playground\n", - " 121\n", - " 2530\n", + " 201712181738372587\n", + " 807\n", + " 2017-12-18 17:38:37+00:00\n", + " Frank H Ogawa Plaza\n", + " 7\n", + " 2017-12-18 17:52:04+00:00\n", + " 10th Ave at E 15th St\n", + " 222\n", + " 2587\n", " <NA>\n", " ...\n", " <NA>\n", - " 37.764285\n", - " -122.431804\n", - " 37.75921\n", - " -122.421339\n", - " 1981\n", + " 37.804562\n", + " -122.271738\n", + " 37.792714\n", + " -122.24878\n", + " 1984\n", " Male\n", " <NA>\n", - " POINT (-122.4318 37.76428)\n", - " POINT (-122.42134 37.75921)\n", + " POINT (-122.27174 37.80456)\n", + " POINT (-122.24878 37.79271)\n", " \n", " \n", " 14\n", - " 20171124115158841\n", - " 384\n", - " 2017-11-24 11:51:58+00:00\n", - " 2nd Ave at E 18th St\n", - " 200\n", - " 2017-11-24 11:58:23+00:00\n", - " El Embarcadero at Grand Ave\n", - " 197\n", - " 841\n", + " 201803161910283751\n", + " 564\n", + " 2018-03-16 19:10:28+00:00\n", + " Frank H Ogawa Plaza\n", + " 7\n", + " 2018-03-16 19:19:52+00:00\n", + " 10th Ave at E 15th St\n", + " 222\n", + " 3751\n", " <NA>\n", " ...\n", " <NA>\n", - " 37.800214\n", - " -122.25381\n", - " 37.808848\n", - " -122.24968\n", - " 1977\n", - " Female\n", - " <NA>\n", - " POINT (-122.25381 37.80021)\n", - " POINT (-122.24968 37.80885)\n", + " 37.804562\n", + " -122.271738\n", + " 37.792714\n", + " -122.24878\n", + " 1987\n", + " Male\n", + " No\n", + " POINT (-122.27174 37.80456)\n", + " POINT (-122.24878 37.79271)\n", " \n", " \n", " 15\n", - " 1321042\n", - " 874\n", - " 2016-08-18 08:14:00+00:00\n", - " San Francisco Caltrain (Townsend at 4th)\n", - " 70\n", - " 2016-08-18 08:29:00+00:00\n", - " Beale at Market\n", - " 56\n", - " 390\n", - " 95050\n", - " ...\n", - " Subscriber\n", - " <NA>\n", - " <NA>\n", - " <NA>\n", - " <NA>\n", - " <NA>\n", + " 201802241826551215\n", + " 1235\n", + " 2018-02-24 18:26:55+00:00\n", + " Frank H Ogawa Plaza\n", + " 7\n", + " 2018-02-24 18:47:31+00:00\n", + " 10th Ave at E 15th St\n", + " 222\n", + " 1215\n", " <NA>\n", + " ...\n", " <NA>\n", - " None\n", - " None\n", + " 37.804562\n", + " -122.271738\n", + " 37.792714\n", + " -122.24878\n", + " 1969\n", + " Male\n", + " No\n", + " POINT (-122.27174 37.80456)\n", + " POINT (-122.24878 37.79271)\n", " \n", " \n", " 16\n", - " 201712131325183120\n", - " 1376\n", - " 2017-12-13 13:25:18+00:00\n", - " Steuart St at Market St\n", - " 16\n", - " 2017-12-13 13:48:14+00:00\n", - " The Embarcadero at Sansome St\n", - " 6\n", - " 3120\n", + " 20171212152403227\n", + " 854\n", + " 2017-12-12 15:24:03+00:00\n", + " Frank H Ogawa Plaza\n", + " 7\n", + " 2017-12-12 15:38:17+00:00\n", + " 10th Ave at E 15th St\n", + " 222\n", + " 227\n", " <NA>\n", " ...\n", " <NA>\n", - " 37.79413\n", - " -122.39443\n", - " 37.80477\n", - " -122.403234\n", - " <NA>\n", - " <NA>\n", + " 37.804562\n", + " -122.271738\n", + " 37.792714\n", + " -122.24878\n", + " 1984\n", + " Male\n", " <NA>\n", - " POINT (-122.39443 37.79413)\n", - " POINT (-122.40323 37.80477)\n", + " POINT (-122.27174 37.80456)\n", + " POINT (-122.24878 37.79271)\n", " \n", " \n", " 17\n", - " 201708310827151646\n", - " 200\n", - " 2017-08-31 08:27:15+00:00\n", - " Powell St BART Station (Market St at 4th St)\n", - " 3\n", - " 2017-08-31 08:30:36+00:00\n", - " Montgomery St BART Station (Market St at 2nd St)\n", - " 21\n", - " 1646\n", + " 201803091621483450\n", + " 857\n", + " 2018-03-09 16:21:48+00:00\n", + " Frank H Ogawa Plaza\n", + " 7\n", + " 2018-03-09 16:36:06+00:00\n", + " 10th Ave at E 15th St\n", + " 222\n", + " 3450\n", " <NA>\n", " ...\n", " <NA>\n", - " 37.786375\n", - " -122.404904\n", - " 37.789625\n", - " -122.400811\n", - " 1988\n", + " 37.804562\n", + " -122.271738\n", + " 37.792714\n", + " -122.24878\n", + " 1984\n", " Male\n", - " <NA>\n", - " POINT (-122.4049 37.78638)\n", - " POINT (-122.40081 37.78963)\n", + " Yes\n", + " POINT (-122.27174 37.80456)\n", + " POINT (-122.24878 37.79271)\n", " \n", " \n", " 18\n", - " 201801251754102907\n", - " 1490\n", - " 2018-01-25 17:54:10+00:00\n", - " Esprit Park\n", - " 126\n", - " 2018-01-25 18:19:01+00:00\n", - " The Embarcadero at Vallejo St\n", - " 8\n", - " 2907\n", + " 201801021932232717\n", + " 914\n", + " 2018-01-02 19:32:23+00:00\n", + " Frank H Ogawa Plaza\n", + " 7\n", + " 2018-01-02 19:47:38+00:00\n", + " 10th Ave at E 15th St\n", + " 222\n", + " 2717\n", " <NA>\n", " ...\n", " <NA>\n", - " 37.761634\n", - " -122.390648\n", - " 37.799953\n", - " -122.398525\n", - " 1989\n", - " Female\n", - " No\n", - " POINT (-122.39065 37.76163)\n", - " POINT (-122.39852 37.79995)\n", + " 37.804562\n", + " -122.271738\n", + " 37.792714\n", + " -122.24878\n", + " 1984\n", + " Male\n", + " Yes\n", + " POINT (-122.27174 37.80456)\n", + " POINT (-122.24878 37.79271)\n", " \n", " \n", " 19\n", - " 201709230951302222\n", - " 319\n", - " 2017-09-23 09:51:30+00:00\n", - " 7th St at Brannan St\n", - " 79\n", - " 2017-09-23 09:56:49+00:00\n", - " San Francisco Caltrain (Townsend St at 4th St)\n", - " 30\n", - " 2222\n", + " 201803131437033724\n", + " 917\n", + " 2018-03-13 14:37:03+00:00\n", + " Grand Ave at Webster St\n", + " 181\n", + " 2018-03-13 14:52:20+00:00\n", + " 10th Ave at E 15th St\n", + " 222\n", + " 3724\n", " <NA>\n", " ...\n", " <NA>\n", - " 37.773492\n", - " -122.403672\n", - " 37.776598\n", - " -122.395282\n", - " 1975\n", + " 37.811377\n", + " -122.265192\n", + " 37.792714\n", + " -122.24878\n", + " 1989\n", " Male\n", - " <NA>\n", - " POINT (-122.40367 37.77349)\n", - " POINT (-122.39528 37.7766)\n", + " No\n", + " POINT (-122.26519 37.81138)\n", + " POINT (-122.24878 37.79271)\n", " \n", " \n", " 20\n", - " 20180220172815415\n", - " 4009\n", - " 2018-02-20 17:28:15+00:00\n", - " Franklin St at 9th St\n", - " 162\n", - " 2018-02-20 18:35:05+00:00\n", - " Telegraph Ave at 27th St\n", - " 179\n", - " 415\n", + " 20170930184510496\n", + " 1367\n", + " 2017-09-30 18:45:10+00:00\n", + " Lake Merritt BART Station\n", + " 163\n", + " 2017-09-30 19:07:58+00:00\n", + " 10th Ave at E 15th St\n", + " 222\n", + " 496\n", " <NA>\n", " ...\n", " <NA>\n", - " 37.800516\n", - " -122.27208\n", - " 37.816073\n", - " -122.267886\n", - " 1973\n", - " Male\n", - " Yes\n", - " POINT (-122.27208 37.80052)\n", - " POINT (-122.26789 37.81607)\n", + " 37.79732\n", + " -122.26532\n", + " 37.792714\n", + " -122.24878\n", + " <NA>\n", + " <NA>\n", + " <NA>\n", + " POINT (-122.26532 37.79732)\n", + " POINT (-122.24878 37.79271)\n", " \n", " \n", " 21\n", - " 201710191714443003\n", - " 691\n", - " 2017-10-19 17:14:44+00:00\n", - " Harrison St at 20th St\n", - " 129\n", - " 2017-10-19 17:26:16+00:00\n", - " Valencia St at 22nd St\n", - " 133\n", - " 3003\n", + " 201712061755593426\n", + " 519\n", + " 2017-12-06 17:55:59+00:00\n", + " Lake Merritt BART Station\n", + " 163\n", + " 2017-12-06 18:04:39+00:00\n", + " 10th Ave at E 15th St\n", + " 222\n", + " 3426\n", " <NA>\n", " ...\n", " <NA>\n", - " 37.758862\n", - " -122.412544\n", - " 37.755213\n", - " -122.420975\n", - " 1958\n", + " 37.79732\n", + " -122.26532\n", + " 37.792714\n", + " -122.24878\n", + " 1986\n", " Male\n", " <NA>\n", - " POINT (-122.41254 37.75886)\n", - " POINT (-122.42098 37.75521)\n", + " POINT (-122.26532 37.79732)\n", + " POINT (-122.24878 37.79271)\n", " \n", " \n", " 22\n", - " 595146\n", - " 453\n", - " 2015-01-07 18:34:00+00:00\n", - " Market at 10th\n", - " 67\n", - " 2015-01-07 18:42:00+00:00\n", - " Townsend at 7th\n", - " 65\n", - " 421\n", - " 95014\n", - " ...\n", - " Subscriber\n", - " <NA>\n", - " <NA>\n", - " <NA>\n", - " <NA>\n", + " 201711062204002182\n", + " 420\n", + " 2017-11-06 22:04:00+00:00\n", + " Lake Merritt BART Station\n", + " 163\n", + " 2017-11-06 22:11:00+00:00\n", + " 10th Ave at E 15th St\n", + " 222\n", + " 2182\n", " <NA>\n", + " ...\n", " <NA>\n", + " 37.79732\n", + " -122.26532\n", + " 37.792714\n", + " -122.24878\n", + " 1992\n", + " Male\n", " <NA>\n", - " None\n", - " None\n", + " POINT (-122.26532 37.79732)\n", + " POINT (-122.24878 37.79271)\n", " \n", " \n", " 23\n", - " 201708290913502454\n", - " 788\n", - " 2017-08-29 09:13:50+00:00\n", - " San Francisco Caltrain (Townsend St at 4th St)\n", - " 30\n", - " 2017-08-29 09:26:58+00:00\n", - " The Embarcadero at Vallejo St\n", - " 8\n", - " 2454\n", + " 201709122036152238\n", + " 612\n", + " 2017-09-12 20:36:15+00:00\n", + " Lake Merritt BART Station\n", + " 163\n", + " 2017-09-12 20:46:27+00:00\n", + " 10th Ave at E 15th St\n", + " 222\n", + " 2238\n", " <NA>\n", " ...\n", " <NA>\n", - " 37.776598\n", - " -122.395282\n", - " 37.799953\n", - " -122.398525\n", - " 1979\n", + " 37.79732\n", + " -122.26532\n", + " 37.792714\n", + " -122.24878\n", + " 1984\n", " Male\n", " <NA>\n", - " POINT (-122.39528 37.7766)\n", - " POINT (-122.39852 37.79995)\n", + " POINT (-122.26532 37.79732)\n", + " POINT (-122.24878 37.79271)\n", " \n", " \n", " 24\n", - " 201712271150433036\n", - " 150\n", - " 2017-12-27 11:50:43+00:00\n", - " Powell St BART Station (Market St at 4th St)\n", - " 3\n", - " 2017-12-27 11:53:14+00:00\n", - " 4th St at Harrison St\n", - " 47\n", - " 3036\n", + " 201712062310481332\n", + " 442\n", + " 2017-12-06 23:10:48+00:00\n", + " Lake Merritt BART Station\n", + " 163\n", + " 2017-12-06 23:18:11+00:00\n", + " 10th Ave at E 15th St\n", + " 222\n", + " 1332\n", " <NA>\n", " ...\n", " <NA>\n", - " 37.786375\n", - " -122.404904\n", - " 37.780955\n", - " -122.399749\n", - " 1989\n", + " 37.79732\n", + " -122.26532\n", + " 37.792714\n", + " -122.24878\n", + " 1981\n", " Male\n", " <NA>\n", - " POINT (-122.4049 37.78638)\n", - " POINT (-122.39975 37.78095)\n", + " POINT (-122.26532 37.79732)\n", + " POINT (-122.24878 37.79271)\n", " \n", " \n", "\n", @@ -742,221 +746,194 @@ "[1947417 rows x 21 columns in total]" ], "text/plain": [ - " trip_id duration_sec start_date \\\n", - "0 1304531 597 2016-08-05 10:55:00+00:00 \n", - "1 184870 403 2014-02-14 14:50:00+00:00 \n", - "2 20170702115603836 16695 2017-07-02 11:56:03+00:00 \n", - "3 1066810 953 2016-01-21 08:24:00+00:00 \n", - "4 220481 679 2014-03-19 19:20:00+00:00 \n", - "5 738474 358 2015-04-23 16:45:00+00:00 \n", - "6 229264 286 2014-03-27 17:56:00+00:00 \n", - "7 352010 3621 2014-07-06 13:55:00+00:00 \n", - "8 156255 416 2014-01-16 18:06:00+00:00 \n", - "9 1040197 1054 2015-12-15 18:05:00+00:00 \n", - "10 1152693 562 2016-04-07 08:18:00+00:00 \n", - "11 201804191735183401 887 2018-04-19 17:35:18+00:00 \n", - "12 209283 943 2014-03-11 09:01:00+00:00 \n", - "13 201708281404312530 389 2017-08-28 14:04:31+00:00 \n", - "14 20171124115158841 384 2017-11-24 11:51:58+00:00 \n", - "15 1321042 874 2016-08-18 08:14:00+00:00 \n", - "16 201712131325183120 1376 2017-12-13 13:25:18+00:00 \n", - "17 201708310827151646 200 2017-08-31 08:27:15+00:00 \n", - "18 201801251754102907 1490 2018-01-25 17:54:10+00:00 \n", - "19 201709230951302222 319 2017-09-23 09:51:30+00:00 \n", - "20 20180220172815415 4009 2018-02-20 17:28:15+00:00 \n", - "21 201710191714443003 691 2017-10-19 17:14:44+00:00 \n", - "22 595146 453 2015-01-07 18:34:00+00:00 \n", - "23 201708290913502454 788 2017-08-29 09:13:50+00:00 \n", - "24 201712271150433036 150 2017-12-27 11:50:43+00:00 \n", + " trip_id duration_sec start_date \\\n", + "201802092135083596 788 2018-02-09 21:35:08+00:00 \n", + " 20171217135737144 1072 2017-12-17 13:57:37+00:00 \n", + "201803261642393539 486 2018-03-26 16:42:39+00:00 \n", + "201802281657253632 560 2018-02-28 16:57:25+00:00 \n", + "201708152357422491 965 2017-08-15 23:57:42+00:00 \n", + "201801161800473291 489 2018-01-16 18:00:47+00:00 \n", + "201802201913231257 596 2018-02-20 19:13:23+00:00 \n", + "201708242325001279 1341 2017-08-24 23:25:00+00:00 \n", + " 20170913210653295 367 2017-09-13 21:06:53+00:00 \n", + "201708192053311490 743 2017-08-19 20:53:31+00:00 \n", + " 20170810204454839 1256 2017-08-10 20:44:54+00:00 \n", + "201711181823281960 353 2017-11-18 18:23:28+00:00 \n", + "201801111613101305 858 2018-01-11 16:13:10+00:00 \n", + "201712181738372587 807 2017-12-18 17:38:37+00:00 \n", + "201803161910283751 564 2018-03-16 19:10:28+00:00 \n", + "201802241826551215 1235 2018-02-24 18:26:55+00:00 \n", + " 20171212152403227 854 2017-12-12 15:24:03+00:00 \n", + "201803091621483450 857 2018-03-09 16:21:48+00:00 \n", + "201801021932232717 914 2018-01-02 19:32:23+00:00 \n", + "201803131437033724 917 2018-03-13 14:37:03+00:00 \n", + " 20170930184510496 1367 2017-09-30 18:45:10+00:00 \n", + "201712061755593426 519 2017-12-06 17:55:59+00:00 \n", + "201711062204002182 420 2017-11-06 22:04:00+00:00 \n", + "201709122036152238 612 2017-09-12 20:36:15+00:00 \n", + "201712062310481332 442 2017-12-06 23:10:48+00:00 \n", "\n", - " start_station_name start_station_id \\\n", - "0 San Francisco Caltrain 2 (330 Townsend) 69 \n", - "1 Howard at 2nd 63 \n", - "2 Union Square (Powell St at Post St) 324 \n", - "3 Civic Center BART (7th at Market) 72 \n", - "4 San Francisco Caltrain 2 (330 Townsend) 69 \n", - "5 2nd at Folsom 62 \n", - "6 Embarcadero at Sansome 60 \n", - "7 Embarcadero at Sansome 60 \n", - "8 Embarcadero at Bryant 54 \n", - "9 Steuart at Market 74 \n", - "10 San Francisco Caltrain (Townsend at 4th) 70 \n", - "11 Montgomery St BART Station (Market St at 2nd St) 21 \n", - "12 South Van Ness at Market 66 \n", - "13 16th St at Prosper St 105 \n", - "14 2nd Ave at E 18th St 200 \n", - "15 San Francisco Caltrain (Townsend at 4th) 70 \n", - "16 Steuart St at Market St 16 \n", - "17 Powell St BART Station (Market St at 4th St) 3 \n", - "18 Esprit Park 126 \n", - "19 7th St at Brannan St 79 \n", - "20 Franklin St at 9th St 162 \n", - "21 Harrison St at 20th St 129 \n", - "22 Market at 10th 67 \n", - "23 San Francisco Caltrain (Townsend St at 4th St) 30 \n", - "24 Powell St BART Station (Market St at 4th St) 3 \n", + " start_station_name start_station_id end_date \\\n", + " 10th Ave at E 15th St 222 2018-02-09 21:48:17+00:00 \n", + " 10th Ave at E 15th St 222 2017-12-17 14:15:30+00:00 \n", + " 10th St at Fallon St 201 2018-03-26 16:50:46+00:00 \n", + " 10th St at Fallon St 201 2018-02-28 17:06:46+00:00 \n", + " 10th St at Fallon St 201 2017-08-16 00:13:48+00:00 \n", + " 10th St at Fallon St 201 2018-01-16 18:08:56+00:00 \n", + " 10th St at Fallon St 201 2018-02-20 19:23:19+00:00 \n", + " 10th St at Fallon St 201 2017-08-24 23:47:22+00:00 \n", + " 10th St at Fallon St 201 2017-09-13 21:13:00+00:00 \n", + " 2nd Ave at E 18th St 200 2017-08-19 21:05:54+00:00 \n", + " 2nd Ave at E 18th St 200 2017-08-10 21:05:50+00:00 \n", + " 2nd Ave at E 18th St 200 2017-11-18 18:29:22+00:00 \n", + " Frank H Ogawa Plaza 7 2018-01-11 16:27:28+00:00 \n", + " Frank H Ogawa Plaza 7 2017-12-18 17:52:04+00:00 \n", + " Frank H Ogawa Plaza 7 2018-03-16 19:19:52+00:00 \n", + " Frank H Ogawa Plaza 7 2018-02-24 18:47:31+00:00 \n", + " Frank H Ogawa Plaza 7 2017-12-12 15:38:17+00:00 \n", + " Frank H Ogawa Plaza 7 2018-03-09 16:36:06+00:00 \n", + " Frank H Ogawa Plaza 7 2018-01-02 19:47:38+00:00 \n", + " Grand Ave at Webster St 181 2018-03-13 14:52:20+00:00 \n", + "Lake Merritt BART Station 163 2017-09-30 19:07:58+00:00 \n", + "Lake Merritt BART Station 163 2017-12-06 18:04:39+00:00 \n", + "Lake Merritt BART Station 163 2017-11-06 22:11:00+00:00 \n", + "Lake Merritt BART Station 163 2017-09-12 20:46:27+00:00 \n", + "Lake Merritt BART Station 163 2017-12-06 23:18:11+00:00 \n", "\n", - " end_date \\\n", - "0 2016-08-05 11:05:00+00:00 \n", - "1 2014-02-14 14:56:00+00:00 \n", - "2 2017-07-02 16:34:19+00:00 \n", - "3 2016-01-21 08:40:00+00:00 \n", - "4 2014-03-19 19:31:00+00:00 \n", - "5 2015-04-23 16:51:00+00:00 \n", - "6 2014-03-27 18:01:00+00:00 \n", - "7 2014-07-06 14:55:00+00:00 \n", - "8 2014-01-16 18:13:00+00:00 \n", - "9 2015-12-15 18:22:00+00:00 \n", - "10 2016-04-07 08:27:00+00:00 \n", - "11 2018-04-19 17:50:06+00:00 \n", - "12 2014-03-11 09:16:00+00:00 \n", - "13 2017-08-28 14:11:00+00:00 \n", - "14 2017-11-24 11:58:23+00:00 \n", - "15 2016-08-18 08:29:00+00:00 \n", - "16 2017-12-13 13:48:14+00:00 \n", - "17 2017-08-31 08:30:36+00:00 \n", - "18 2018-01-25 18:19:01+00:00 \n", - "19 2017-09-23 09:56:49+00:00 \n", - "20 2018-02-20 18:35:05+00:00 \n", - "21 2017-10-19 17:26:16+00:00 \n", - "22 2015-01-07 18:42:00+00:00 \n", - "23 2017-08-29 09:26:58+00:00 \n", - "24 2017-12-27 11:53:14+00:00 \n", + " end_station_name end_station_id bike_number zip_code ... \\\n", + "10th Ave at E 15th St 222 3596 ... \n", + "10th Ave at E 15th St 222 144 ... \n", + "10th Ave at E 15th St 222 3539 ... \n", + "10th Ave at E 15th St 222 3632 ... \n", + "10th Ave at E 15th St 222 2491 ... \n", + "10th Ave at E 15th St 222 3291 ... \n", + "10th Ave at E 15th St 222 1257 ... \n", + "10th Ave at E 15th St 222 1279 ... \n", + "10th Ave at E 15th St 222 295 ... \n", + "10th Ave at E 15th St 222 1490 ... \n", + "10th Ave at E 15th St 222 839 ... \n", + "10th Ave at E 15th St 222 1960 ... \n", + "10th Ave at E 15th St 222 1305 ... \n", + "10th Ave at E 15th St 222 2587 ... \n", + "10th Ave at E 15th St 222 3751 ... \n", + "10th Ave at E 15th St 222 1215 ... \n", + "10th Ave at E 15th St 222 227 ... \n", + "10th Ave at E 15th St 222 3450 ... \n", + "10th Ave at E 15th St 222 2717 ... \n", + "10th Ave at E 15th St 222 3724 ... \n", + "10th Ave at E 15th St 222 496 ... \n", + "10th Ave at E 15th St 222 3426 ... \n", + "10th Ave at E 15th St 222 2182 ... \n", + "10th Ave at E 15th St 222 2238 ... \n", + "10th Ave at E 15th St 222 1332 ... \n", "\n", - " end_station_name end_station_id \\\n", - "0 Powell Street BART 39 \n", - "1 Commercial at Montgomery 45 \n", - "2 Union Square (Powell St at Post St) 324 \n", - "3 Embarcadero at Sansome 60 \n", - "4 Civic Center BART (7th at Market) 72 \n", - "5 Steuart at Market 74 \n", - "6 Davis at Jackson 42 \n", - "7 Embarcadero at Sansome 60 \n", - "8 San Francisco Caltrain (Townsend at 4th) 70 \n", - "9 San Francisco Caltrain (Townsend at 4th) 70 \n", - "10 Steuart at Market 74 \n", - "11 Civic Center/UN Plaza BART Station (Market St ... 44 \n", - "12 Temporary Transbay Terminal (Howard at Beale) 55 \n", - "13 Mission Playground 121 \n", - "14 El Embarcadero at Grand Ave 197 \n", - "15 Beale at Market 56 \n", - "16 The Embarcadero at Sansome St 6 \n", - "17 Montgomery St BART Station (Market St at 2nd St) 21 \n", - "18 The Embarcadero at Vallejo St 8 \n", - "19 San Francisco Caltrain (Townsend St at 4th St) 30 \n", - "20 Telegraph Ave at 27th St 179 \n", - "21 Valencia St at 22nd St 133 \n", - "22 Townsend at 7th 65 \n", - "23 The Embarcadero at Vallejo St 8 \n", - "24 4th St at Harrison St 47 \n", + "c_subscription_type start_station_latitude start_station_longitude \\\n", + " 37.792714 -122.24878 \n", + " 37.792714 -122.24878 \n", + " 37.797673 -122.262997 \n", + " 37.797673 -122.262997 \n", + " 37.797673 -122.262997 \n", + " 37.797673 -122.262997 \n", + " 37.797673 -122.262997 \n", + " 37.797673 -122.262997 \n", + " 37.797673 -122.262997 \n", + " 37.800214 -122.25381 \n", + " 37.800214 -122.25381 \n", + " 37.800214 -122.25381 \n", + " 37.804562 -122.271738 \n", + " 37.804562 -122.271738 \n", + " 37.804562 -122.271738 \n", + " 37.804562 -122.271738 \n", + " 37.804562 -122.271738 \n", + " 37.804562 -122.271738 \n", + " 37.804562 -122.271738 \n", + " 37.811377 -122.265192 \n", + " 37.79732 -122.26532 \n", + " 37.79732 -122.26532 \n", + " 37.79732 -122.26532 \n", + " 37.79732 -122.26532 \n", + " 37.79732 -122.26532 \n", "\n", - " bike_number zip_code ... c_subscription_type start_station_latitude \\\n", - "0 214 95121 ... Subscriber \n", - "1 342 94122 ... Subscriber \n", - "2 836 ... 37.7883 \n", - "3 212 94103 ... Subscriber \n", - "4 478 94107 ... Subscriber \n", - "5 443 94105 ... Subscriber \n", - "6 342 94133 ... Subscriber \n", - "7 390 4038 ... Customer \n", - "8 510 94107 ... Subscriber \n", - "9 700 94111 ... Subscriber \n", - "10 419 94158 ... Subscriber \n", - "11 3401 ... 37.789625 \n", - "12 532 94105 ... Subscriber \n", - "13 2530 ... 37.764285 \n", - "14 841 ... 37.800214 \n", - "15 390 95050 ... Subscriber \n", - "16 3120 ... 37.79413 \n", - "17 1646 ... 37.786375 \n", - "18 2907 ... 37.761634 \n", - "19 2222 ... 37.773492 \n", - "20 415 ... 37.800516 \n", - "21 3003 ... 37.758862 \n", - "22 421 95014 ... Subscriber \n", - "23 2454 ... 37.776598 \n", - "24 3036 ... 37.786375 \n", + " end_station_latitude end_station_longitude member_birth_year \\\n", + " 37.792714 -122.24878 1984 \n", + " 37.792714 -122.24878 1984 \n", + " 37.792714 -122.24878 1984 \n", + " 37.792714 -122.24878 1984 \n", + " 37.792714 -122.24878 \n", + " 37.792714 -122.24878 1984 \n", + " 37.792714 -122.24878 1984 \n", + " 37.792714 -122.24878 1969 \n", + " 37.792714 -122.24878 1987 \n", + " 37.792714 -122.24878 \n", + " 37.792714 -122.24878 \n", + " 37.792714 -122.24878 1988 \n", + " 37.792714 -122.24878 1984 \n", + " 37.792714 -122.24878 1984 \n", + " 37.792714 -122.24878 1987 \n", + " 37.792714 -122.24878 1969 \n", + " 37.792714 -122.24878 1984 \n", + " 37.792714 -122.24878 1984 \n", + " 37.792714 -122.24878 1984 \n", + " 37.792714 -122.24878 1989 \n", + " 37.792714 -122.24878 \n", + " 37.792714 -122.24878 1986 \n", + " 37.792714 -122.24878 1992 \n", + " 37.792714 -122.24878 1984 \n", + " 37.792714 -122.24878 1981 \n", "\n", - " start_station_longitude end_station_latitude end_station_longitude \\\n", - "0 \n", - "1 \n", - "2 -122.408531 37.7883 -122.408531 \n", - "3 \n", - "4 \n", - "5 \n", - "6 \n", - "7 \n", - "8 \n", - "9 \n", - "10 \n", - "11 -122.400811 37.781074 -122.411738 \n", - "12 \n", - "13 -122.431804 37.75921 -122.421339 \n", - "14 -122.25381 37.808848 -122.24968 \n", - "15 \n", - "16 -122.39443 37.80477 -122.403234 \n", - "17 -122.404904 37.789625 -122.400811 \n", - "18 -122.390648 37.799953 -122.398525 \n", - "19 -122.403672 37.776598 -122.395282 \n", - "20 -122.27208 37.816073 -122.267886 \n", - "21 -122.412544 37.755213 -122.420975 \n", - "22 \n", - "23 -122.395282 37.799953 -122.398525 \n", - "24 -122.404904 37.780955 -122.399749 \n", + " member_gender bike_share_for_all_trip start_station_geom \\\n", + " Male Yes POINT (-122.24878 37.79271) \n", + " Male POINT (-122.24878 37.79271) \n", + " Male Yes POINT (-122.263 37.79767) \n", + " Male Yes POINT (-122.263 37.79767) \n", + " POINT (-122.263 37.79767) \n", + " Male Yes POINT (-122.263 37.79767) \n", + " Male Yes POINT (-122.263 37.79767) \n", + " Male POINT (-122.263 37.79767) \n", + " Male POINT (-122.263 37.79767) \n", + " POINT (-122.25381 37.80021) \n", + " POINT (-122.25381 37.80021) \n", + " Male POINT (-122.25381 37.80021) \n", + " Male Yes POINT (-122.27174 37.80456) \n", + " Male POINT (-122.27174 37.80456) \n", + " Male No POINT (-122.27174 37.80456) \n", + " Male No POINT (-122.27174 37.80456) \n", + " Male POINT (-122.27174 37.80456) \n", + " Male Yes POINT (-122.27174 37.80456) \n", + " Male Yes POINT (-122.27174 37.80456) \n", + " Male No POINT (-122.26519 37.81138) \n", + " POINT (-122.26532 37.79732) \n", + " Male POINT (-122.26532 37.79732) \n", + " Male POINT (-122.26532 37.79732) \n", + " Male POINT (-122.26532 37.79732) \n", + " Male POINT (-122.26532 37.79732) \n", "\n", - " member_birth_year member_gender bike_share_for_all_trip \\\n", - "0 \n", - "1 \n", - "2 \n", - "3 \n", - "4 \n", - "5 \n", - "6 \n", - "7 \n", - "8 \n", - "9 \n", - "10 \n", - "11 1979 Male No \n", - "12 \n", - "13 1981 Male \n", - "14 1977 Female \n", - "15 \n", - "16 \n", - "17 1988 Male \n", - "18 1989 Female No \n", - "19 1975 Male \n", - "20 1973 Male Yes \n", - "21 1958 Male \n", - "22 \n", - "23 1979 Male \n", - "24 1989 Male \n", - "\n", - " start_station_geom end_station_geom \n", - "0 None None \n", - "1 None None \n", - "2 POINT (-122.40853 37.7883) POINT (-122.40853 37.7883) \n", - "3 None None \n", - "4 None None \n", - "5 None None \n", - "6 None None \n", - "7 None None \n", - "8 None None \n", - "9 None None \n", - "10 None None \n", - "11 POINT (-122.40081 37.78963) POINT (-122.41174 37.78107) \n", - "12 None None \n", - "13 POINT (-122.4318 37.76428) POINT (-122.42134 37.75921) \n", - "14 POINT (-122.25381 37.80021) POINT (-122.24968 37.80885) \n", - "15 None None \n", - "16 POINT (-122.39443 37.79413) POINT (-122.40323 37.80477) \n", - "17 POINT (-122.4049 37.78638) POINT (-122.40081 37.78963) \n", - "18 POINT (-122.39065 37.76163) POINT (-122.39852 37.79995) \n", - "19 POINT (-122.40367 37.77349) POINT (-122.39528 37.7766) \n", - "20 POINT (-122.27208 37.80052) POINT (-122.26789 37.81607) \n", - "21 POINT (-122.41254 37.75886) POINT (-122.42098 37.75521) \n", - "22 None None \n", - "23 POINT (-122.39528 37.7766) POINT (-122.39852 37.79995) \n", - "24 POINT (-122.4049 37.78638) POINT (-122.39975 37.78095) \n", + " end_station_geom \n", + "POINT (-122.24878 37.79271) \n", + "POINT (-122.24878 37.79271) \n", + "POINT (-122.24878 37.79271) \n", + "POINT (-122.24878 37.79271) \n", + "POINT (-122.24878 37.79271) \n", + "POINT (-122.24878 37.79271) \n", + "POINT (-122.24878 37.79271) \n", + "POINT (-122.24878 37.79271) \n", + "POINT (-122.24878 37.79271) \n", + "POINT (-122.24878 37.79271) \n", + "POINT (-122.24878 37.79271) \n", + "POINT (-122.24878 37.79271) \n", + "POINT (-122.24878 37.79271) \n", + "POINT (-122.24878 37.79271) \n", + "POINT (-122.24878 37.79271) \n", + "POINT (-122.24878 37.79271) \n", + "POINT (-122.24878 37.79271) \n", + "POINT (-122.24878 37.79271) \n", + "POINT (-122.24878 37.79271) \n", + "POINT (-122.24878 37.79271) \n", + "POINT (-122.24878 37.79271) \n", + "POINT (-122.24878 37.79271) \n", + "POINT (-122.24878 37.79271) \n", + "POINT (-122.24878 37.79271) \n", + "POINT (-122.24878 37.79271) \n", "...\n", "\n", "[1947417 rows x 21 columns]" @@ -1167,32 +1144,32 @@ "[2842 rows x 2 columns in total]" ], "text/plain": [ - " trip_hour num_trips\n", - "0 2018-01-01 00:00:00+00:00 20\n", - "1 2018-01-01 01:00:00+00:00 25\n", - "2 2018-01-01 02:00:00+00:00 13\n", - "3 2018-01-01 03:00:00+00:00 11\n", - "4 2018-01-01 05:00:00+00:00 4\n", - "5 2018-01-01 06:00:00+00:00 8\n", - "6 2018-01-01 07:00:00+00:00 8\n", - "7 2018-01-01 08:00:00+00:00 20\n", - "8 2018-01-01 09:00:00+00:00 30\n", - "9 2018-01-01 10:00:00+00:00 41\n", - "10 2018-01-01 11:00:00+00:00 45\n", - "11 2018-01-01 12:00:00+00:00 54\n", - "12 2018-01-01 13:00:00+00:00 57\n", - "13 2018-01-01 14:00:00+00:00 68\n", - "14 2018-01-01 15:00:00+00:00 86\n", - "15 2018-01-01 16:00:00+00:00 72\n", - "16 2018-01-01 17:00:00+00:00 72\n", - "17 2018-01-01 18:00:00+00:00 47\n", - "18 2018-01-01 19:00:00+00:00 32\n", - "19 2018-01-01 20:00:00+00:00 34\n", - "20 2018-01-01 21:00:00+00:00 27\n", - "21 2018-01-01 22:00:00+00:00 15\n", - "22 2018-01-01 23:00:00+00:00 6\n", - "23 2018-01-02 00:00:00+00:00 2\n", - "24 2018-01-02 01:00:00+00:00 1\n", + " trip_hour num_trips\n", + "2018-01-01 00:00:00+00:00 20\n", + "2018-01-01 01:00:00+00:00 25\n", + "2018-01-01 02:00:00+00:00 13\n", + "2018-01-01 03:00:00+00:00 11\n", + "2018-01-01 05:00:00+00:00 4\n", + "2018-01-01 06:00:00+00:00 8\n", + "2018-01-01 07:00:00+00:00 8\n", + "2018-01-01 08:00:00+00:00 20\n", + "2018-01-01 09:00:00+00:00 30\n", + "2018-01-01 10:00:00+00:00 41\n", + "2018-01-01 11:00:00+00:00 45\n", + "2018-01-01 12:00:00+00:00 54\n", + "2018-01-01 13:00:00+00:00 57\n", + "2018-01-01 14:00:00+00:00 68\n", + "2018-01-01 15:00:00+00:00 86\n", + "2018-01-01 16:00:00+00:00 72\n", + "2018-01-01 17:00:00+00:00 72\n", + "2018-01-01 18:00:00+00:00 47\n", + "2018-01-01 19:00:00+00:00 32\n", + "2018-01-01 20:00:00+00:00 34\n", + "2018-01-01 21:00:00+00:00 27\n", + "2018-01-01 22:00:00+00:00 15\n", + "2018-01-01 23:00:00+00:00 6\n", + "2018-01-02 00:00:00+00:00 2\n", + "2018-01-02 01:00:00+00:00 1\n", "...\n", "\n", "[2842 rows x 2 columns]" @@ -1253,227 +1230,227 @@ " \n", " \n", " 0\n", - " 2018-05-05 01:00:00+00:00\n", - " 50.123672\n", + " 2018-04-26 11:00:00+00:00\n", + " 204.291275\n", " 0.95\n", - " -13.062586\n", - " 113.309931\n", + " 149.151441\n", + " 259.431109\n", " \n", " \n", " \n", " 1\n", - " 2018-05-05 07:00:00+00:00\n", - " 103.112846\n", + " 2018-04-27 13:00:00+00:00\n", + " 196.034332\n", " 0.95\n", - " 33.725954\n", - " 172.499739\n", + " 203.125978\n", + " 188.942686\n", " \n", " \n", " \n", " 2\n", - " 2018-05-03 15:00:00+00:00\n", - " 230.49147\n", + " 2018-04-27 20:00:00+00:00\n", + " 133.339386\n", " 0.95\n", - " 152.635986\n", - " 308.346954\n", + " 132.658946\n", + " 134.019826\n", " \n", " \n", " \n", " 3\n", - " 2018-05-02 08:00:00+00:00\n", - " 737.477356\n", + " 2018-04-28 05:00:00+00:00\n", + " -27.321686\n", " 0.95\n", - " 562.979208\n", - " 911.975504\n", + " -13.918083\n", + " -40.725288\n", " \n", " \n", " \n", " 4\n", - " 2018-05-01 08:00:00+00:00\n", - " 679.980469\n", + " 2018-04-29 12:00:00+00:00\n", + " 117.657822\n", " 0.95\n", - " 479.980134\n", - " 879.980803\n", + " 58.020439\n", + " 177.295205\n", " \n", " \n", " \n", " 5\n", - " 2018-05-06 18:00:00+00:00\n", - " 136.80835\n", + " 2018-04-24 10:00:00+00:00\n", + " 221.464111\n", " 0.95\n", - " -13.813863\n", - " 287.430562\n", + " 154.598621\n", + " 288.329602\n", " \n", " \n", " \n", " 6\n", - " 2018-05-01 11:00:00+00:00\n", - " 120.364288\n", + " 2018-04-24 23:00:00+00:00\n", + " 56.203827\n", " 0.95\n", - " 52.778249\n", - " 187.950328\n", + " 42.096868\n", + " 70.310786\n", " \n", " \n", " \n", " 7\n", - " 2018-05-06 22:00:00+00:00\n", - " 64.722443\n", + " 2018-04-29 07:00:00+00:00\n", + " -14.801514\n", " 0.95\n", - " -55.555842\n", - " 185.000727\n", + " -48.905982\n", + " 19.302954\n", " \n", " \n", " \n", " 8\n", - " 2018-05-03 02:00:00+00:00\n", - " 42.689804\n", + " 2018-04-24 22:00:00+00:00\n", + " 58.174316\n", " 0.95\n", - " 33.258414\n", - " 52.121194\n", + " 85.290985\n", + " 31.057648\n", " \n", " \n", " \n", " 9\n", - " 2018-05-07 17:00:00+00:00\n", - " 594.999084\n", + " 2018-04-25 08:00:00+00:00\n", + " 666.577393\n", " 0.95\n", - " 346.917217\n", - " 843.080952\n", + " 518.655663\n", + " 814.499122\n", " \n", " \n", " \n", " 10\n", - " 2018-05-03 20:00:00+00:00\n", - " 161.822281\n", + " 2018-04-29 01:00:00+00:00\n", + " 40.19632\n", " 0.95\n", - " 100.005942\n", - " 223.63862\n", + " 48.957491\n", + " 31.435148\n", " \n", " \n", " \n", " 11\n", - " 2018-05-01 20:00:00+00:00\n", - " 173.801025\n", + " 2018-04-29 02:00:00+00:00\n", + " 29.00975\n", " 0.95\n", - " 56.460376\n", - " 291.141675\n", + " -8.137303\n", + " 66.156804\n", " \n", " \n", " \n", " 12\n", - " 2018-05-04 17:00:00+00:00\n", - " 485.449829\n", + " 2018-04-30 18:00:00+00:00\n", + " 488.885284\n", " 0.95\n", - " 356.038539\n", - " 614.86112\n", + " 315.531321\n", + " 662.239248\n", " \n", " \n", " \n", " 13\n", - " 2018-05-04 09:00:00+00:00\n", - " 418.055878\n", + " 2018-04-27 10:00:00+00:00\n", + " 188.79628\n", " 0.95\n", - " 281.134736\n", - " 554.977019\n", + " 157.126395\n", + " 220.466165\n", " \n", " \n", " \n", " 14\n", - " 2018-05-07 03:00:00+00:00\n", - " 24.735134\n", + " 2018-04-24 21:00:00+00:00\n", + " 107.512665\n", " 0.95\n", - " -100.607727\n", - " 150.077995\n", + " 108.890078\n", + " 106.135251\n", " \n", " \n", " \n", " 15\n", - " 2018-05-05 11:00:00+00:00\n", - " 186.08136\n", + " 2018-04-28 14:00:00+00:00\n", + " 149.738419\n", " 0.95\n", - " 140.706789\n", - " 231.455931\n", + " 161.696173\n", + " 137.780664\n", " \n", " \n", " \n", " 16\n", - " 2018-05-03 08:00:00+00:00\n", - " 675.380249\n", + " 2018-04-28 20:00:00+00:00\n", + " 71.378677\n", " 0.95\n", - " 532.913707\n", - " 817.846791\n", + " 98.940288\n", + " 43.817067\n", " \n", " \n", " \n", " 17\n", - " 2018-05-02 09:00:00+00:00\n", - " 537.494812\n", + " 2018-04-30 13:00:00+00:00\n", + " 139.673706\n", " 0.95\n", - " 376.406922\n", - " 698.582702\n", + " 66.493742\n", + " 212.85367\n", " \n", " \n", " \n", " 18\n", - " 2018-05-01 12:00:00+00:00\n", - " 101.637169\n", + " 2018-04-24 12:00:00+00:00\n", + " 144.577728\n", " 0.95\n", - " 55.141509\n", - " 148.132829\n", + " 120.01921\n", + " 169.136247\n", " \n", " \n", " \n", " 19\n", - " 2018-05-05 00:00:00+00:00\n", - " 7.469772\n", + " 2018-04-25 00:00:00+00:00\n", + " 54.215515\n", " 0.95\n", - " -23.930392\n", - " 38.869936\n", + " 46.8394\n", + " 61.591631\n", " \n", " \n", " \n", " 20\n", - " 2018-05-02 14:00:00+00:00\n", - " 153.851379\n", + " 2018-04-26 05:00:00+00:00\n", + " 8.140533\n", " 0.95\n", - " 104.224826\n", - " 203.477932\n", + " -14.613272\n", + " 30.894339\n", " \n", " \n", " \n", " 21\n", - " 2018-05-04 13:00:00+00:00\n", - " 162.676117\n", + " 2018-04-26 14:00:00+00:00\n", + " 198.744949\n", " 0.95\n", - " 113.098327\n", - " 212.253907\n", + " 174.982268\n", + " 222.50763\n", " \n", " \n", " \n", " 22\n", - " 2018-05-04 16:00:00+00:00\n", - " 330.643402\n", + " 2018-04-27 02:00:00+00:00\n", + " 9.91806\n", " 0.95\n", - " 205.125168\n", - " 456.161636\n", + " -26.749948\n", + " 46.586069\n", " \n", " \n", " \n", " 23\n", - " 2018-05-04 21:00:00+00:00\n", - " 136.264679\n", + " 2018-04-29 03:00:00+00:00\n", + " 32.063339\n", " 0.95\n", - " 41.947438\n", - " 230.58192\n", + " -35.730978\n", + " 99.857656\n", " \n", " \n", " \n", " 24\n", - " 2018-05-02 17:00:00+00:00\n", - " 675.527222\n", + " 2018-04-27 04:00:00+00:00\n", + " 25.757111\n", " 0.95\n", - " 516.358698\n", - " 834.695746\n", + " 8.178037\n", + " 43.336184\n", " \n", " \n", " \n", @@ -1482,86 +1459,86 @@ "[168 rows x 6 columns in total]" ], "text/plain": [ - " forecast_timestamp forecast_value confidence_level \\\n", - "0 2018-05-05 01:00:00+00:00 50.123672 0.95 \n", - "1 2018-05-05 07:00:00+00:00 103.112846 0.95 \n", - "2 2018-05-03 15:00:00+00:00 230.49147 0.95 \n", - "3 2018-05-02 08:00:00+00:00 737.477356 0.95 \n", - "4 2018-05-01 08:00:00+00:00 679.980469 0.95 \n", - "5 2018-05-06 18:00:00+00:00 136.80835 0.95 \n", - "6 2018-05-01 11:00:00+00:00 120.364288 0.95 \n", - "7 2018-05-06 22:00:00+00:00 64.722443 0.95 \n", - "8 2018-05-03 02:00:00+00:00 42.689804 0.95 \n", - "9 2018-05-07 17:00:00+00:00 594.999084 0.95 \n", - "10 2018-05-03 20:00:00+00:00 161.822281 0.95 \n", - "11 2018-05-01 20:00:00+00:00 173.801025 0.95 \n", - "12 2018-05-04 17:00:00+00:00 485.449829 0.95 \n", - "13 2018-05-04 09:00:00+00:00 418.055878 0.95 \n", - "14 2018-05-07 03:00:00+00:00 24.735134 0.95 \n", - "15 2018-05-05 11:00:00+00:00 186.08136 0.95 \n", - "16 2018-05-03 08:00:00+00:00 675.380249 0.95 \n", - "17 2018-05-02 09:00:00+00:00 537.494812 0.95 \n", - "18 2018-05-01 12:00:00+00:00 101.637169 0.95 \n", - "19 2018-05-05 00:00:00+00:00 7.469772 0.95 \n", - "20 2018-05-02 14:00:00+00:00 153.851379 0.95 \n", - "21 2018-05-04 13:00:00+00:00 162.676117 0.95 \n", - "22 2018-05-04 16:00:00+00:00 330.643402 0.95 \n", - "23 2018-05-04 21:00:00+00:00 136.264679 0.95 \n", - "24 2018-05-02 17:00:00+00:00 675.527222 0.95 \n", + " forecast_timestamp forecast_value confidence_level \\\n", + "2018-04-26 11:00:00+00:00 204.291275 0.95 \n", + "2018-04-27 13:00:00+00:00 196.034332 0.95 \n", + "2018-04-27 20:00:00+00:00 133.339386 0.95 \n", + "2018-04-28 05:00:00+00:00 -27.321686 0.95 \n", + "2018-04-29 12:00:00+00:00 117.657822 0.95 \n", + "2018-04-24 10:00:00+00:00 221.464111 0.95 \n", + "2018-04-24 23:00:00+00:00 56.203827 0.95 \n", + "2018-04-29 07:00:00+00:00 -14.801514 0.95 \n", + "2018-04-24 22:00:00+00:00 58.174316 0.95 \n", + "2018-04-25 08:00:00+00:00 666.577393 0.95 \n", + "2018-04-29 01:00:00+00:00 40.19632 0.95 \n", + "2018-04-29 02:00:00+00:00 29.00975 0.95 \n", + "2018-04-30 18:00:00+00:00 488.885284 0.95 \n", + "2018-04-27 10:00:00+00:00 188.79628 0.95 \n", + "2018-04-24 21:00:00+00:00 107.512665 0.95 \n", + "2018-04-28 14:00:00+00:00 149.738419 0.95 \n", + "2018-04-28 20:00:00+00:00 71.378677 0.95 \n", + "2018-04-30 13:00:00+00:00 139.673706 0.95 \n", + "2018-04-24 12:00:00+00:00 144.577728 0.95 \n", + "2018-04-25 00:00:00+00:00 54.215515 0.95 \n", + "2018-04-26 05:00:00+00:00 8.140533 0.95 \n", + "2018-04-26 14:00:00+00:00 198.744949 0.95 \n", + "2018-04-27 02:00:00+00:00 9.91806 0.95 \n", + "2018-04-29 03:00:00+00:00 32.063339 0.95 \n", + "2018-04-27 04:00:00+00:00 25.757111 0.95 \n", "\n", - " prediction_interval_lower_bound prediction_interval_upper_bound \\\n", - "0 -13.062586 113.309931 \n", - "1 33.725954 172.499739 \n", - "2 152.635986 308.346954 \n", - "3 562.979208 911.975504 \n", - "4 479.980134 879.980803 \n", - "5 -13.813863 287.430562 \n", - "6 52.778249 187.950328 \n", - "7 -55.555842 185.000727 \n", - "8 33.258414 52.121194 \n", - "9 346.917217 843.080952 \n", - "10 100.005942 223.63862 \n", - "11 56.460376 291.141675 \n", - "12 356.038539 614.86112 \n", - "13 281.134736 554.977019 \n", - "14 -100.607727 150.077995 \n", - "15 140.706789 231.455931 \n", - "16 532.913707 817.846791 \n", - "17 376.406922 698.582702 \n", - "18 55.141509 148.132829 \n", - "19 -23.930392 38.869936 \n", - "20 104.224826 203.477932 \n", - "21 113.098327 212.253907 \n", - "22 205.125168 456.161636 \n", - "23 41.947438 230.58192 \n", - "24 516.358698 834.695746 \n", + " prediction_interval_lower_bound prediction_interval_upper_bound \\\n", + " 149.151441 259.431109 \n", + " 203.125978 188.942686 \n", + " 132.658946 134.019826 \n", + " -13.918083 -40.725288 \n", + " 58.020439 177.295205 \n", + " 154.598621 288.329602 \n", + " 42.096868 70.310786 \n", + " -48.905982 19.302954 \n", + " 85.290985 31.057648 \n", + " 518.655663 814.499122 \n", + " 48.957491 31.435148 \n", + " -8.137303 66.156804 \n", + " 315.531321 662.239248 \n", + " 157.126395 220.466165 \n", + " 108.890078 106.135251 \n", + " 161.696173 137.780664 \n", + " 98.940288 43.817067 \n", + " 66.493742 212.85367 \n", + " 120.01921 169.136247 \n", + " 46.8394 61.591631 \n", + " -14.613272 30.894339 \n", + " 174.982268 222.50763 \n", + " -26.749948 46.586069 \n", + " -35.730978 99.857656 \n", + " 8.178037 43.336184 \n", "\n", - " ai_forecast_status \n", - "0 \n", - "1 \n", - "2 \n", - "3 \n", - "4 \n", - "5 \n", - "6 \n", - "7 \n", - "8 \n", - "9 \n", - "10 \n", - "11 \n", - "12 \n", - "13 \n", - "14 \n", - "15 \n", - "16 \n", - "17 \n", - "18 \n", - "19 \n", - "20 \n", - "21 \n", - "22 \n", - "23 \n", - "24 \n", + "ai_forecast_status \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", "...\n", "\n", "[168 rows x 6 columns]" @@ -1573,7 +1550,8 @@ } ], "source": [ - "result = df_grouped.ai.forecast(timestamp_column=\"trip_hour\", data_column=\"num_trips\", horizon=168) # 1 week\n", + "# Using all the data except the last week (2842-168) for training. And predict the last week (168).\n", + "result = df_grouped.head(2842-168).ai.forecast(timestamp_column=\"trip_hour\", data_column=\"num_trips\", horizon=168) \n", "result" ] }, @@ -1597,6 +1575,13 @@ "df_all = df_all.tail(672) # 4 weeks" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Plot a line chart and compare with the actual result." + ] + }, { "cell_type": "code", "execution_count": 8, @@ -1614,7 +1599,7 @@ }, { "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAABSkAAAKnCAYAAABqEsg1AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8hTgPZAAAACXBIWXMAAA9hAAAPYQGoP6dpAAEAAElEQVR4nOz9e7gsVXXvD4/qXmvf2G5uB/aGiIiRHMGAEsxPd8iJvkhAJMYLxmg8eSXhmCe+GI9yMIm/EEW8hp8ajaIxiQI5xpMT80Zf9XgBUbxxEa/xQI4mCNkYbkaE7QbZ69L9/lE9q8acVb16jjnnqJ6r1vfzPPupXr179pxdXVU96zu/Y4xiPB6PCQAAAAAAAAAAAAAAAObEYN4DAAAAAAAAAAAAAAAAbGwgUgIAAAAAAAAAAAAAAOYKREoAAAAAAAAAAAAAAMBcgUgJAAAAAAAAAAAAAACYKxApAQAAAAAAAAAAAAAAcwUiJQAAAAAAAAAAAAAAYK5ApAQAAAAAAAAAAAAAAMwViJQAAAAAAAAAAAAAAIC5sjDvAYQwGo3ojjvuoIc97GFUFMW8hwMAAAAAAAAAAAAAwLpiPB7Tj3/8YzryyCNpMJi/j3FdipR33HEHHXXUUfMeBgAAAAAAAAAAAAAA65rbb7+dHv7wh897GOtTpHzYwx5GROVO3LFjx5xHAwAAAAAAAAAAAADA+mLv3r101FFHVTrbvFmXIqUJ8d6xYwdESgAAAAAAAAAAAAAAAsklleL8A84BAAAAAAAAAAAAAAAbGoiUAAAAAAAAAAAAAACAuQKREgAAAAAAAAAAAAAAMFfWZU5KAAAAAAAAAAAAAA1WV1dpeXl53sMAIJrhcEgLCwvZ5JycBURKAAAAAAAAAAAAACLat28fff/736fxeDzvoQCQhG3bttERRxxBmzZtmvdQZgKREgAAAAAAAAAAABue1dVV+v73v0/btm2jww47bN24zwBoYzwe09LSEv3gBz+gW2+9lY499lgaDPLO+giREgAAAAAAAAAAABue5eVlGo/HdNhhh9HWrVvnPRwAotm6dSstLi7Sv/7rv9LS0hJt2bJl3kNak7wlVAAAAAAAAAAAAIAOgYMS9Inc3ZOc9TNSAAAAAAAAAAAAAABAL4FICQAAAAAAAAAAAAAAmCsQKQEAAAAAAAAAAADAXLjooovo8Y9//LyHATIAIiUAAAAAAAAAAAAASMZTnvIUevnLX+712gsuuICuvvpq3QGBdQGqewMAAAAAAAAAAACAThmPx7S6ukrbt2+n7du3z3s4IAPgpAQAAAAAAAAAAABwGI/H9ODSylz+jcdj73E+5SlPoZe97GX0+7//+3TIIYfQrl276KKLLiIiottuu42KoqBvfvOb1evvu+8+KoqCrrnmGiIiuuaaa6goCvr0pz9NJ510Em3dupVOPfVUuueee+iTn/wkHXfccbRjxw76jd/4DXrwwQdnjuecc86hz3/+8/SOd7yDiqKgoijotttuq/r55Cc/SSeffDJt3ryZvvSlLzXCvc855xx61rOeRa997WvpsMMOox07dtDv/u7v0tLSUvWav//7v6cTTjiBtm7dSoceeiiddtpp9MADD3jvM5AncFICAAAAAAAAAAAAOPxkeZWOf/Wn59L3zRefQds2+Us2V1xxBZ1//vl0ww030HXXXUfnnHMOnXLKKXTsscd6v8dFF11E73rXu2jbtm30vOc9j573vOfR5s2b6YMf/CDt27ePnv3sZ9M73/lO+oM/+IM13+cd73gHffe736Wf/dmfpYsvvpiIiA477DC67bbbiIjoD//wD+ktb3kLPepRj6KDDz64Eks5V199NW3ZsoWuueYauu222+i3fuu36NBDD6U3vOENdOedd9ILXvACuuSSS+jZz342/fjHP6YvfvGLImEX5AlESgAAAAAAAAAAAIB1zIknnkivec1riIjo2GOPpXe961109dVXi0TK17/+9XTKKacQEdG5555Lr3rVq+iWW26hRz3qUURE9NznPpc+97nPzRQpDzzwQNq0aRNt27aNdu3a1fj/iy++mH75l395zffYtGkTvf/976dt27bRYx/7WLr44ovpla98Jb3uda+jO++8k1ZWVug5z3kOHX300UREdMIJJ3h/TpAvECkBAAAAAAAAAAAAHLYuDunmi8+YW98STjzxROvvI444gu65557g99i5cydt27atEijNc1/5yldE79nGE57whJmvedzjHkfbtm2r/t69ezft27ePbr/9dnrc4x5HT33qU+mEE06gM844g04//XR67nOfSwcffHD02MB8gUgJAAAAAAAAAAAA4FAUhSjkep4sLi5afxdFQaPRiAaDshQJD4VeXl6e+R5FUUx9z1gOOOCAqPbD4ZCuuuoquvbaa+nKK6+kd77znfRHf/RHdMMNN9AxxxwTPT4wP1A4BwAAAAAAAAAAAKCHHHbYYUREdOedd1bP8SI6WmzatIlWV1eD23/rW9+in/zkJ9Xf119/PW3fvp2OOuooIioF01NOOYVe+9rX0je+8Q3atGkTffjDH44eN5gv62NJAAAAAAAAAAAAAACI2Lp1Kz3pSU+iN7/5zXTMMcfQPffcQxdeeKF6v4985CPphhtuoNtuu422b99OhxxyiKj90tISnXvuuXThhRfSbbfdRq95zWvopS99KQ0GA7rhhhvo6quvptNPP50OP/xwuuGGG+gHP/gBHXfccUqfBnQFnJQAAAAAAAAAAAAAPeX9738/rays0Mknn0wvf/nL6fWvf716nxdccAENh0M6/vjj6bDDDqM9e/aI2j/1qU+lY489ln7pl36Jfv3Xf51+9Vd/lS666CIiItqxYwd94QtfoKc//en0Mz/zM3ThhRfSW9/6VjrzzDMVPgnokmK8Dmu07927lw488EC6//77aceOHfMeDgAAAAAAAAAAANY5Dz30EN166610zDHH0JYtW+Y9nA3LOeecQ/fddx995CMfmfdQesFax3Vu+hqclAAAAAAAAAAAAAAAgLkCkRIAAAAAAAAAQBbc+8ASffgb36eHlsMLbgAAdNmzZw9t37596j9paDcABhTOAQAAAAAAAACQBe/67L/Q+798K+1fHtHz/69HzHs4AIAWjjzyyDUrhB955JFR73/55ZdHtQfrF4iUAAAAAAAAAACy4L4Hl4iI6N7JFgCQHwsLC/ToRz963sMAPQTh3gAAAAAAAAAAsmA0qeu6urru6rsCAACIBCIlAAAAAAAAAIAsMNLkyggiJQAAbDQgUgIAAAAAAODJaDSmq//pbvrBj/fPeygA9BKjTa5CpAQAgA0HREoAAAAAAAA8ufaWH9K5V3yVLvrYTfMeCgC9ZDwJ94aTEgAANh4QKQEAAAAAAPDkhw+UDsof7oOTEgANxpWTcjTfgQAAAOgciJQAAAAAAAB4Yop6QD8BQIcRnJQAbDguuugievzjH99pfzt37qSiKOgjH/lIZ/2C2UCkBAAAAAAAwJPK5TWGgAKABmPkpASgFzzlKU+hl7/85V6vveCCC+jqq6/WHdCEf/qnf6LXvva19N73vpfuvPNOOvPMMzvpVwPJPl4vLMx7AAAAAAAAAKwXUNQDAF2Mk3J5FecYAH1nPB7T6uoqbd++nbZv395Jn7fccgsRET3zmc+koiiC32d5eZkWFxdTDQtMgJMSAAAAAAAAT0xRjxGclACoYM6sDZ+Tcu8dRA/eO+9RgPGYaOmB+fwT/M485SlPoZe97GX0+7//+3TIIYfQrl276KKLLiIiottuu42KoqBvfvOb1evvu+8+KoqCrrnmGiIiuuaaa6goCvr0pz9NJ510Em3dupVOPfVUuueee+iTn/wkHXfccbRjxw76jd/4DXrwwQdnjuecc86hz3/+8/SOd7yDiqKgoijotttuq/r55Cc/SSeffDJt3ryZvvSlLzXCvc855xx61rOeRa997WvpsMMOox07dtDv/u7v0tLSUvWav//7v6cTTjiBtm7dSoceeiiddtpp9MADD6w5rosuuoie8YxnEBHRYDCoRMrRaEQXX3wxPfzhD6fNmzfT4x//ePrUpz5VtTP78H/+z/9JT37yk2nLli30N3/zN0RE9Fd/9Vd03HHH0ZYtW+gxj3kMvfvd77b6/P73v08veMEL6JBDDqEDDjiAnvCEJ9ANN9xARKVg+sxnPpN27txJ27dvp5//+Z+nz3zmM1b7d7/73XTsscfSli1baOfOnfTc5z53zX283oGTEgAAAAAAAE8QigqALqjuTURLDxK96/8i2n440cu+Pu/RbGyWHyR645Hz6fv/voNo0wHeL7/iiivo/PPPpxtuuIGuu+46Ouecc+iUU06hY4891vs9LrroInrXu95F27Zto+c973n0vOc9jzZv3kwf/OAHad++ffTsZz+b3vnOd9If/MEfrPk+73jHO+i73/0u/ezP/ixdfPHFRER02GGHVSLaH/7hH9Jb3vIWetSjHkUHH3xwJZZyrr76atqyZQtdc801dNttt9Fv/dZv0aGHHkpveMMb6M4776QXvOAFdMkll9Czn/1s+vGPf0xf/OIXq+vHNC644AJ65CMfSb/1W79Fd955pzXet771rfTe976XTjrpJHr/+99Pv/qrv0o33XSTtf/+8A//kN761rfSSSedVAmVr371q+ld73oXnXTSSfSNb3yDXvziF9MBBxxAL3rRi2jfvn305Cc/mX7qp36KPvrRj9KuXbvo61//Oo0mizD79u2jpz/96fSGN7yBNm/eTH/9139Nz3jGM+g73/kOPeIRj6CvfvWr9LKXvYz++3//7/QLv/ALdO+999IXv/jFNffxegciJQAAAAAAAJ6MyTgp5zwQAHoKUioQ0U/uJVr6MdGPZjvWADCceOKJ9JrXvIaIiI499lh617veRVdffbVIpHz9619Pp5xyChERnXvuufSqV72KbrnlFnrUox5FRETPfe5z6XOf+9xMkfLAAw+kTZs20bZt22jXrl2N/7/44ovpl3/5l9d8j02bNtH73/9+2rZtGz32sY+liy++mF75ylfS6173OrrzzjtpZWWFnvOc59DRRx9NREQnnHDCzM+3fft2Ouigg4iIrHG95S1voT/4gz+g5z//+URE9Cd/8if0uc99jt7+9rfTpZdeWr3u5S9/OT3nOc+p/n7Na15Db33rW6vnjjnmGLr55pvpve99L73oRS+iD37wg/SDH/yAbrzxRjrkkEOIiOjRj3501f5xj3scPe5xj6v+ft3rXkcf/vCH6aMf/Si99KUvpT179tABBxxAv/Irv0IPe9jD6Oijj6aTTjrJax+vVyBSAgAAAAAA4InRTUYbWUABQBE4KYloPAl1H6/OdxyAaHFb6WicV98CTjzxROvvI444gu65557g99i5cydt27atEijNc1/5yldE79nGE57whJmvedzjHkfbttX7YPfu3bRv3z66/fbb6XGPexw99alPpRNOOIHOOOMMOv300+m5z30uHXzwweKx7N27l+64445KnDWccsop9K1vfWvquB944AG65ZZb6Nxzz6UXv/jF1fMrKyt04IEHEhHRN7/5TTrppJMqgdJl3759dNFFF9H/+l//qxJef/KTn9CePXuIiOiXf/mX6eijj6ZHPepR9LSnPY2e9rSn0bOf/Wxrv/QNiJQAAAAAAAB4gureAOhSOSk3cuEcfn0ZjYgGKCUxN4pCFHI9T9wiLkVR0Gg0osHk+OGh0MvLyzPfoyiKqe8ZywEHxO3T4XBIV111FV177bV05ZVX0jvf+U76oz/6I7rhhhvomGOOiR7fNPi49+3bR0REf/mXf0lPfOITG+MjItq6deua73fBBRfQVVddRW95y1vo0Y9+NG3dupWe+9znVrk3H/awh9HXv/51uuaaa+jKK6+kV7/61XTRRRfRjTfeWDlC+waudgAAAAAAAHhiCubASQmADubMgpPSPIabEsRh8hTyHIy8iI4WmzZtotXV8OP3W9/6Fv3kJz+p/r7++utp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Z9Pdd99tvceePXvorLPOom3bttHhhx9Or3zlK2llZSXdJwIAAAAAAK1YocCZClh//vlb6ISLPk033nbvvIfSipWTUuj0+uG+/fTzb/gM/d7/+EbiUdUg3Busd8xCwGi8gYs/obo3AGCDIhIpb7zxRrrzzjurf1dddRUREf3ar/0aERG94hWvoI997GP0oQ99iD7/+c/THXfcQc95znOq9qurq3TWWWfR0tISXXvttXTFFVfQ5ZdfTq9+9asTfiQAAAAAANAGd/Bpuvli+Nbt99HKaEw3/dv98x5KK6MIEfB7//4A/fihFfrW9+9LOyjGenHLAjCVdbCYoo61GgKREgCwcRCJlIcddhjt2rWr+vfxj3+cfvqnf5qe/OQn0/3330/ve9/76G1vexudeuqpdPLJJ9Nll11G1157LV1//fVERHTllVfSzTffTB/4wAfo8Y9/PJ155pn0ute9ji699FJaWlpS+YAAAAAAAKAk98rURPUYcy3sOyYuAsraGleYZtVi/s4b1oUG1jUo8EVwUgIANizBOSmXlpboAx/4AP32b/82FUVBX/va12h5eZlOO+206jWPecxj6BGPeARdd911RER03XXX0QknnEA7d+6sXnPGGWfQ3r176aabbpra1/79+2nv3r3WPwAAAAAAIGM93PxXoZ6Zjs8WemVjNB9Js2ox328b1oUG1jX89NiwFb7tvBLzGwcAAHRMsEj5kY98hO677z4655xziIjorrvuok2bNtFBBx1kvW7nzp101113Va/hAqX5f/N/03jTm95EBx54YPXvqKOOCh02AAAAAMCGxXLZZSpgmVHlKrBZIqBQQBl3XLUY2gZYj3C3sqbrOG9Q3RsAsDEJFinf97730ZlnnklHHnlkyvG08qpXvYruv//+6t/tt9+u3icAAAAAQN/gul+mGmDnQp4Uq3q2cIzm5cureuphbPVxAOYNF9c3bIVvVPcGAGxQFkIa/eu//it95jOfoX/4h3+ontu1axctLS3RfffdZ7kp7777btq1a1f1mq985SvWe5nq3+Y1bWzevJk2b94cMlQAAAAAADBhvA7Cvc0Qcw33jilMM+pAgI2pPg5AbuR6nVIHOSkBABuUICflZZddRocffjidddZZ1XMnn3wyLS4u0tVXX109953vfIf27NlDu3fvJiKi3bt307e//W265557qtdcddVV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j4D9a7TPc6vklZ8TESUxaUFKALjllltwyy23yFwCERERERERHWnScpMjSFl6LJCSrZV5v/d77WvVZ8pbDxFREhsS072JiIiIiIiILAsMTIb2qLST0wVU6XMX6tdrH8eeLW89RERJjEFKIiIiIiIiOrIE9qGUmUkJmCXfgLaWKg6LJSIKh0FKIiIiIiIiOrIEZVImUZCyer6WXUlERAMwSElERERERERHlsASb9lByrIpQIo+zXvcOXLXQkSUxHgJh4iIiIiIiI4sgeXegZ/L4HAC5/1Rm+49lkFKIqJIGKQkIiIiIiKiI0sylXsDwORvan+IiCgilnsTERERERHRkSWZyr2JiMgSBimJiIiIiIjoyBKUSZkrbRlERGQdg5RERERERER0ZEmmnpRERGQJg5RERERERER0ZMks1j6m5QNOjmIgIhoK+GpNRERERERER5b8kcD83wB5w2WvhIiILGKQkoiIiIiIiI48s78vewVERBQHlnsTERERERERERGRVAxSEhERERERERERkVQMUhIREREREREREZFUDFISERERERERERGRVAxSEhERERERERERkVQMUhIREREREREREZFUDFISERERERERERGRVAxSEhERERERERERkVQMUhIREREREREREZFUDFISERERERERERGRVAxSEhERERERERERkVQMUhIREREREREREZFUDFISERERERERERGRVAxSEhERERERERERkVQMUhIREREREREREZFUDFISERERERERERGRVAxSEhERERERERERkVQMUhIREREREREREZFUDFISERERERERERGRVAxSEhERERERERERkVQMUhIREREREREREZFUDFISERERERERERGRVAxSEhERERERERERkVQMUhIREREREREREZFUDFISERERERERERGRVC7ZCxgMVVUBAO3t7ZJXQkRERERERERENPSIuJqIs8k2JIOUHR0dAIDKykrJKyEiIiIiIiIiIhq6Ojo6kJOTI3sZUNRkCZfGwe/3Y//+/cjKyoKiKHH/+/b2dlRWVmLv3r3Izs5OwAqJouMepGTAfUiycQ+SbNyDlAy4Dw8NHz+SjXuQksFg96Gqqujo6EB5eTkcDvkdIYdkJqXD4UBFRcUh3092djZfREgq7kFKBtyHJBv3IMnGPUjJgPvw0PDxI9m4BykZDGYfJkMGpSA/TEpERERERERERERHNQYpiYiIiIiIiIiISKqjMkiZkpKCRYsWISUlRfZS6CjFPUjJgPuQZOMeJNm4BykZcB8eGj5+JBv3ICWDI2UfDsnBOURERERERERERHTkOCozKYmIiIiIiIiIiCh5MEhJREREREREREREUjFISURERERERERERFIxSElERERERERERERSJTRI+Zvf/AYzZsxAVlYWiouLccEFF2DLli1Bt+np6cHNN9+MgoICZGZm4qKLLkJ9fX3QbX74wx9i2rRpSElJwdSpU8P+rLfeeguzZs1CVlYWioqKcNFFF2HXrl0x1/jiiy9i3LhxSE1NxeTJk/HGG29EvO2NN94IRVHwwAMPxLzfPXv24Nxzz0V6ejqKi4txxx13oL+/P+g2Dz/8MMaPH4+0tDSMHTsWTz/9dMz7pfgczXsw1pq3bNmCU089FSUlJUhNTcWoUaNw1113oa+vL+Z9U3y4DyOvefHixVAUZcCfjIyMmPdN1h2te/CLL77ApZdeisrKSqSlpWH8+PF48MEHg25TW1uLyy67DGPGjIHD4cDChQtjrpXixz0YeQ8uX7487OtgXV1dzDWTddyDkfcgYO285Eh4DK+++uoBz7Wzzjor5v3GOrfje4k9uAcj70G+l9iH+zCxca6EBilXrFiBm2++GR9//DGWLl2Kvr4+nHnmmejs7DRuc+utt+K1117Diy++iBUrVmD//v34xje+MeC+vvvd7+KSSy4J+3N27tyJBQsW4LTTTsPatWvx1ltvoampKez9BProo49w6aWX4tprr8WaNWtwwQUX4IILLsD69esH3Pbll1/Gxx9/jPLy8pj/b5/Ph3PPPRderxcfffQRnnrqKTz55JP45S9/adzmz3/+M+68804sXrwYGzZswN13342bb74Zr732Wsz7J+uO1j1oZc1utxtXXnkl3n77bWzZsgUPPPAAHn30USxatMjy/ZM13IeR13z77bejtrY26M+ECRPwrW99y/L9U2xH6x5cvXo1iouL8eyzz2LDhg34+c9/jjvvvBMPPfSQcZve3l4UFRXhrrvuwpQpU2LeJw0O92DkPShs2bIl6LWwuLg45v2TddyDkfeg1fOSI+UxPOuss4Kea88991zU+7Vybsf3EntwD0begwLfSxKP+zDBcS7VRg0NDSoAdcWKFaqqqmpra6vqdrvVF198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8MEHA8q9r776alxwwQW4++67UVRUhOzsbNx4443wer2Wf77f78dPfvIT5Ofno7S0FIsXLw76/p49e7BgwQJkZmYiOzsbF198Merr6wesIdDChQsxd+5c4+9z587FLbfcgoULF6KwsBDz58+P5yEiIiIioiTAICURERHREezBBx/E7Nmzcd1116G2tha1tbWorKwEAPz0pz/Fb3/7W2zatAnHHnts2H+/bNkybNq0CcuXL8dzzz2Hv//977j77rst//ynnnoKGRkZ+OSTT/C73/0O99xzD5YuXQpAC2AuWLAAzc3NWLFiBZYuXYodO3bgkksuifv/+dRTT8Hj8eDDDz/EI488Eve/JyIiIiK5WO5NREREdATLycmBx+NBeno6SktLAQCbN28GANxzzz0444wzov57j8eDxx9/HOnp6Zg4cSLuuece3HHHHfjVr34FhyP29e5jjz0WixYtAgBUV1fjoYcewrJly3DGGWdg2bJlWLduHXbu3GkETp9++mlMnDgRn332GWbMmGH5/1ldXY3f/e53lm9PRERERMmFmZRERERER6np06fHvM2UKVOQnp5u/H327Nk4ePAg9u7da+lnhGZolpWVoaGhAQCwadMmVFZWGgFKAJgwYQJyc3OxadMmS/cvTJs2La7bExEREVFyYZCSiIiI6CiVkZGR8J/hdruD/q4oCvx+v+V/73A4oKpq0Nf6+voG3M6O/wsRERERJQ6DlERERERHOI/HA5/PN6h/+8UXX6C7u9v4+8cff4zMzMyg7MfBGj9+PPbu3RuUlblx40a0trZiwoQJAICioiLU1tYG/bu1a9ce8s8mIiIiouTCICURERHREW7EiBH45JNPsGvXLjQ1NcWVyej1enHttddi48aNeOONN7Bo0SLccsstlvpRxjJv3jxMnjwZl19+OT7//HN8+umnuPLKKzFnzhyjFP20007DqlWr8PTTT2Pbtm1YtGgR1q9ff8g/m4iIiIiSC4OUREREREe422+/HU6nExMmTEBRURH27Nlj+d+efvrpqK6uximnnIJLLrkE559/PhYvXnxY1qUoCv7xj38gLy8Pp5xyCubNm4dRo0ZhyZIlxm3mz5+PX/ziF/jJT36CGTNmoKOjA1deeeVh+flERERElDwUNbTJDxERERERgKuvvhqtra145ZVXZC+FiIiIiI5wzKQkIiIiIiIiIiIiqRikJCIiIqK47dmzB5mZmRH/xFNSTkRERETEcm8iIiIiilt/fz927doV8fsjRoyAy+Wyb0FERERENKQxSElERERERERERERSsdybiIiIiIiIiIiIpGKQkoiIiIiIiIiIiKRikJKIiIiIiIiIiIikYpCSiIiIiIiIiIiIpGKQkoiIiIiIiIiIiKRikJKIiIiIiIiIiIikYpCSiIiIiIiIiIiIpGKQkoiIiIiIiIiIiKT6/xADwLwQpy7PAAAAAElFTkSuQmCC", + "image/png": "iVBORw0KGgoAAAANSUhEUgAABREAAAKnCAYAAAARNgr5AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8hTgPZAAAACXBIWXMAAA9hAAAPYQGoP6dpAAEAAElEQVR4nOz9e7wlV13mjz+rau99ujtN5zZJd6IhBIkDgQAx+IU2jjAQE0JEgSCKjEM0oz95BRnIgMoYSQgIyA9QhKCoEHAQncEvMBi5JEQCSEK4XwQHFIgdzI0Rkk6TdJ+9q9b3j6pVtdbatc/pWmudtfap87xfr37tc+ldp2rvXatqPev5fB4hpZQghBBCCCGEEEIIIYSQBWSpd4AQQgghhBBCCCGEELLcUEQkhBBCCCGEEEIIIYSsCUVEQgghhBBCCCGEEELImlBEJIQQQgghhBBCCCGErAlFREIIIYQQQgghhBBCyJpQRCSEEEIIIYQQQgghhKwJRURCCCGEEEIIIYQQQsiaUEQkhBBCCCGEEEIIIYSsySj1DrhSliVuvfVW3O9+94MQIvXuEEIIIYQQQgghhBCyqZBS4p577sGJJ56ILFvba7hpRcRbb70VJ510UurdIIQQQgghhBBCCCFkU3PLLbfgB3/wB9f8P5tWRLzf/e4HoDrIXbt2Jd4bQgghhBBCCCGEEEI2F/v378dJJ53U6GxrsWlFRFXCvGvXLoqIhBBCCCGEEEIIIYQ4cjitAhmsQgghhBBCCCGEEEIIWROKiIQQQgghhBBCCCGEkDWhiEgIIYQQQgghhBBCCFmTTdsT8XCQUmI2m6EoitS7Qog3eZ5jNBodVp8CQgghhBBCCCGEkJAMVkRcXV3FbbfdhnvvvTf1rhASjB07duCEE07AZDJJvSuEEEIIIYQQQgjZQgxSRCzLEt/61reQ5zlOPPFETCYTurfIpkZKidXVVXznO9/Bt771LZx66qnIMnYjIIQQQgghhBBCSBwGKSKurq6iLEucdNJJ2LFjR+rdISQI27dvx3g8xr/8y79gdXUV27ZtS71LhBBCCCGEEEII2SIM2spEpxYZGvxME0IIIYQQQgghJAVUJAghhBBCCCGEEEIIIWtCEZEQQgghhBBCCCGEELImFBFJUC6//HI88pGPTL0bhBBCCCGEEEIIISQgFBHJujzucY/D85///MP6vy984Qtx3XXXbewOEUIIIYQQQgghhJCoDDKdmcRHSomiKLBz507s3Lkz9e4QQgghhBBCCCGEkIBsGSeilBL3rs6S/JNSHvZ+Pu5xj8Pznvc8/MZv/AaOOeYY7NmzB5dffjkA4Oabb4YQAl/4whea/3/XXXdBCIHrr78eAHD99ddDCIEPfehDOOOMM7B9+3Y8/vGPx5133okPfOADeMhDHoJdu3bhF37hF3Dvvfeuuz8XXnghPvrRj+L1r389hBAQQuDmm29u/s4HPvABnHnmmVhZWcHf//3fz5UzX3jhhXjKU56Cl770pTjuuOOwa9cu/Nqv/RpWV1eb//PXf/3XOP3007F9+3Yce+yxOPvss/H973//sF8zQgghhBBCCCGEELKxbBkn4n3TAqe95ENJ/vZXrzgXOyaH/1K//e1vxyWXXIKbbroJN954Iy688EKcddZZOPXUUw97G5dffjne+MY3YseOHXjGM56BZzzjGVhZWcE73/lOHDhwAE996lPxhje8Ab/5m7+55nZe//rX4+tf/zoe9rCH4YorrgAAHHfccbj55psBAL/1W7+F17zmNXjgAx+Io48+uhEzda677jps27YN119/PW6++Wb80i/9Eo499lj87u/+Lm677TY885nPxKtf/Wo89alPxT333IOPf/zjvYRXQgghhBBCCCGEELKxbBkRcTPx8Ic/HJdddhkA4NRTT8Ub3/hGXHfddb1ExJe//OU466yzAAAXXXQRXvziF+Mb3/gGHvjABwIAnv70p+MjH/nIuiLikUceiclkgh07dmDPnj1zv7/iiivwkz/5k2tuYzKZ4K1vfSt27NiBhz70objiiivwohe9CC972ctw2223YTab4WlPexpOPvlkAMDpp59+2MdJCCGEEEIIIYQQQjaeLSMibh/n+OoV5yb72314+MMfbnx/wgkn4M4773Texu7du7Fjx45GQFQ/+9SnPtVrm1086lGPWvf/POIRj8COHTua7/fu3YsDBw7glltuwSMe8Qg84QlPwOmnn45zzz0X55xzDp7+9Kfj6KOP9t43QgghhBBCCCGEEBKGLSMiCiF6lRSnZDweG98LIVCWJbKsamGpl/pOp9N1tyGEWLhNX4444giv5+d5jmuvvRY33HADrrnmGrzhDW/Ab//2b+Omm27CKaec4r1/hBBCCCGEEEIIIcSfLROsMgSOO+44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BAA8/elPx0c+8pF1RcQjjzwSk8kEO3bswJ49e+Z+f8UVV+Anf/In19zGZDLBW9/6VuzYsQMPfehDccUVV+BFL3oRXvayl+G2227DbDbD0572NJx88skAgNNPP33d49u5cyeOOuooADD26zWveQ1+8zd/Ez//8z8PAPi93/s9fOQjH8Ef/MEf4Morr2z+3/Of/3w87WlPa76/7LLL8NrXvrb52SmnnIKvfvWrePOb34xnP/vZeOc734nvfOc7+PSnP41jjjkGAPCgBz2oef4jHvEIPOIRj2i+f9nLXob3vOc9eN/73ofnPve52LdvH4444gj81E/9FO53v/vh5JNPxhlnnHFYrzEhhBBCCCFDgD0RCSEpoRPRn60jIo53VI7AVH+7Bw9/+MON70844QTceeedztvYvXs3duzY0QiI6mef+tSnem2zi0c96lHr/p9HPOIR2LGjfQ327t2LAwcO4JZbbsEjHvEIPOEJT8Dpp5+Oc889F+eccw6e/vSn4+ijj+69L/v378ett97aiKeKs846C1/84hcX7vf3v/99fOMb38BFF12EX/mVX2l+PpvNcOSRRwIAvvCFL+CMM85oBESbAwcO4PLLL8ff/u3fNsLofffdh3379gEAfvInfxInn3wyHvjAB+KJT3winvjEJ+KpT32q8boQQgghhBAyZOhEJISkRBU8UkR0Z+uIiEL0KilOiR3yIYRAWZbIsqqFpV7qO51O192GEGLhNn054gi/1zTPc1x77bW44YYbcM011+ANb3gDfvu3fxs33XQTTjnlFO/9W4S+3wcOHAAA/Omf/ike/ehHz+0fAGzfvn3N7b3whS/Etddei9e85jV40IMehO3bt+PpT3960/vxfve7Hz73uc/h+uuvxzXXXIOXvOQluPzyy/HpT3+6cVQSQgghhBAyZGTjROQEnhASn7JU4U7VYkaeuQe0blW2TrDKAFB98vQegHrIykYxmUxQFIXz87/4xS/ivvvua77/5Cc/iZ07d+Kkk04CUAmaZ511Fl760pfi85//PCaTCd7znvf0/ju7du3CiSeeiE984hPGzz/xiU/gtNNOW/i83bt348QTT8Q3v/lNPOhBDzL+KSHz4Q9/OL7whS/gu9/9buc2PvGJT+DCCy/EU5/6VJx++unYs2fPXDjKaDTC2WefjVe/+tX40pe+hJtvvhl/93d/B8D/NSaEEEIIIWTZaSbw1BAJIQnQTdB0I7qxdZyIA2D79u14zGMeg1e96lU45ZRTcOedd+LSSy/d8L/7gAc8ADfddBNuvvlm7Ny5c2FJ7yJWV1dx0UUX4dJLL8XNN9+Myy67DM997nORZRluuukmXHfddTjnnHNw/PHH46abbsJ3vvMdPOQhD3Ha1xe96EW47LLL8EM/9EN45CMfiauuugpf+MIXmgTmRbz0pS/F8573PBx55JF44hOfiEOHDuEzn/kMvve97+GSSy7BM5/5TLziFa/AU57yFLzyla/ECSecgM9//vM48cQTsXfvXpx66ql497vfjSc/+ckQQuB3fud3DKfn1VdfjW9+85v4iZ/4CRx99NF4//vfj7Is8e///b8H0P0aK+cpIYQQQgghQ6CkE5EQkpBSq+pkb1Y3qFJsMt761rdiNpvhzDPPxPOf/3y8/OUv3/C/+cIXvhB5nuO0007Dcccd1/T5O1ye8IQn4NRTT8VP/MRP4Od+7ufw0z/907j88ssBVO7Bj33sY3jSk56EH/7hH8all16K1772tTjvvPOc9vV5z3seLrnkEvy3//bfcPrpp+ODH/wg3ve+960bSvNf/st/wZ/92Z/hqquuwumnn47HPvaxeNvb3tY4ESeTCa655hocf/zxeNKTnoTTTz8dr3rVq5py59e97nU4+uij8WM/9mN48pOfjHPPPRc/8iM/0mz/qKOOwrvf/W48/vGPx0Me8hD88R//Mf7yL/8SD33oQwH4v8aEEEIIIYQsO+yJSAhJid4abkYnohNC6q/iJmL//v048sgjcffdd2PXrl3G7w4ePIhvfetbOOWUU7Bt27ZEe0gA4MILL8Rdd92F9773val3ZRDws00IIYQQQjYrl/3vf8Dbb/wXPPTEXfjb5/2H1LtDCNlivOZDX8MbP/LPAIBP/fYTcPz9OKcG1tbXbOhEJIQQQgghhBCy4SgDIp2IhJAUFIYTkeOQCxQRtzj79u3Dzp07F/5LWVa71n59/OMfT7ZfhBBCCCGEkP4MtZz5K7fejVe8/x9x933T1LtCCFkDvScig1XcYLDKFufEE09cM+H5xBNP9Nr+2972NufnrrVfP/ADP+C8XUIIIYQQMgyklHjuX34eAsAbf+FH1v3/JC1DdSL+0fXfwNVfug2nHr8TP/uok1LvDiFkAdJIZx7WOBQLiohbnNFohAc96EGpd6OTZd0vQgghhBCyHNy7WuBvv3QbAOD3LpjhiBVOb5YZ1Y5/aKmoB6cFAOC++pEQspyUpZ7OTCeiC4MuZ96kmTGELISfaUIIIYSQFr00reB90tIz1HJmdTjssUbIcqMPPdMZz1cXBikijsdjAMC9996beE8ICYv6TKvPOCGEEELIVkafEJYDE6aGSCO2DcwBNFRxlJChYfREHNg4FItB+v3zPMdRRx2FO++8EwCwY8cOCCES7xUh7kgpce+99+LOO+/EUUcdhTzPU+8SIYQQQkhy9CqNoZXIDpFWbEu8I4FpxVF+BglZZvRrxnQ2sIEoEoMUEQFgz549ANAIiYQMgaOOOqr5bBNCCCGEbHXoRNxcyCZYZViTd9mIo8M6LkKGhn6ZoOjvxmBFRCEETjjhBBx//PGYTqepd4cQb8bjMR2IhBBCCCEa7Im4uSgHGqwy1OMiZGjo14np0CzRkRisiKjI85zCCyGEEEIIIQPEEBEp4Cw9ZeNEHNZ7pQyIQzsuQoaGUc7MICQnBhmsQgghhBBCCBk+0ihnTrcf5PBQJedDc+zRiUjI5kC/TszoRHSCIiIhhBBCCCFkU8Jy5s2Fer+G1r9SDtRhScjQ0K8ZqxQRnaCISAghhBBCCNmU6JoNBZzlR3fsyQGJvs1xsTySkKXGCFbh+eoERURCCCGEEELIpkR3tJUDEqWGipGmPaC3q2Q6MyGbAn3xYsbz1QmKiIQQQgghhJBNiaSrZFMx1Am8EkTZE5GQ5cYsZ+b56gJFREIIIYQQQsimRJ8QDs2J+E933IN7Dk5T70ZQNqL8/Gu334PvH5oF2ZYr6khYUk/IcmOWMw9nISMmTiLiAx7wAAgh5v5dfPHFAICDBw/i4osvxrHHHoudO3figgsuwB133GFsY9++fTj//POxY8cOHH/88XjRi16E2Szt4E8IIYQQQgjZPBjBKgMScL71f7+Pn/z9j+G57/x86l0JSmk4Ef3fr6/cejfO/YOP4YXv+qL3tnyQTGcmZFOgj0FTiohOOImIn/70p3Hbbbc1/6699loAwM/+7M8CAF7wghfgb/7mb/Cud70LH/3oR3HrrbfiaU97WvP8oihw/vnnY3V1FTfccAPe/va3421vexte8pKXBDgkQgghhBBCyFbAcLYNyIl46133AQC+/b17E+9JWIyeiAEEt3/9XvU63ZL4dWp7Ig7nM0jIEDFFRJ6vLjiJiMcddxz27NnT/Lv66qvxQz/0Q3jsYx+Lu+++G295y1vwute9Do9//ONx5pln4qqrrsINN9yAT37ykwCAa665Bl/96lfxjne8A4985CNx3nnn4WUvexmuvPJKrK6uBj1AQgghhBBCyDDRe+yFEKWWhaGKUjKwE1G9TtNZ2tdJtXekE5GQ5UZvxco+um5490RcXV3FO97xDvzyL/8yhBD47Gc/i+l0irPPPrv5Pw9+8INx//vfHzfeeCMA4MYbb8Tpp5+O3bt3N//n3HPPxf79+/GVr3yl8+8cOnQI+/fvN/4RQgghhBBCti4b0WNvGRhqUEfo8nNVjThNHNLCdGZCNgcsZ/bHW0R873vfi7vuugsXXnghAOD222/HZDLBUUcdZfy/3bt34/bbb2/+jy4gqt+r33Xxyle+EkceeWTz76STTvLddUIIIYQQQsgmRkITpQZUzjxUJ6LhAgohIionYmIxQH30hvZ+ETI09FM09eLDZsVbRHzLW96C8847DyeeeGKI/VnIi1/8Ytx9993Nv1tuuWVD/x4hhBBCCCFkudHngEOaDw41qMNwIgYoJWxep8RliUMVfQkZGnpLhdRtEDYrI58n/8u//As+/OEP493vfnfzsz179mB1dRV33XWX4Ua84447sGfPnub/fOpTnzK2pdKb1f+xWVlZwcrKis/uEkIIIYQQQgaEIUoNyYlYC6JDE6X0tyjE+6Ven9ROxHKgoi8hQ8NMiB/QylNEvJyIV111FY4//nicf/75zc/OPPNMjMdjXHfddc3Pvva1r2Hfvn3Yu3cvAGDv3r348pe/jDvvvLP5P9deey127dqF0047zWeXCCGEEEIIIVsEQ5Qa0ISwXJIy3dCYPRH9j60VEdOKdyxnJmRzYJQzM1jFCWcnYlmWuOqqq/DsZz8bo1G7mSOPPBIXXXQRLrnkEhxzzDHYtWsXfv3Xfx179+7FYx7zGADAOeecg9NOOw2/+Iu/iFe/+tW4/fbbcemll+Liiy+m25AQQgghhBByWJiiVMIdCUw5UFGq3Kh05mVxIlKUIGSpYbCKP84i4oc//GHs27cPv/zLvzz3u9///d9HlmW44IILcOjQIZx77rl405ve1Pw+z3NcffXVeM5znoO9e/fiiCOOwLOf/WxcccUVrrtDCCGEEEII2WKETvtdFjaiJ+L/PXAI4yzDkTvGwbbZF/1wQghuSgNILd4NVfQlZGgYCxkUEZ1wFhHPOeccoymlzrZt23DllVfiyiuvXPj8k08+Ge9///td/zwhhBBCCCFki6NrNuWQeiIGFqUOTgs84bUfxf22jfDx3/iPEEIE2W5fZGDRV73nq0UJKWWy42p7IlKUIGSZ0U9RljO74Z3OTAghhBBCCCEpCC1KLQt62u8i40Yf9t83xd33TfHt792XNPxD/9MhglVCl0e7wp6IhGwcq7Nw4jzLmf2hiEgIIYQQQgjZlAzXiRjasdd+fXBaeG/PfT/CHpe+jZQlzUxnJmRjeP+Xb8PDLvsQrv7SrUG2p18meL66QRGREEIIIYQQsikZbk/E9usQE13d9XcooKun934EFv307a0mdBXpzlFCSDi+cMtdWC1KfH7fXUG2RyeiPxQRB87d907xpNd/HG+6/p9T7wohhBBCCCFBGaqIGDzFWNtGSieiLo6G7IkIpA1JUIdCZxMhYVFjV6jzmyKiPxQRB86X/vUufPW2/fjfnw9j/10WDhya4R2f/Bd8555DqXeFEEIIIYQkIrQotSwYvQMDO/YOTtM79oAwIST665QyJEHSiUjIhlAEbhWwLGPGZoYi4sBRF7KhJYX9v5/9Ni597z/gzR/9RupdIYQQQgghiTCciAPtiRhGbNPLmZejJ2KIHpa6aJfSVdQ6EYc15yIkNWqYCNXzVC6Je3kzQxFx4Aw1Kezu+6bGIyGEEEII2XoYwSoDut8NnTqtC3YpnYhGr8cAokC5NCJi7USks4mQoLSmKDoRlwWKiANnqElhbF5MCCGEEEKG2xOx/TpIsIqmry2LEzFIOnPg3pGulIGFDkJIRatnhFkkWBb38maGIuLAUSfJkFZmAV6oCSGEEEKI5dgb0G2hPtEN7UQ8lLQnYvt16MCY1YSp00Ot/iIkNaFDi0KHVm1FKCIOnKEmhZW8UBNCCCGEbHl0c8qQFs1l4ARRM1hlmE7EZShnHtqci5DUhE5n1lsqpFx42MxQRBw4Q00KKwLbmgkhhBBCyOZjuMEq7dfBnYhL4NgDQh1X+3XScmYaHAjZEEK3MQsdWrUVoYg4cIbrRBymOEoIIYQQQg6f0GLbshC65E7fxJCciEawSkJxNHTfNkJIhTrFQ4WglIZ7eTjXjJhQRBw4akV2SOUdAHsiEkIIIYQQs+x3SPe7ocVRfRspnYihxVEjJCHh+8+eiIRsDKEFet0NzWAVNygiDhw50P4cLBkghBBCCCHLUs4aGhncibgsPRHbr4sAooDRE3EJxNEhfQY3Aiklvvf91dS7QTYRzbm1AU7EUNvcalBEHDhDLfsNPZgQQgghhJDNh4TmRBxUT0S97Dd0sErKnoiBxdFS3156EVHKYTliQ/Oyq/8RZ778Wnzp23el3hWySSgCVyDqm6ET0Q2KiANHnRdD68+hLs5DE0cJIYQQQsjhM9yeiO3XIRbNzWCVdE7EUbmK87NPYhcOBA9WWU1oLhiqIzY0/+f2/Sgl8E93HEi9K2SToIaucCKiuZAhB7T4FAuKiANHnSSlxKBOkDYwZljiKCGEEEIIOXz0+9thpTOHduy1X6d0Ip4vr8eVkz/ExaP/HabXo1GamOa47DnWkMTs0KjXZkiuYbKxtBWIYc5v2ynMcJX+UEQcOMaN1YAuaMVAy7QJIYQQQsjho4sRQyojlYGdbcWSOBGPkvsBAMeKe4KXM6cqTbQPgyaHxVBEJH0pAlcg8nz1hyLiwBmqtX6ogTGEEEKAV77/H/Hcd35uUA56QsjGoM//htTeShfHQvRENINV0r1Qmaz+9giz4KnTqcqZbUGMJofFtEaQxDtCNg3qdAq1SGCfr3Qi9oci4sApjBuQ4Zwg6l5qSMdECCGk4qpP3Iyrv3Qbbt9/MPWuEEKWHMOJOKCFh+A9EfVglYRORECJiGVwh2Wqcmb7c0eTw2LoRCR9kYErEO3NMFylPxQRB07oBLRloaATkRBCBosqLQkxcSaEDBtdixjS4nIZuCWRvo1DS+BEzFGEcVguQTmzrYeFeL/2/du9eOG7voh/uuMe720tExQRSV/UvD+UY9CucuG9Zn8oIg6c4SbWsSciIYQMFTW0c4wnhKxH6ACSZSG0EUDfRMqeiALV3x6hDFLSqh9XqrLEjXAivvvz38Zff/bbeOen9nlva5kI3d+ODJ/Q94Tz5cx0IvaFIuLACb2KuSyoVUc2QiWEkGEx1KRVQsjGoN/ehgpW+czN38X/+OS/JO3LGtoIoM8JUjoRRb0foZyI+nViWYJVigBi5qFZdSwHDs68t7VMUEQkfWmzEEL1RDS/p4jYn1HqHSAby2B7IqoVCdqPCSFkUAzVQU8I2RjKDVh4+O/v+TK+fscB/D8POAb/fs/9gmyzL2VgcUwfT1P2RMyanohF8HTmVGWJ807EcGXa901T9q8MjzpHuUZIDpeiMQ9tjBNxSA72WNCJOHD0c2RIrr2SPREJIWSQDNVBTwjZGHS3YCgnonJ/7T84DbI9FzbSiXgwoTAlZCsihu71mKwnovVnQx5XyvdqI1DnKCsNyOHSzPsDLRLY14nV2XA0klhQRBw4Q52MsSciIYQMk6E66AkhG4MhtgUSJtQ2pwknl+F7ImrlzImOS0rZOBFzESad2XRsLosTMVzq9NCciOq14fWdHC7KB7VR5cw0JfWHIuLAGWpZWDuYDOeYCCGEDDdplRCyMWzEgrna5jThGBQ+nbn9OpW7TUqznDmEc9QMVknVE9E8jiDOUVXOvDosEVEdV8p+o2RzEdyJWG9vkldSGHsi9oci4sAZqhOxoBOREEIGyUb0NyOEDBcjWCWYE7EWERM6EfXjCt07MJUTsZQSGar9CNUTcRnKmTfC2aSuf/cOTERsnYiJd4RsGvQ2ZiHEZ7WJlRFFRFcoIg4co9nwgAS30ClNhBBClgNdBAjV34wQMlzkhjgRq8eUk0vTCBAgqGMJeiKWEsgRtifiMpQz28JGiPfruO//M/7H+BV4wMF/9N7WMtG0pOIiITlM9NM65JgxqUXEVIFMmxmKiANnsOXMKp15QMdECCEkvPuGEDJsTLEt7DZXE4qIMvBYWBgiYpmknLSUEkL1RESYnojL6EQMsRsPu+sj+A/5P+A/rl7vv7ElQr1fXCQkh8tG9YelE9EdiogDpwh80i0LoaPeCSGELAf6xIKTDELIepgL5oEa79cbTeVsA0xxNIRTZi6RNMHEWUog18qZQ7xfhVF1tRw9EUPsh5BVMvioPOi9rWVCzd1CtR4gw6cMLiJWj5NGRORnsS8UEQfORpR4LANqMJGSk0xCyNblqk98C2e/7qO4Y/9wJhmhbxYJIcPG7KMaapvV47KUM4ecOCsOTuMfWyklcqGciOHLmVdnaa4ZGxGsImohMi+n3ttaJtS8jeXM5HDRh+HCc5DXtZEJnYjOUEQcOEMNVuEkkxBCgL/90m345zsP4NM3fzf1rgTDcBVxkkEIWQd9mAi1sNwEqyQVEduvQzv2AODQLH5fRL2ceSxCiYjt16mciPalKsjcRFbvz0hOMRuQyNE4ETl/I4eJLvxNPc9x/WO3MsoBMGPBBYqIA2cZLqwbgX4oQxJHCSGkD0pkG1JTaP1mcUiTjKKUuOR/fgF/fuPNqXeFkEGhjxPhglXqnogJ05k3qg+Y4lASJ2IbrDKsnoiWEzHANVnIGQBggikOJvwchka9VgO6vJMNpgg4xuvPZzmzOxQRB85G3FgtA2avx+FcWAkhpA/LUHIXmqEGq3z9jnvw7s//K678yD+n3hVCBsVGuJfbsTVhT8SAJXzA/DwgRUKzlBKZ0RMxrDia6v2yDyPEtUvI6gMwwQz3raZJ094I1Hs+pHkp2VjMc9zXidhui8Eq7lBEHDhDLWceaq9HQgjpwzI0/w9NMVAnorpJHdJ7RcgyIBF+zFDbSVlGutE9EQ8lcLeVEsjQ9kQM4aJfSidiwHLmCaZJBN+NQErZfA4ZrEIOF/2j4ntu6dtSIuKQqnliQRFx4JQBT7plYqhOFUII6YO6CR+SI1sXAYY0vqtDGVJvK0KWgY1xIi5bT8Tw5cwphKlSyqaceYQyyHEtg4gordc2SDpzvY2JmOHegTgR9feKIiI5XIqAbmPTiZjX2+R9WV8oIg6coQaQhOyNQAghmxU1/oXq2/VvBw7hLz+1D/ccTJcGaYQkDGiS0Qq+wzkmQpaBjeijqjazmtChIgO37pkPVkmTzmyUMwcY4/VNpHIU2R+7IOnMaJ2I9w3EiVgYlWQJd4RsKkJWVurbYk9EdygiDpyh9kQM3WyaEEI2I6GFqT/9+Lfw4nd/Gf/rM98Osj0XhtqGoymPHNAxEbIMhF4wN5JAl6WcOcAkdxmciNIuZw7hRNSOa3VJypnZE7Ebw4nIayE5TPQ1FP+eiO3Xk6acmYp2XygiDpyhlv0aK1lcPSCEbFGa5v+BHCV337cKALjr3tUg23NhqA56dShDEkYJWQZCl/0uQ3kssAHlzHPBKkvgRAzgsDRaYKRyIlqHEeL9yvR05qE4EXVzy4AqDcjGEvK+UBrlzAxWcYUi4sApN6DEYxnQL9ZD6gVGCCF9aIJVAo3v6gY/aSKpXs48pOtWfT0uSjnXP4sQ4o5xrxvg3NKHnaVxIgZ27AHAoVmKnohoeiLmQqIIsA+FlI27cVmCVYI6EcVsOOXM7IlIHDArVMI7EUPdQ28lKCIOnKE6EYda7kYIIX1Qk8JQE6fG2TigifOyMNTAGEJSEzK5EzDHoNVZyp6I7ddhjsv8PokTsWwFPwCQpb849u9md+JzK/8//NboL5emt1kR4BoqmnRmljOTrU1hlDNvQE/EBP1hNzsUEQeO2RNxOCfIUCeZhBDSh6YnYigRsQy7Pad9COwqWhZClyYSQipKw93kvz192FmeBZWwZb9AKieiKSKKumTXhx8qvomjxPfxY9k/DNOJONRgFV4GyWEiA5qH1DiYCWCc1T0ReU/WG4qIA2eoYhsnY4QQ0rZ2COW+UNeMlImk+lx5SOP7UK/HhKQmeE/EwOKdK6GrbuxtpOmJCOTQe1b4i4gCbQBJqgWwDUln1oJVhtgTcUiLhGRj0T8roYJVMiEwykWQbW5FKCIOnKGKbSwLI4SQ9sYq1A2Q0g6XxYk4pOuWIUzwhpWQYIQeM5alnDkvDuFPx6/Fz+UfCbJQNF/OnMiJKLRy5sJfREQtto0xS1bOXEqJHxTfwS/m12AFq2GciFDlzFPcy3JmsoUxAnl8nYhSOREFxjmDVVwZpd4BsrEMdTIWssEqIYRsVtoglMDlzAmvF6H7gC0Lkk5EQjYEGbgFgjT6b6W7xzzl4Ffxk/lncX9xB15W/qz39uzX5lCCPmDSLmcO4USs5wErYorVooSUEkII7+32QUqJF4zehQvyv8d+uQNF+TDvbWb1B3EkShxcXfXe3jIw1EoDsrGYLSYCiYgZMK6diKlS3TczdCIOnOGKiO3XQzrx3//l2/C0N30C3/7eval3hRCyCWh7IoYuZ043cTZ7Jg1nfOfkiZCNIXSIYMjSOR+yul/gBNMgZdXz5cxp05mBMMEqqB17Y1SvV4rxtZTALlT37keLA0E+h5lsX5vpoYPe21sG9M8xy5nJ4RJSz1Cb0p2IKe95NysUEQfOUNOZQ9qal4l3f+7b+Ny+u/Dxf/q/qXeFELIJUMNfqBsgNZ4uTTnzgBaJiiURJggZGkYYU+CeiEnP1VpEGosiaK/HUVa5b1I4EatglfZY8gDBKnqKMRCuR3AfylI24ugKpkGqpIQmts5WhyEimsFpCXeEbCpC9qnVy5lHtYg4JENSLCgiDpyhOhGHWha2DBN4QsjmoR0zQjkRq8dUfaUAK4VvQE6FkOmChJAWo/93gDHD6ImYcCxUwRojFEHuddUYtGOSAwAOpXAiljDKmWU59d6m0HoiAsA0QZsj3WFZOUcDOxFX7/Pe3jIwG6gJhGwsISsQ1baEACaqnJmt0XpDEXHglAMdrIuBTsaaUIMBHRMhZONQY3ywnoiBg1rc9qH9elDju/aScownJByhBXqj/1YCt15DXeo7QiAnYqlExKol/sFZomAVzYkoQpQzy1a8A9K8Z3qvx4mYBXHRG07EQ4e8t7cMMJ2ZuGAGqgZ0ImaqnJmfxb5QRBw45UAde0Mt01aD5JAmzoSQjaMR/QKNGUshIg508cu4HvOGlZBghC5nXpaeiHqZbogxQx1K60RMIbaZPRERIlhFiYiiACCTzAtKiUYcXQnmRGxfp2I6kHJm/a2niEgOk5B6hrpGZAIYNcEqdCL2hSLiwDEdHWFOkIPTInk/J3PVeTgnfjuB54WVELI+ypUdynkRujzaaR8GWs5cBuzpo7j8fV/Bb/71l4Jsi5DNSuhy5qIMN2H1QrZOxBBjhrp33l6LiKmciEJ3Ikr/fdC3MUaB1US9HnPROiJDLIDpZd/lQERE/XM8pEVCsrFsRDlzJgQmdU/E1LrGZoQi4sAJ3TtwdVbi8a+5Hk970w3e2/JhaW7wAlM0TkQOZoSQ9VFDRShRSl0yQjkbffYBGFawSugexdOixNtuuBn/8zO34K57V723R8hmxXQi+m9PH4NSCFKKtifiLMi9rhJYj6jLmVM4EUspDSdiFiBYBZpjL1Q/wr6UWjlzOCdiK47OpsMoZw59rpLhY7vLvZ2I9WdQaMEqNO/0Z5R6B8jGYqQYBzhBvnfvKm69+yBuvfsgylIiqxPeYjPUnllqYBuSMEoI2TjUmBGqn4saT1P2ASuH6kQM3BNRv/aFSucmZDNiLDwEDlZZinJmUaAIsB9qzEjrRDQddrksvOYTei9CoApXSfGeSa2ceSLCpDNnaN+fciAiou4iYzkzORzsz4lv6XHbExEYM1jFGToRB85GlnikSD9TGA1WB7R60DoRh3NMhJCNQ43rofq5NNtLOb4PfJEICHPdCu1sJGSzYp8L0vN+Vz+d0oqIWopx4S/4qZdF9UQ8mKQnoin65Z7J03oqMlD1j0xWztw4EcM4R/WeiJilExFf8r//Aef+/sdw36r/Z3Co7UrIxmGfSr7nlvrY5ZnAWDkRZ/ws9oUi4sAJPckwnA/L4lQZ0ORJHQpt1YSQ9ZBStuXHgSa6avKdcgwa7vgetidiMdDFNEL6Yg8TvsOGsWCe8NzSe/2Jcuq9PTud+VAiJ6Iu+o1E6TXOF6UpSk5EGAGvL7rDcmUDeiLKhCLiB/7hdnztjnvw9Tvu8d6WEawyoOs72TjmnYhhypmrdObKiZjSGLVZoYg4cEKnM+vnccobqyLwcS0L6v1iT0RCyHqUGzAeN+XMS5LOPKRyp9AOy2VxSxGSGtt56Ht+6dtL2SpAdyIihIgolYiYzolYSolMtK9v7hkaU/UibLc3wTTJeKj3RAzVl3FZRER1TT449RedjWCVAV3fycZh3wf6zpHVqSkEMB4xWMUViogDRz/PQvRE1Af8tE7E9ushCW5NMuqAhFFCyMZgumXCjINqkymdbSFT+JaJ4OXMAw0YI6Qvthbhu/hgC/S+5dGu6CJiFkBELEtTRDwUQBTqvQ9zPQwLbydivhQ9ESXyWsxcEdMgcy79dUIxTebcU+fTfQE+LwxWIX2xxwff4D/diTjOKilsSPeasaCIOHBCOxGXpdl06NTpZWEZJvCEkM3BRozHZVPOvCTtKgbkVAgt+hVLcj0mJDXzTpUwk0ygEihTtVUQWrAGSv8U47JxIlblzAcTmAGkVc6c+4qIlig5wSxJpZRezhzKiZhr5ewTTHEokXmjCOhE1C9VQ6o0IBuHfSqFcppnAhiP6nJmzrt7QxFx4Ojjc4jBWp8EpbqYAVbq9JBERDoRCSGHSWhnm77NZVkkGtT4HthBvxHvPyGbkTkR0fN+d875kuj8Eto4IQKIiOowlBNxdVZGd7fZ5ccj+PVElCWM7Y0xCxY01gfdYVn1RAyRzmz2egzhBHRBnU4h/r7+ugzp+k42DtsJ7nt/qp5e9URkObMrTiLiv/7rv+I//af/hGOPPRbbt2/H6aefjs985jPN76WUeMlLXoITTjgB27dvx9lnn41/+qd/Mrbx3e9+F8961rOwa9cuHHXUUbjoootw4MABv6Mhc4TuHbgszgej3G1AFyH1+g6pRJsQsjEYQVeh0pnrzaQNVmm/HtIkI3hlgB7cyWsG2cLMBasESu9UpGq6rwerhCxn3l6LiED8no+6Yw/wT2cupFnOvCJS9URsHZaTUOnMhsNyintX/YVkF9Tc5L7VEIFg7dd0IpLDwb4PDOU0FwIY58qJyHuovvQWEb/3ve/hrLPOwng8xgc+8AF89atfxWtf+1ocffTRzf959atfjT/8wz/EH//xH+Omm27CEUccgXPPPRcHDx5s/s+znvUsfOUrX8G1116Lq6++Gh/72Mfwq7/6q2GOijSYKZcBnA/aJlL1RLRvDoc4yaSrhBCyHhvRO1Ct+KYUpfQxfUiTjNBpyuai3nBeJ0L6EjpYxR53ponud3URScgAIqIVrAKEKVHttQ9lWCdiUUpkwu6JGH88lFawSoi5SW6JiLHfK0XInoj6vcWApm9kA7E/J77nt94TMa/TmYekJcRi1PcJv/d7v4eTTjoJV111VfOzU045pflaSok/+IM/wKWXXoqf+ZmfAQD8+Z//OXbv3o33vve9+Pmf/3n84z/+Iz74wQ/i05/+NB71qEcBAN7whjfgSU96El7zmtfgxBNP9D0uUhN6krkMPRFDR70vEyxnJoQcLvqCSqjxuE1nlpBSQggRZLt9GGqZrn7pCuNEHObrREhf5npmeQerLEk5s+ZEzGVRCXCZ+5isxvfJKEOeCRSljN6aqLScgyNP1569vUmiYBW9THtFhE9nXsEsiBPQBaX7hRAxTXMLr1tkfeYXifzOA7U5XUQc0oJ1LHo7Ed/3vvfhUY96FH72Z38Wxx9/PM444wz86Z/+afP7b33rW7j99ttx9tlnNz878sgj8ehHPxo33ngjAODGG2/EUUcd1QiIAHD22WcjyzLcdNNNnX/30KFD2L9/v/GPrI9+4gXpibgE6czzDVaHY0Fuy5k5mBFC1sYujw2RIBq6BYYLoXv5LgvBKwP0Rb0BXQcJ6ctGBqsA6RbN9XTmEWbBxNFcCGwbVVPA6E5Eu5xZBHAizvVEjH/dKMvqWIAwTkRpi6NimqwnovrchQ5W4VyHHA72uOd7fjdOxEwgF3QiutJbRPzmN7+JP/qjP8Kpp56KD33oQ3jOc56D5z3veXj7298OALj99tsBALt37zaet3v37uZ3t99+O44//njj96PRCMccc0zzf2xe+cpX4sgjj2z+nXTSSX13fUtilE+F6Im4AT24+jLnRBzQia/mgezNQAhZD/vGKoRbxnDLJXLfDNWpELr82AgYoxORbGFCp3faT091v6uLbWPPFGOgPS4hBLaNq5Lmg9P4TkSznNnvuPRAE6ASEVO8X3awiu/cpCglcujpzOmCVZpy5tWwwSohFj7J8LFPJd9zSz09E2ic3aXk57EvvUXEsizxIz/yI3jFK16BM844A7/6q7+KX/mVX8Ef//Efb8T+Nbz4xS/G3Xff3fy75ZZbNvTvDYXQDeqXw4k4/J6IQzomQsjGYJvPQvQxXI6FovbrIS0SyeDX4/ZrBquQrYw9+fM9Heze2+mciK1oMw5QpqvGnTwTWKmdiIdmcYUp22GXo/Qav8oSSxGsImWbEj3BzDvcxw6MmWAaRMTri5QycDqz9jVFG3IY2OeS7/2O0RNRa9kzoNvNKPQWEU844QScdtppxs8e8pCHYN++fQCAPXv2AADuuOMO4//ccccdze/27NmDO++80/j9bDbDd7/73eb/2KysrGDXrl3GP7I++o1VmJ6I7depesTYk68hTTLVsQ3pmAghG8N88/+wC0WzVCLiQINVjB6GgRf1GKxCtjJzi8veZb/m9yHGVie04xiJEE5EJSICK7UTMX5PREBYzkGvcmZplzMXacqZNdFvLAqUhV+Sclna6cyzJMEq+lsTRkQsta+9N0e2AKF71MpGRITRY5YGnn70FhHPOussfO1rXzN+9vWvfx0nn3wygCpkZc+ePbjuuuua3+/fvx833XQT9u7dCwDYu3cv7rrrLnz2s59t/s/f/d3foSxLPPrRj3Y6ENJN6LIw06WSylZvfj+kk14dCl0lhJD1sMe+EH3xQgtdTvsw0GCVwrgeh3WN8ppBtjL2xz90T8R05cy6E7EIUiILVOXMKlAg9hhrB6FUTkTfnoimYy9NObMp+mXlqtf2Op2ICURE/VwK0xOx3R7LR8nhEHrer4YHfRys/g4/j33onc78ghe8AD/2Yz+GV7ziFXjGM56BT33qU/iTP/kT/Mmf/AmA6g15/vOfj5e//OU49dRTccopp+B3fud3cOKJJ+IpT3kKgMq5+MQnPrEpg55Op3juc5+Ln//5n2cyc2CMnkmBg1VSrczaF50hTZ7U6zukiTMhZGPYiOb/+r3ZMoRnDemmLrSTX7++04lItjL2OOE7bsz13k4lItrBKoF6geVaGV/sMbYsJXJh9kT0Kf21RclkwSqWI1L4iohFabxOEzHDXQnKmfXPR5CeiHpbjwFd38nGMbdg7jkel5oTUS9nHpIpKQa9RcQf/dEfxXve8x68+MUvxhVXXIFTTjkFf/AHf4BnPetZzf/5jd/4DXz/+9/Hr/7qr+Kuu+7Cj//4j+ODH/wgtm3b1vyfv/iLv8Bzn/tcPOEJT0CWZbjgggvwh3/4h2GOijQYTfJDlE9p5+2hRDdV9kk+pJNeHcuQjokQsjHMNZsOIUwtQTrzUINVZODjWoYQHEKWAVuLCB2skkqkF1awineggNYTUc2dY4uIUppzhxBOxNwq+03TE9FyWBaHvLZXWtVeqZyIhogYvJyZ1y2yPrZ5KETyOVD1RMy0mlyK2v3oLSICwE/91E/hp37qpxb+XgiBK664AldcccXC/3PMMcfgne98p8ufJz0wJ2MByqcMJ2J6lwowrMmTer+mvLASQtbBvpEKUcKl36ylChMILbYBwDe+cwBf/vbd+JlHngihrTzHxCw/Di34DseRT0hfQgfubYTL2wU7WMU3hb3QJs+qjC+2kCNLs1dgiHRmXWydiBnuTTAezpUzF1Ov7RXW81cwSxKsYpYzh2jD0X5NzYYcDqHn/W06sxWswrl3L5xERLJ5MCYZQYJV9J6ITGcOTdk4ETkhJISszXzJXegS2fRu81Arw7/9ni/jk9/8Lk46ZgfOPPnoINvsi5GmHOC1ZTkzIRXBy5k3YIHGBV2UGqEImEqKRkSM7kS0xqqR8HNYVunMWtkvprg7Qbslu5w5l37lzHYwSzonYvt1iJ6IQ600IBvHfKBqmHFQaONg198ha9M7WIVsLvTzLMSNgn5jlc6JaA8mwznp1YR5SO5KQsjGsBFuGcMtl2gcMsW2MPtw172Vq+P/HvArMfMh9ORJGouEXHgiW5c5p0rwcuYl6InoKbYBZjlzJpQT0WuTvZGyy4novhOFlMiF1RMxkRPRKGf27IlYlnY58yyNiKh95kL8ff2azvJRcjiEnvc3vWEzASHa1g78PPaDTsSBY0wyQvRE1DaRzolofj+klYM2nXk4x0QI2RhCN5sGzPKiVBNn/YYxlEtGvVaHEi1+AeGTr0OXRxOyWbF7ZvmWpS1NObPdEzFUOXOWrpzZjtLOUXoJmUVplTMn7ImYGSKi34JVYTsRxRQHBxGsol3fed0ih0HoqhspJXbiXvzi3X8NfHuEXAjMpLSHJrIOFBEHjj4+h7hR0LeRTEQMbGteJtpyZl5YCSFrY+trQRJ/jZ6IacYhGfi6BbTHdSiBk0MRvJx5CfpXErIMhF5ctkXJaYLyWADI7J6I3k7EertCQFXx2ce60UjLYefrROxKZ15NVs6sOUc9y5nlzHydxomciPp1JnSwSuxSerI5mXea+5cz/8fsCzjvwLuBj92LLLsQKCWdiD1hOfPA2dCeiEtSzjwkwa0pZx6QMEoI2Rjm+sQEEJLKJRCmNqJnktpkUidi4MqAjRBbCdmMzN0Xek4G7aFvOXoi+pfpqtcpF1o5c3QR0XYi+vZEtNKZxTRNOXMpkQutJ2LpGayyNOXM7dcheiLqp1Ip44vYZPMRvJy5BLaJWuQ/dKAJV6Ezth8UEQdO6JTLZZhgzjdYHcZJL6VsJoXsiUgIWQ978hdioqtPGFItZujHFWqCq64bISZBroQWRxmsQkiFfTr5Dl3LUs6s90Qce6YYA+2YkWVIl84sbYed33EVlgMwXTmzeVy+wSrSEiEnmCZJZzbnfdL7tZ0PQfLaHNkCzFUgBmjr0Cw8zA6ma+2wyaGIOHCMcuYAkzGjnDmZE9H8fignvVHqNpBjImSoSCnxD/96d5Kben0fdEK7zZeinDnQPixFT8TATkSz0oDudbJ1scdC3/vd0D24XBFory8jT8ce0N5nZkIkS2eG5bDLUXq9vmUJIxV5jCLNtctSrseeTkRZWE5Ekaic2frM+S7E2e81S5rJemxEuwpdRMwYrOIERcSBEz4Nsv061QRzWW7uQqO/P0MRRgkZKp/453/DT73h73HF1V9Ntg+2bhQknXkJ3Ob6qnOomzp13UgpIuovZwjRL3RwGiGbFXuY8C1Ls7eXrJxZdyIKfydiU86cMp3Z7okoCq9x3nAVoXLsJXEi2sclV71KdUsrWGUF0yTmDfsQDk7DOhE53yHrEdoZXkqtVYTmRGQ5cz8oIg4cI70xxARzKZyIG3MB2n9wmrQ3xzKUihNCDo9/veteAMC3v3dvsn2YS2cOsDqrD4GpFmjMQLAw22ydiMvhHA2Tztx+zWsG2cqEvi+cG1uXoCfiOECZblPOnNCJKKV5DLlnOXNZyrnXKcW1y+71uCKmfmXapZXOjGkS154t8Ho7Ea3XiU5Esh62uBdiMaV1Ih5qy5n5WewFRcSBE7rxuhGsksylYn4fom/XN75zAGe+7Fr89nv/wXtbrmxEmAAhZGNQIlBK8WY+QdR/dVYn2RhvjIVh9qFxInq6KELsAxC+J+JQHPmEuBC88f6y9EQ0glUCOBFL3Ylo/iwac+nMpdf7VZTSKGeeiFmSa5ftRJxg5nVc0jqGSYB0bhfsc8G3pNp+azjfIeuhPiKTvJKtQjgRzXJm9kR0gSLiwAndg2mo6cz/dMc9mBYSX711v/e2XDEmhBzICFlq1MQrpXhjr5r6LqjMpz2nb1kR6qZuGZyIpnMwRHsRzb2eKASHkGVgrpzZ01FiPz1V+545h10gcTQTWJp05hEKFB6igOEqQi3eJRARhRWssgI/J2LZ6UR03pzHflgiomcfaHt7nO6Q9VBj1GRUyVYheiI2Y+tUL2f22uyWgyLiwNFvpEJYxpehfCr0ijPQHlfacub2azbJJ2S5UTcxviXEPsw7B8O6b1KlM5vXrTDbbETEhE5EGdhhaQarcCZGti5qzBjnYRwl9liYatFc74kYwolYNCKiSNYHTFriWO4ZGFPOpTNPk4i+thNxBVO/45pVr9OsnqrnQgJWn8QY2CKzrxPRfk3Yh46sR2mJiL733WVpB6uwnNkFiogDR5+nDMWJaN9EhSzTTjmA6BfSUvLCSsgyo+YoviXEPthjhO/iw7JMnM2k+lDlzNXjoNKZDfc6F57I1kWdCqMsq78PI7YplqWc2duJWG8uzwSyLFEJn+xwInqVM8NwIoboHemC3etx4tkTUYmSB7Gt+VlWrjpvzxX70uIrIs5Vk1G4IeugFmBVObP/IpE2tpZTjEQRZLtbDYqIA0cfrGUAYWoZwj/sQwgpjqach9kX0lAX1ntX469cEjJ0mnLmhING6Ob/GzG2uu2HtqAyoGAV3RwT4mZVv0SkKrckZBkI70Q0v08jSlllumLm7WAudSdi477x2mRv7HLmHKV3OnMmtJ6IiURE+2JVORHd90M5Ng+JleZnI5lARLSDVTzLme1zk8EqZD3U6ayXM3sln1tj67ZaRORnsR8UEQdO8GbT2vNTOTrsgSNkg/qUA8jcexXgzu5vv3QbHnbZh/D/fvbb3tsihLSoSU9K8Wa++b+n+8YWJZON8e3X4ZyI1UYPLkmwSojx3QxWoRORbF3UqTUO5FSx7zNTtAsw3DKonYi+Y7wSETM0wSrR2/hIc2G76ono0zvQSmcWszTXZWn3MPQLQpFFJWxMMYZE9WZl5dR9/xwJH6xilzN7bY5sAexyZsBPzyilRC40ETGrzitWAPaDIuKAkVLOrab6l3i0X6dyIs41/w/RW6reZspFiI1Inf7yv96NUgJf+vZd3tsihLQ0PRETijehE0Tn0p4T3VDpY3wpw0xym3TmhE5E/TiCXLcCl0cTsllR5/eodiL63uvak8kUab+2W2bkm/YrZXOPm4t05cz2eD4S/j0Rc6snYopFFdthWe2HjzhaiZKlyCDzCYA0TkT78+G7EDfXkoruL7IOdjkz4LewYy/QbEclIvKz2A+KiAOm61wI6URMcVMFzJeZ+KxgttustpFyAJkrZw5S7lYLHZxgEhKUZRAR7T/tuy/z6czpg1Wq7/232ZYzL0f5ecjxHWCwCtnaNCJippyIvtszv08xzttumbF3AEn7tVnOHHnssAJI/HsiSmQwy5l9Q8acsHoi+qYzq56IEhlkXpU05yl6IjJYhSTGLmcG/BZi7YWH7UI5EZ03uSWhiDhgulZifQU3oyfiLH2/LCBsOnPScuY5h2W4cjeWuhESlvbcWp5yZt99WYaJMzC/AObr2tNd+SnTmfXXN0S5nf72MFiFbGXUudX2RAwbMpUk7ddyy4wxQ+ExJuuCVpalS2e2RcQcZeB05lma8dAuZxZ+6cyyTmIuRN44EXOZopzZ/P5g4GAV9qEj61EGdiJKCeTawsOKoBPRBYqIA6br2uV7YdVPsHROxGof8oClGOq4kpYzb0APnmXo20bIEFHna6pxUN8Hhe++LMPEuWs/fOeD+iXi4JKUM4e8bgEc48nWRp1bo2DpndXzVd/AFAsqRWmXM/uX/SrMdGb3fXSiI53ZR0jqTGdO4Ti33puQTkTUIuI4gYhoH8N9gYNVmIhL1qMJzhqJ5mdeY6HVR3WbYE9EFygiDphOJ6J3s+n261RN9+0V5yDpzEsQrLIRvR7VcaUsuSRkiCyDE3G+/NhvX0KnPbsy7zYPV6ad0oloBKEEGN9D91gkZLOiTq1RFiZxWG1vZZQDAFYT3O/aJXdj4V/2q8i1cubo972yw4no8YYV0ixnHokSRZlgscg6Ll9HZFkHq5Qi18qZBxisQvcXWQc9VV6N8X7lzLDSmWsnIkXEXlBEHDCdIqJvsIo+GUs1wSxNW3OIk77piZhwAJnr9RjQqcJ+WYSEpXX5Lkd5LOC/LxvhhnbBPgx/J6J23UroRNTfr5DtKgCO8WRr0zhV6vtCX0eJ2t7KuNpemp6IHenMgZyIQrQuy9gijrSciGPMvMrPbVcRAKBYjZ86XdrpzH5ORCVKSmTAqC5nRgIR0Xppw4uIXpsjWwD1GcyEaMKz/IJVLCciql6jLGfuB0XEAaMPzPWCY1CnyrQo41+kMR/1Pph05g0oJVT3vXSpEBIWNUmdlTLJOKjvg8J3zLCHiWmiccN+PYMufiUMVtHH+CDtKqzrMSFbFTUWNj0RA6Uzr4yUiBh/jJcd6cw+opQ+nJvlzLHFtvA9EXNLRBxLv9fKBfu6teLZE7Es2nRm1E7E0RI4EQ+ynJlEpmiciMA4U3N/j1YBloi4ApYzu0ARccDoA38o155+kZQyjJuiL+oYNsKJuEzlzEGOqyln5sBISEhmhoCTSEScW3gYZk/EkL18l0VEDJPO3H6d4lpMyLKgzoVwPRGrR1XOvAxOxAkKr5A8fRzME6YzC7snYoAybduJOIGfgOeCmCtnDuREFHnTE3GE+OnM9ufD24loLxLy2kXWQWkPeSaQN05Ev7Ew7xAR6UTsB0XEAaMr6qrEw/eiap9gqW6sAGA8CnNMwHKkM29IT8QlKLkkZIjo42sqp689HocU24B0qe72kO5dzlya4l2yXo9GmnK4dhUAy5nJ1qYtZw6TONyUM49SljPPOxH9y5klXjt+E8Q1/z1ZOrPscCJ6OSzlvIg49nRtuu2IuQ8rmPolyConInJgVDkRJ3IWvfLB/nz4pjPb7wt1G7Ie6iMjhMAogBPRXqBZYU9EJygiDhj9XGhKPAKtzipSNJuWzc1iVn8f7oYxpdZmX0hDpjOnnmDeetd9+OItdyXdB0JCYiTjzpajnHnVcz/syclQglXsy0MqN6Ih+gVswwFwoYhsbdpgldqJ6FvOXD9921g5EdO07rHTmf3KmSWOxX5ckP89xCffhFxUYlCqdOaynoL6HpedzgwAE+EnuLogpN0T0U/IbNKZRdsT0dvd6ID950L3RKT7i6yH+szkerCKZ0/EXAtjUj0RGfLTD4qIA0Y/GYKVeNiT1gQTF3XBUeXMQACHZdMTMaETcW7iHLCcOXFPxF9+26fx1Dd9AnfsP5h0PwgJhT70pTq/7CHCP8XY/D5ZObPdeN3z5bWve4c8J0Gu6NeXIsBra6Yz8+aXbF3U/e4okLtuvidiigVz0y0z9gxWKaTECO3YN5GHACSYONcDepGNAYQJjMmE+fwxZvEdlp09ET0+N6Vezlw7EcUsuuhmXz/vm/reZ7CcmfSjKWcWZRus4nFuSVltSzFR5cxci+0FRcQBoy6gVSPSME5E+/kpnIh2OTMQ7riWqZw5TDpz9ZjaiXjnPYdQSuA79xxKuh+EhEKfoKRygdljRPieiMshjvq7iiwRMZETMXw6s7Y93v2SLUzbE1FNMAOVMydNZ7aciJ69A0sJQ0TcVh5s/k5U6l5/hVCJw/4OS7uceSVBT0RYPRFXPF2DyolYigxClTNj6r2o1ns/Ager2J83ur/IepQSODv7LF77jSfjceVNADzLmUs7WGW1+Tk5fCgiDhh1LmRCa0QauCwsSYmHWiE2nIhhJs8pxw/7Qh1iUtgGq6SdYKrXN4VzlZCNYBn60ZWWK9t3PJ7ry7okwSqFt8MybE+nEPsRpJyZTkRCAACyLk1rqm4ClTOrYJVUC+a6w26Mmde4UZYSmeG+qRZ1YzvBVABJ60QsvY7LDkkA0vREtANjfMNdpApWQa6JiAmciNZ9hm85s/2aULgh61GUEv9P9n+wTd6HM+RXAfiWM5stECaSwSouUEQcMGoilukpbIFWZxVpbqyqfRiPRPOzUMeV8mI2H6wSwqmyHCJisx8Jk1EJCYl+vqYSx0M3/59zIiYq07bv43xf3rly5kTjUOjyY7kEQjYhy0BToRKonFnOja1pFsxzo5zZTxgrrO1tK9OUM0vVE7EWESsnovv2upyIE0/B1W1HKnFtmm0D4O9EhNETsXUipuqJuGOlEtR9RUT73KRwQ9ZDd2VPUPUe9VpQmUtnTrOgstmhiDhg1I2BEGhS2EKLiCnEKXUMqoE2EKInYvWYtJx5zn0Topy52kZql4q6aUi9H4SEwnCVJRJw1LilSu5892PeaT6UYJXlEBH11zdEIJgRrJK47y0hKWnTmVX/b7/tqXMrdU9EO1jFR8y0HXsTWZUzR5841yKiKmceo/BymxvHJar3ayxm0ct+m+PKtwOohUyfz02dziyz1om4IuL3elR/74jJCIB/OfO8E9Frc2QLoCewT4QSEX1cvubCg+qJyNL6flBEHDBqYM6zNhI9dE/EFJMxdY5XxxVWHE1bzmx+H+KmVa2mp3apqNeV5cxkKBjpzMmdiCpBNGzZbzpxNOwkY1mCVUK7zfXPoJRcRSdbEyml1hOxutf1nQza5cyzUkYP3iutia5virG0RcSmJ6L7PjrRBKtUImImJIrCfUwuSiBTSavjHQBUKXHs5oHVMczyyomYCYmyWPXeXlXOrKUzJwpWOaJ2Ih6chU1npnBD1kMvPx7XfV1DljOPZXWe8h6qHxQRB4xRzpyFbTatSNVsGqgCY0Idlxo4UtrqNyRYZVnKmSXLmcmwWIZglaY/bKDm//YkOV2Ztvl96GCVg8mciGEdlhux8ETIZkM/D8Z52IVlNbYC8Uua7ZK7MQovZ1tR2n3AahExUQCJ6okIALKcuW9OF1tHlYA3SdgTsahFRADALICImGUQuRIREzgRpRIRKyfitJBe15q5nscUEck66GPhSpOk7Ode7nIiUkTsB0XEAbMh5czWOZuiJ2KhiaONEzFQoEDslWZjH+YmmCHKmcNty4fGEckBmgwEo5Q0lWNvzokY1mmeyoloj8P+wSrm96mciPblJdTiV6jtEbIZ0UWJpurG815OjUHbxnnzs9give2WyYT0EtsKq3egciLGFnFUsEoZSEQ0ej1qImL08bC+Ts00EVHODjpvTtbbk8jNnogJHLFAW84M+IWTqfdFBbUwWIWsh973dFz3RPS5351zZcs0/WE3OxQRB4yRzhzKsbcUTsTq0TyuAaYzByjFaNKZEzsAl8URSUgo9JuNEEnqbvtQPaq+Xb77YY9/6Y7LFhH9trcMbTiAjr63wUVfjq9k66GfBsqJ6N1v1ApWAeLfv9h9uwBAzqbO2yulxAit+DMu0/ZELOty5monPEREKbVy5qofYZp0ZhWEMkaBSnyWU3cRUQWrIMsAzYmYKlhl2zhHndHpFa6izs3mXKVuQ9bBKGcWtZPZ44NTlnY5s3IieuzkFoQi4oBRE7GN6B2oSJLOXGrHlYft9Zg0WMV6KUO4gBrxLnHnYvX2pHJsERIafUFmmuguuClnrie6q97BKnY5c6LjssuZA1+30gWrWItw3uXM9qIex1ey9TCciHmo1j319rIM9e1z9PYOthOx+qGfiNjdEzGy2FYqJ+JE+5m7iGikM48rF+BYxBfbVOo0hMC0Do1BiJ6IQnMiimn0IJKimXMB22tn7sFVv1JSABiPwszfyPAppUQulBOxGgN9S+oNV3bdE5FOxH5QRBwwXb0D/cuZ0/fM6irTDtXrUcp0Jc0b0hNxCYJVlqF3HCGhMcqZEzvbVsaq+X8YR7YqM4remN7aD0V4EXE5ypm9F782wL1OyGZDPw1UObOvE1Fq988q8TlFT8Q5J6KPY88qZ07mRKxdg2U2gkSt0PqUM+vi6EhLRo58XJkm+s1Uqfb0kPsG69dEihzI05czZ0I0IqKPE1E58MeBQpDI8OkqZ/ZyIloLNCMGqzhBEXHAlM2imNBWZz17S1nnVxInolbOHMxhqR1GqjFkzlUSYEfUjXSKZMFmH5YgxZaQ0BjlzInFNuVE9BUz1TgaanuuhG68bl8fDk4Tib6Be05uhHudkM2GPl40wSq+YUz1uZVlohURI4+HtnMQAETpLkpVE2fttSoPNT+PiirTFTlkVvXZ8+uJCIyE6UScYBZfnKqdiFJkmImVeud8RETdiZiwnLn+e1kmmh6hXiKitVhJ4YasRynRtGJQpcc+c2SpORurbVJEdIEi4oBpypmFQCY2ppw5RfmUXs68Eb0eU62KzffLCtAT0RA60h8Xy+3IUNDH0mRlv/U+qBt734UHO5E0XZm2+b1vsMqyOBGDOywZrEKIJSIGanGjua+UMBm/J6ImjqmfeTgRq5JAvSfifdXPE6UYQ4hKIAO8nIhSvz6MdwCoHHvRF1Wa48pQiNqJOPMQEZvt6U7E+OKoehlzIbCtvje4bzVAsMqITkRyeOhpyiPlRAxYzjxmsIoTFBEHjF7OHM6xZ5UzJ5iM6eXM7XF5TjK140omIm7AhFC/h0rlApRLsA+EhEYfclIFWqg/2zgHfYNVmu3V5dGDCVYxvz+UyIk4n84cVhxlsArZiui3SqNAJZL6/bMSJqP3RCzn769F4d4T0UgxBjAq0qQzA8qxlwO1E9FPRNSeW6czpwxWQZZjVvd7FD5OxKY8OkvqRNRL+7dP6p6IHnM/Nd9S8zcKN2Q9Kld2/bmpRUSfObJdzjwu6UR0gSLigFHnghACeRZmdXYZnIjqhifX05l9y8J0J2Kiedj8BDNcOTOQzgW4DL3jCAmN6bBdjnJmKf3GeDuRtPTcnivBewcuSzpz4IUie/JPpzfZikjDiRhmwVxtsnIi1j1iY/dE7BARfcS20hIRlRMxmdgmssaJ6Besor1OdTrzRMyitxkRshVHCxUa4yEiirIVJRsnophGf7/U38syoQWr+Jczt65hzx0kg0dqot9IhhARTSdi0xORgnYvKCIOmGYlNWtXfLwnLdZgnyJYRb+5GwUSR4sldCIGCVbRtrEMrqJU5ZGEhGa2BAJ9KyLm2r64n+dqMq7KjHy358pca4fAi18HPfo5hdwPX1FivpyZszGy9TCciFkYYaK9fxbNeBi9nFkrXS5qEclLbJNAjnbsU07EdL0Dc8g6gERI9+MyVv41J2Kq44IQKNRx+aQzN43ttXRmTKMfl96H3rcnYlnKZg7HcmZyuOihUI2I6F3O3H7ulIgYu7XDZoci4oBpegcKgTzQ6mxpTTKTBKs0q2Lh05ntr2Myl7QZQJhYhp6I+v0dy5nJUFiG1HG7h6HvvjTl0eMwoqQrGx2sksqJaA/B/tct83s6EclWRB8vQpVINu4r0boboy+aaw67shERPcqZpV3OrHoiOm/SCaH1DkRWXWsyj50o5bwTcSVBT8TWYZmjyOr3y6MnohJWZdaWfY9QxA9W0aq/fEVE/VquglUo3JD10EOmRnWwipcTsbTLmavzlFPUflBEHDD66lEuwopt20ZpesQAZsPrUSBx1HQiem3KGTs9OYSrpFgCoUO/aWDPLjIUTJdvKvdy9bhiOAf9Sjzs7aU4NnsM9u15a4uQyxOsEva4Uo2v06LEB//hdvzbAY/+X4Q4ovcvzAL1/+4qZ44t0utlurIREd2dbWVpuW8S9UQ0ypkD9ETUxdaUPRH1Xo+l6ono8X5BEyWV2JqjjB+sohk31L2Ba19h/T0Zj8IkqZPhIyUaJ2KuRETPe129nDmvz1O6YvtBEXHAdAWQ+K74qAtAkwaawomoi6MbUKadalXMnv+FCVZJL3QY5cx0ypCBoH+uUyymAO1YNc4z1EOhd4lHtT2Bet0J0wQlsvaCindp4tI4ETe6nDnN+Prhr96BX3vHZ/HqD34tyd8nWxtp3BNWX4equtGDVWLf78qiS0T0TGfWJ85FmnRmvZwZqiei9FjY6eqJiFn08VB3WKqeiFmIdOasfZ1ylNHdUqVm3Mg9nb6GiEgnIjlM9FCoIE5EaToRR3IKgZLBKj2hiDhgugZ+34uqum4oETHF5Lkp085EsHRmfdK6LOXMIQazZUhG1m8QUokthIRmGZyI+kJRiARR/ZqRyn2j70ceePFLcShRT0S1H6qMK2QbDiDdGP9/awfiHfccTPL3ydZGH7cyEbZ1T5aJ5nyN3hPRKGeuHHY+ImI1EZ/viRjfiaiXM1dOxEy6l2nLrp6IIn5PxOa4sqxxImZlgGAVLcU6TyB06HOu3PP80j9rjYhI3YasQyklcqGciP49EaW1oALUyed0IvaCIuKAUdfVPNPLfj3Lp+rRXiV0peyXJUS4nohGOvOSlDOHeG3NcuZUQkf7NcuZyVBYhnRmPak+RIKoOoxMCIzV2Jpkoah6VL3IQvcOTOVEVB+ZUMc1515PPManCqwhW5vmNBJasIp3T8TqUW+bk7InIkaqPHbzOxF1sU3mKljFY+yQ8+XMk8Q9EZtyZp9gFbW9LEtazqxXfwkhjJ/1pdDek0mTzkzhhqxNNXZV50NVziyDpjMDwDas0hXbE4qIA6YV29rV2VBi27a6kX+KyZg6hHyD0pltMS8WG57OnCi50xRbOECTYWAI9InOLd0hoIQpH0HT2F6iRFJAL6sOIwjYky7Xfk6+2MFkvot6c+XRqcb4+nOTSpwlW5smbE+gKWf2nQxK2W4zlStbORELZE2KcSY9eiJKNG4eAMhnlYgY3YmoJu9aOXPmISKqFOtS5EAtSk4SBJA0q0QiQ1mnKecePRF1URKi+gxmIr4TUe9D35xfruXMeghSHiYEiQwfPQhFoFoM8bnfscuZgSqMiYJ2PygiDhi9p8soWLPp6vkrjRMxXalblmll2r69pbSLWCo7s/3ehOjnsgz9CFnOTIaIISLO0jrAhBAYBS1nbh09Kcf4RkT0HDfm05kTlzOP/F2jQJeImLasPpU4S7Y2ek/Eppw50MKDMFo7xC5nrsUxZECuymP9ypmzTieix066ULbBKsirMl1fhyUASJEBtXg3TtATMQvsRGxExGxkOBFjz1FMkd5vLtlVGk0RkayH7Rz0dRqXUiIT5vNXxCrLmXtCEXHAqHMhzwTywI69pidigsmYugiJgOLocqQzm98XASbvZt+29GECLGcmQ0EXx1O7fHOh9dnzKWc2eiKGWaBxQb20zfjuuQtzTsTk5cxheiLOLTwlSwmv/u7BROIs2dpI6E6pMH1U9YqXyShNawflRCzR9g4U0k9sG2kT8Wx2HwAZv3eg2ocsB4Tqieg+diixTYq8EVsnmCZwWLblxyoIJ/fpibggWCV2yaXeo9i356i65uVCaEnqAXaSDJrCasXgu0hgt3YAKiciy5n7QRFxwBSN2Kb3RAxzY7VtlM6lovcBC9UTUdcAkqUz2z0RAwgTS+FE1P4sy5nJUFiGnohNyZ3W99arnFlz9IQIanHfD9OJGCpYZcekmoil6t1nlzMPJViloBORJKR1ZKMVJgI5EbNMD62KXM5ci0glMoi6TNfHiWi7eQRkkhI+JY4JzYmYB0lnbh2bYzHzdrD3pilnbp2IeekeGKPEUZHpwSrxy7Sb/qBZuHTmSpCE17bI1kFKIEf7OZmg8FrU0cujFSuYei9YbzUoIg4YvTQtmNhWb3P7RDkR408a9NKVYIExy5DOvME9EVP1bTPDXTjJJMNA/yinEsfVuRUqTVkvNRrladw3QDvGhxLbbBExlRNRXWfahvKePRHtYJVU5czsiUgSoqczt+mxAbeZ+S/QuNA4EUXW9PrLPJyIRcfEeTsOxS/hq0VEKXII5bCE+3Gp10mKTHMixi9nbnsYZijr/cg9eliiSbHOEwertHPJzLMEWRcRfUujydbBXgAJ4URksIo/FBEHjO4qyT0t6IqmnHlUi4gJJpjNxFkr0w7p6FiWdOYwPRG17SUSOuQSOLYICU25BJ9r9WfzTDSlv35OxLZVxCRRmADQjvHjQI3X7cWvFGKXlHJOHPV9be3Jf6p2EerPHmI6M0mA7FgwDyXQCyGCtc3pi17OrJyIVTKpG2UpMYJ5ju7AoaTpzE2ZdumTztyKkirFeoJZdLEt08qPVTnzKESwSqYFq0BGL/9typn1nqOO+1AEFCTJ1qEorXJmMfOa01bORk2kB7AiGKzSF4qIA0ZqA3+oFZ+mnLlOZ07hRNyIwBj9JiqdE9H8PsSE0OjblrjUDWA5MxkOZr/RtAJ9JnTXnvt5XnQ4EVM4mNUYPArWy7d63DGuJqwpxC79stI6EcNct1IKvoBWzkwnIkmAvmDu27Ot3ea8MBlbbDN6IoYIVulw32wXh6IvnKt90MuZRyicX1/ZBLUIoydidGd24xzMIDP/wJim7DsznYjRg1W0kCHfdGb1Ho/yLNl5RTYfUiK4E7ERESdHAKh7IlLQ7gVFxAFTaK6SUbDegbUTsUlnTjHBrB6zgD0R9Yuy7QiMxXypW4ByZl3AS9XrcQmETEJCo3+uU6WO60Eoaoxf9UiKNlpFKJd3AmGqDSAJO74rJ+LBBGKXPhaH7okYQkD22o/6OFaL+E3/CdFFjlHjXg6zTcN9FfvesGwDQ5QT0Uds6woT2I5D8d03mmNPlTPnwl0cM4NV2nTmEOGEvfZDdzbVop8t2vbanu5EbHoixh9jixJ4wehdePo/PAejuuzcN1glZJI6GT62E3EFU6/7HaOcebyj2SZFxH5QRBwwhtgWqHegOsFWxul6Ihp9u4KlM3d/HRM5NyEMICLq/QgTJ5IC8RuTE7JRLIM4rjsH28RfDyeiJkq27rbNH6yinn/ESnXdKkoZ/T3Tb07Vcfk2/lfD6UrCoLNqP9q/SzciiY0a8vQSyVBVN3o5c3SBXM6XM49QON8blmWHExGrCcp+296BShwbewSGSL13YP06TUT8noh6ObMQKk3b3fWeNcEqIy2dOX6wSllKPDP/CE66+zPYc/CbANzPL/W8kZb0zHUnsh72AsgYM6/zoNTLmSeViLgNqyxn7glFxAHTJneGcyKqyYIqZ045wRR6YIzn5Gk5ypktETHAhFA/llQuFQarkCGyDGX6unNwHED0a0VJBEl7dqUpZw7lRKyfv70uZwbii11GOXMoJ+IGXDN89gMADs3YF5HEpSsEJWQ5cxbo/rkverBKNmpFRB8BZ2SJiDtEAieiSlnVHJY5CjjfeivHZqalMyfoiaiciCLLGidi0//RZXtGOnO1vZGHY9OVSsCp9mVcOxHDBKvU26dwQ9ahlEAu2nuLMWZe96ZSdyKqcmbBdOa+UEQcMHpyZ7DVWTtYJWFZWG40vB5AOrNVzhxC9DMFvEQTTF3IpIhIBkK5BOK4HjI1bkQ/n3Lm9poRIu3ZFfVyNk7EwMEqQHwRUR+L2zE+zHGtJC5nLgwRkWM8iUu7mIImRNB/zFDb1IIJI98bmsEqda8/MXPuU1tNxO1E0kPpnIhZDqH1RHR+fZtQlgwYVeXMadKZW0ekqMd4Hyei0JyNyokIAGURd6Gm0FxgufQUETVzS6h5KRk+c05E4efINZyIY9UTkenMfaGIOGD0m6BgAST101VPxBS9wHT3TbB0Zu35qVoi2Mmdvu+VlNJKZ07UL2sJHFuEhEaf8ERv4G7tQy4ERkGciNWjLkqmGDektaASylU0ytoy7YORw1X0cTDUGK+evzJSPYrTlzPHfl0J0Xsi1reEwRbMK8dUonJm5bBDDtE4Ed17/Rl9wGp2IH6wShMYopUz5yjd3zO9d6AqZ/Ysd3RB72EoatFPePREbAJoshzNBxtA6RHW4oKeZDup08FdbwuaYJUsaxy+1G3Iethj1wQzr/udopQYqQWVSdsTkYJ2PygiDhgjXS7QZEw9fxnSmYVoy928j0ubBKUaROxyZt8JoS2GphI69D/LcmYyBMpSGudXqs+1kc6c+5e06teMxomYYNywy5lD9bzNhGhce7Edc/ohhOo3aQer+DryXdHPBToRSWxKzd3UCH6B3MtG25yE5cwi04JVPEpJRzBF/u0ifh+wRliznIjO6cy1KClF1gSrrIgpZpHHogzqM6OXM/s4EeeDVQAARVwRUf/cqGAV1/eqDVYJ5xomw8dwDsK/XUGT6A4YwSoM+ekHRcQBoyaYegCJ702Q2mZKJ2LRsUIcqizM/jomjYgYSvC1jiNZguwSlH0SEhL73ErmANPKmUP0MCy7nI1JFoqqx3FgJ2KetaFgsXv36ZOu8ShsZUDyYBW9nHnKMZ7Epav02P+eUNtmYieiHqwy9ij7LeV8WvB2HIx+XE0AiciadGafcmZ9e8qJCACinHrtZ18MJ2J9XJlHT8TWiThKWs6su8BGqJ2Irp9Bbf6WBaqQI8OntNKZJ76uQaNJdVXOvE2wnLkvFBEHjJpLCr2nSyBhavs4XfmU7Lhh9C9d0b5OVs5sukpChQkokjXdZzkzGRj2uZVKHNcnuiF6GOplgeNmgSZ9OnOosTDPNCdiZLHLKGfOq+tnqDYcK+PEPRH1cmYGq5DI6L1cQwkTxjZT9USsRSmpiWNjzJxb7th9xYAqnTn2cQnl2MvaYJWRcHciNuXMWjozABSRy34bh6XhRPQQETuCVQCg9HA3ulDK1ok4ln5OxKYFS5ahPlXp/iLrUgnZ2kKsZ7sCwyGsglXoROyNk4h4+eWXQwhh/Hvwgx/c/P7gwYO4+OKLceyxx2Lnzp244IILcMcddxjb2LdvH84//3zs2LEDxx9/PF70ohdhNos74A8do5w5VDpzfT1Uk5ailNFXkXT3TR5ooqsfg0zlRLRFxEClbopkPRG1P0snIhkCS3NuaQ67cQAnonpqnqUNVlETFHVMvqvDesiYunalKmcWAsH6Tc6FcS1DOjOdiCQy+rk1CuQabO8zoSU+e22yP40TsRXHfNOZbRExRTpzkzps90R0vPeW6ibTCiCRkct+DXG0Fv0y+AerCOu4ENuJWBTIRd1ipHYiulZszcr2nkWZQFLNucjmwR67JsJ9MQVAu/AAGOXMdCL2Y7T+f+nmoQ99KD784Q+3Gxq1m3rBC16Av/3bv8W73vUuHHnkkXjuc5+Lpz3tafjEJz4BACiKAueffz727NmDG264Abfddhv+83/+zxiPx3jFK17hcThER19JHQWajNnlzEA1ac21VbKNRhdHQwXGLEdPxOpxJXDTfUWK3maA7UTkBJNsfuadiInLmTUnoo8wpfdYDFEe7cpcOXOg/maVE7G6VqUKVtHLI0O14QjVR9cVM505zOv6lVvvxglHbscxR0yCbI8Ml3IDXINd52v0nqOl5kSseyL6pJJ2iYjbU6Qz1/sgtV5/PuKo0J2IWu9AGdmJmGn7ITKVzhwqWEVzIpaR3d7aMah0ZtdLTVvOnLGcmRw2ergP4NfWAaATMRTO5cyj0Qh79uxp/v27f/fvAAB333033vKWt+B1r3sdHv/4x+PMM8/EVVddhRtuuAGf/OQnAQDXXHMNvvrVr+Id73gHHvnIR+K8887Dy172Mlx55ZVYXV0Nc2SkLXUL6dhTIuKovaClcnTkQiBvHB1hysL07cdG7cMkUAmf/Vanckvpg3IpecNANj/2ubUM5cyjetKy6plYB6h05nTuNrucOdSCStpglfCLX2Wz8KTKo9OP8QcDOBH/5d++j/P/8O/xnHd81ntbZPh0Vd34ngpG25xUTkTZBqu0TkR3B47Uy5nrAJLtSdOZW3GsClZx3KDeEzFL50RsRb+216OPE7EpZ85HgBAo6ym7LOL2epSa83FUpzP7BqvkAq3gT28BWQc7nXmMmZcpyhARx9sBVD0R+Vnsh7OI+E//9E848cQT8cAHPhDPetazsG/fPgDAZz/7WUynU5x99tnN/33wgx+M+9///rjxxhsBADfeeCNOP/107N69u/k/5557Lvbv34+vfOUrnX/v0KFD2L9/v/GPrI0+cQo3aTGdD0D8CbS+QrwRTsRU1nrbVRKq1E2RyqViD/R0I5LNzvy5lapVQOuwU2EdPuNG069IiCDl0a6ol3ccKp1ZcyJua8qZYzsRq8dMC60J1euxvWakH+NDvK633X0QAHDr3fd5b4sMn07BL5gTMV2KrCrTlcgbJ6JPAEmhT8RXdgJIk86si20IEKzSuP2yrBISa2Rkx167H205s48TUfVYFHUpc1kfW/zjasXYkXIiOn5m9HuW+jLIcmayLoWUGIv2cz/B1G881s/LSTUWspy5P04i4qMf/Wi87W1vwwc/+EH80R/9Eb71rW/hP/yH/4B77rkHt99+OyaTCY466ijjObt378btt98OALj99tsNAVH9Xv2ui1e+8pU48sgjm38nnXSSy65vKfSbILXiEyqxbpSJxjG3msjRIURliQfChpCkGkPmeiIGTJwG0rulUu8HIaGwHV+pxJuiY0HFZ9yQHUJXkp6I9Y6MQqUzG8EqdTpz7GAVbVGvqQwI1Pe2TWdO5URsvw7h8FTv93TGG3qyPnogVOtuCrPwoFfyxO8dWAk2VbBKJbZNMHOePBclmoAMrNwPALADhwDETp5WF5pWHM1Rur++jRMxrxx7SnSL3hNRT51WPRF9glWq52b1ey+RRkTU+8f5pjPrC3qpAovI5kNa99xjzLw+N8Y51PREjB8ytdlx6ol43nnnNV8//OEPx6Mf/WicfPLJ+F//639h+/btwXZO58UvfjEuueSS5vv9+/dTSFwHc0IYttl01XhfYLWIP3HR9yG0wxJId0Gzy5lDHhOQUOhYkv0gJBR22dVq4mAV3S3jM27o5cwZ0jkR1RgcqpxZdwEqwS12inBXOXOohaK2nDm9EzFEr0n1mUtVnk02F02wCtpyZqD6XGba9722qUxlIp2IqCa6VTlz1Ru0Kvt12w8pJTKhnIiViLitFhErl6Lba9WXrCNYZYTCWRxteyLWIhsyAEXTUzIWuSwBgaqUWYmIHk7EHKqcWTkR80p/jS0i6uXMpV85sz5/S3Vekc2HsBLJJ2Lm1bJCjUESAmK8DQCdiC44lzPrHHXUUfjhH/5h/PM//zP27NmD1dVV3HXXXcb/ueOOO7Bnzx4AwJ49e+bSmtX36v/YrKysYNeuXcY/sja6SyWUY093ASrHXGwnYlfpiu9kQ39ZYpes2PswCeQqmSu5TDQhs0sV6EQkmx373Eot3uSZaJuUe4xfutA1DjS29kVK2YzxkzyMU6GduAAr4zRORF2gDeWwtMuZkzkRjXLmgE5ELjiRw6AZt7TEV8Bv3NBDppIFQKhyZhGonFkPVlmp5lA7xKHmd7Fo3HlaYIifE7FotwdA1sJkdMdeU6YtkKlyZh8noh6sAt2JGNdhCa2cOUf1tbsbtp2XZonaBJDNh30ujz0c2fUGq0eRA6NKRNwm6ETsSxAR8cCBA/jGN76BE044AWeeeSbG4zGuu+665vdf+9rXsG/fPuzduxcAsHfvXnz5y1/GnXfe2fyfa6+9Frt27cJpp50WYpcINqiRu2ZFVy6R2C6c9oZxWD0R7XLm0OnMqUsuFalcW4SEwl6tLEqZZAVTD89SN+Q+41czvguBcaLEX333g5Uzawtq6YJV0OxD40T0DQSrn76SuCeiPsaHEGdnjYjIawVZH6md35k2q/EZN/TzNU9Vdtm4Zdpy5rFHAEmhB6vUTsTtqMIsYx6agLqHH7VORDFzf79KS2wTacS2RvQTo+rYoCU2u2yvfq4qZ1Zl2tHFUe11zEtVzuy2KTW2j7SFTxrOybpYjt6xR5o70DobZZYDoypkagVTumJ74lTO/MIXvhBPfvKTcfLJJ+PWW2/FZZddhjzP8cxnPhNHHnkkLrroIlxyySU45phjsGvXLvz6r/869u7di8c85jEAgHPOOQennXYafvEXfxGvfvWrcfvtt+PSSy/FxRdfjJWVlaAHuJXRHXuheyJmQiRzIrYrWdCciOFKf1Nd0OzQmtDpzMvSE5HlzGSz03WjMS1LrGjJkDH3I1TJXVPCpy3QpArOAjYmnTlVsIrUFuCCOejr41oZh3mdfPcDCFMmro6D1wpyOKghQ2gp9YBna4cNCPDri6jFIsOJKNwdOFKiQ0SsQoxiCqRNOXMmtHLm0lnIbHsRKhExjdiW1eXMlSU2QE9E24lYH1fscmY9HCb3TGc2glXYE5EcJiKwE1Hoie4jljO74iQifvvb38Yzn/lM/Nu//RuOO+44/PiP/zg++clP4rjjjgMA/P7v/z6yLMMFF1yAQ4cO4dxzz8Wb3vSm5vl5nuPqq6/Gc57zHOzduxdHHHEEnv3sZ+OKK64Ic1QEgNbIPWt7IoacjE0SNd7v6vUY0omYrCei6m8VKrmT6cyEbAi6a1gtokwLiRWnK6o7unOwDRRw354+cVYCXmwhRx8uVDqzb7lTp4M+9uKXXioe+LqleiKmallRBnYiqmvEtCwhpYTQSlQJsWkXt2E6EUOUM2cJy5nlfE9En8mzUc48adOZ1e9iIZpglRGQq2AV/3RmUb/5SmwTqZyI2ShMsAqsYJVU6cy6E7EWEV0/L2awSvUzCjdkXSxH7wqmXiGoasyQIgdG25ttUtDuh9OU56/+6q/W/P22bdtw5ZVX4sorr1z4f04++WS8//3vd/nz5DDRJy1hXCrtc1M2xe3s9egx0dV7cKnvU6Am/3o5s88Eaq6ceQkmmAD7XJHNjzq3tmkiom/SrgtmUr35M5/tVUJX2nYVAIIJmcUSuB/U8Cs2oEfxJHk5c/t1CIener+krL5Wi4WEdKG3K9B7IvqIE0Y5c4Cx1W0n1ETXLGde9RBwMsuJmCKduS37FU0fw5FHT8Q2WMV07JWxnYi1OCqyTCtnDiAizpVpx3YidpUzuwvZgNnHmT0Rybp0OBFDlDNXPRHrcmYx9VqE34oE6YlIlpOuHkx+PWLa54YSJn32I1SvR/u5qRbF7Akh4Hdcthi6LOnMdCKSzY4eaKHmrCl6fao/mWe6E9F/oSjX3OuxxdHOcmbvYJXqMRMCuXIBRndYtq7RYD0RlR7QVAUkWigy0pnD9US0vyakC9mxYA6Eud/NBIKMrU40PRHNYBVX0aUsJUZ14q8KVtHTmWORydaxp8qZc4/javqlqXYiWSInYlOmnUPUY7KPE7FNZ67e+1JUr1Xs49IFnLwWFH3Tmc1eo577RwaPsHsiipnXwkezvawtZ96GVQraPaGIOGC6bqx83Gj6TUYWsK9TX4yeGoGPC0jYV0qJiHl7WvpMoObLmVOlM5vfU0Qkmx19NX0cwA3til6mGyLpUB2CSNiuoqucOVwgGNI5EQNfjwGtnHkcxtnovB9GOnM4JyLA6wVZn1Ibt6p/1fc+57gudowCOYd70zT/z5qy30pEdNtc2RGsMhEFRvCbkPdF6L3+stZh6TLOS81dKZRTT6RJZ9ZTp1UJslc5s1TlzJYT0SOsxQnt72WqJ6KnE7EKVql+xnJmsi4ycE9EaE7Eemz1dTduRSgiDhg9xTiEa1A/X3V3Y2zlPrjD0rrGp1qJaN1NbTiDl4g4V86cqtSN5cxkWBgpxnmaABJ9P3RhKkQ5c74ETnNgY4JVmtcp+nFVj0L7zIQ6rklT9p1GcDPSmQP0mtSPg9cLsh76OAi0CwU+Gr3eeztPJXaU805En4luUWqi1srO5ufbsRq1AqfZB5GZTkSHndD7PNrpzDEde1LKptejEFmQnoh5U86seiKmCYzRQy0yz3Tm5locaOGTbA30knoAmHj0UAXQjq1Z3jiXM5QUtHtCEXHA6CVcTflUIFEq1yZj8Rvvqwt1mHRm+wKW6nrWWc7s8douTzrzcjgiCQmFfiM8SuTYM/ZDiCDN/7tc3rFFRL1qJdRCld7MvTmuRE7Eah/8PzN6uwrlRFyG8KyDU/8JrlHOzOsFWQd9YRnQglCC9IfVWkVEHjNU366qJ2ItIgqPcmYpkatQk/H2SsQDsB2H4pYz1/uQ5a0TcYTSaR9K2W7PLmeOKbaVEpqYOWp7IsJ9H9pgFeWwVGp27J6IWjlzWQXxOJczy9aJmOoeg2w+hDWhnWDqtUjUpjPrY5CnMLkFoYg4YMxyZn9Hh9ETMUNT4hE9WEXv2xVgH+xBI9WqmBoQx1oTeZ+0zWVxADKdmQyNrrTfFJ9rdYobgSEeu9Eu0KQT2/S/FypYReoCXqL+ZmXZXo9D9/JV6cypwrNCOxH1Y0vRa5RsLqQm+AFaywKfRVhtsXoUyDncGz1BtClnnjlPnkspkYtaEMpGwPgIAMB2cShNsEqmORGFWzlzqZczq/rYupw5pthWvbatI1KVII9CBKvkKgO1FkkTBqs0TkRXEbGYvxbTiUjWQyBwOXMTrJI1gUy5kCgS3UNtVigiDhh9JTV02a9eFha7TNYsMwngRLSem2pVTHfLbERgTLJStzkRkTcMZHOjRC29nDlFT0SzTLf6mVdPRC2oRd3gxx7f9f1Xk3dvJ2Jgx6YLRhuOPEBlgPaapE5n1q+hQcqZDScirxdkbXTBD9BEeh8nYuDQKheMBNGmnNndLaOX/kLklRsRVUJzzGPrClZxDYyZOybUPSSBuT5qG4mUaMuZ9XTmAOXMeb2tMkskIhrBKp49EbV5qUgVWEQ2HbareCxmXuO73r+0cTBXf8h5m1sRiogDppm0ZGHENv2EzbWJUHQnotQnzuHFtmTlzMphGUoctdOZkwXGmN/TiUg2O7rgr9xySdKZtb63IfoLGe71ZnxPk85sBpD4iojVo1mm7bXJ3nT3KHbfCf1tXhmlTWc2nIghypm140jlriSbB/URmStnDpLOnLDs0ihn9hPbACtYJcuByQ4AKcqZtWRUvZTQ4VQv5HxPRLXN6E5EtR+52WfNBVmWyEUtSo7q46lF0pjiKNAG4QBAVveZ9EkIB8xyZmqIZD3m0pkxg5RmWxen7Ym8aesAILpAv9mhiDhgWvdFmCb5+kUjVD9Cn/2ojqv62SDKmbVJZtPD0mNSuCxlxPNiZpj9CNF7ixAXdJdK4ypLUs7cjoUh3DL6As0okdhmOM2bgAS/MVm/ZowCCHhO+2CUM/s7B7vKmZO56LWXMrQTcXXGGSZZm7lglYAhU5lAurLLUi9nngCoy/gcz/OyrAJMAFRCW75Sb7Nwnoz3RU9TzjQX0AilWzlzqQWaKBExgdgmpR6EkiPPVWCM23hY6u6/JlglVU/EtpxZ1D0RXa81amyvFtOqn7GcmaxHZp3Lk3occ77lkdpiiuVEjDUWDgGKiANGLlhJdT1B9EmQEHpvqbiTMb0PWIiJ83w6s/OmvCi0ybMKawjlHAWWR0ScBpgU/v8/9H/wiJdeg6/dfo/3tgjpiy62TQKcq8770eFe9ps4V49ZwvHdaMOh3JAhy5mbkASvTfYmRjnzNJXbXC9nDrC4o1/P6UQk66EvPOiPfovm1aMIVBnighGsopcze4RaNMEqwu5H6L+/h4OUbRCKyNpej7mjw9IMNKmntPVxichOxDadOW/6GLo6EYuZJtyldiJ2pDO7ngpNFQfLmUkf5HxPRMD9syP0FgiiFRFzlHTG9oAi4oAxJi1qiRbug796nrqhSnVjpQYNEWjibE9QU6UztcmoYRrvq7mX6hOUqreUfXMaouzzMzd/D4dmJb78r3d7b4uQvugpxkoQil3OLKU0WlYMJZ25Hd9bMcB37DKTkcO4G133IVSPYr26p+2JmL6c+WBgJyJ76JL10AOhAP8KFX1sSDlmGCV3tdiWCWm41PpQltLqBVa9UCMPYbL3PuhBKMIuZ+6/D3pPxLacuTouEVFsM0rF86wqaYaPE7EVEfN6W1KJHdrvYqC7wDJPJ2ITrJLrwSqeO0iGj1XOPIF7b05pt3VQ7Q8QdywcAhQRB0xX70DAfWW/sG7UUvVE1MtMwjgRzeemsjJ39eDxKnert7etLnVbFidiiImuOpb7VuPeTBECaM42rSdibJFeP63M0l/3beoCXioRUXcVqbLfUMEqeSCx1WkfmnYV7WvrMybrwp3qiVjK+EJH9XfDOhH1awR76JL1aBfMq0ff8mP9eZnQeixGL2fWnYjtRBfF1G1zxuS5DTWp3DexRETdOThqk1Ed90F3ADaOIlX+G9WJqJczj6pSbVSir3S4KOtOxKacuQlWiTsm6mKs8E1n1pyIqcR5svnI7J6Iwr03p36uVqvVrRMxizgWDgGKiAPGaJKviYi+q7N5s9qbeW3PFd0tkwVYybIHjFQXNL3krhEmPG4W1HE0LpUlKHUDwjhL1DbuXWVfRBKf9kYYGGdpQi3soKsQ/YW6HHuxJ876PqjqNP9glXkXYOzxUG8vMgpw7dSfq64XADBNUP7LdGaSEn0BFvAPVtGfJgIt0LjQOhGzxokIANJRRCyk1hNRtA6c+E7EerEqz72FTDOduXYg1mJiJuMtMkurnFn1RASAoui/H4VeQjwyez3GdFjaf68tZ/abR470dlQUbcg6COtc9ilnLmxHthaskjv2Zt2qUEQcME1iXWY7Ef1WZ9WmQpRkue0H6v0IVM5s7X+q8aNrAu/VM6t+7soojVNKYR9CiLJPJdhQRCQpKJob4axxZEcXEbUTK8tah7jPDXnZ4RIoIo8bbS8yBHMqGGNropAEPUE2RE9EfZFwnGvX9wTjvP6Zm5XS222uf7bpRCTr0ZwL9YzG977QdiK292OxU6aUEzFveiJWP1512lxZdpfxZSijCTlSApmoHXsia940176MRSmRC+2YgCbJOmbvQMOJmGcQniIidCeiEpAT9UTUy5lF/dlzPbf0YBU1l6QTkayHLZxPoJyI/bdlJKmLHBCiCS2KORYOAYqIA8YojxXtJMN1wNZL+IBlSGcW3r1v9O0t+j4WXT0sQyStroyrF2m1SJM6ZQ/IISa5Soi8jwnNJAHtWNi6wGL3bltczuwzFlaPer/ZVE5EYxwMGaySupxZS4j2EdsKTRhVzsZqmwnKma3D8HUjzigikh7o4xYA73Yw+j2g2R/WYycdaCbOIjNK7lzLdA3BzXAixrs3NJ2IVjmzw5hclTNrxwTNiRg5WCXTyrRVH0MAKIv++1FofQ9VabRU43xCJ2Jbzuy2ra6FSpaPkvVQrmyZbwNQBUwBbve7epI6rER313Foq0IRccDok5Yg5cz109S20jkR2wl8FmDibF8Mk4mI5fz75TOBUjeFK6P2ZiaFTdu+OQ0xKWydiOyJSOKjO9uUCyx2qEWxaKIbwJWdMlhFHwebcidPYazQrl2jRBMXaSx+hXOaV+0v2ut7inJm+zPiLSJq51KqNhxk82CXM+eermz9Ixeq4sWFZuKcVW6ZUk3ZHEQpwO6JmDcT6Bzx0plNF1Arjro6gMouQSCL79jTRUSIDJnWw7J0cCIq4XEms2bhqxFJI4qjgO1E9Ctnbu4xcpGu1yjZdDRj4agSEVWwituYoZ+rZh/VkWA5cx8oIg4YvUG9EKJJ6XW/sbJu1AKEf7jtB5r9CDlxtrcfG8NZkofomVU9bhtrLpUEB2cfQ4hJ7nRWbfO+VbpUSHy6+pfGdkyZJXe6w859m3qJ7DK0q2h6B3pOMmQztrZO+tjXLfW+6GE8XunM1vU91fUYmH9/Ds38Jrl0IpI+NE7E+vu2DYLr9trPn95WIfZCkdDLmYFGRCwd03nnRcQUPRHRTOCzPDccQO7pzOoDUPdEbMqZ471fhrtJ5G0fQ5j9DQ8X9R4X+jQ9i1+mDXSlM0vnz4sa23Oh9bTnEE/WoRkLx9sBVGKfawiKsZBRLzgIz8WMrQpFxAGjTi4lHvo2h9YnzoCe3pmmF1imXYSGUc7clouHaP7flDNrNzMpJmT2gKwEQB+adOYpnYgkPrpjb5SonFl3X2eiHd99ytIKbQxSY2uydhVasIrvBFe/Zvi6lFzRewrrTkTX90v/DAKt6JtijLcrAQ5OQ/ZE5A09WRt98aN69DvHde3JaBUR+6OoB6sAKFUAgEc5s+HAUaEmIl4iqdRcQFk+gl5S7SoIZHawihIEYjsRhVrZyZpEZQCQLk7EuidiqU/T1fElLGcGqlJS1+ovPRgzVX9isrnQxwzUTkSgCldxkR/0hYzucmafvd1aUEQcMHb5ceY5YNvBKul7IrauEp9r0JwTMVmKcfWYBXKVqONQwSq+23PFfm9CTHJXGaxCEqL39RkvQbBKrjUp91pQ0cM/AiQIO+1Dh9gWSkSsyrTrv5PouPSSan3f+lJY12PlbkziNt9AJ2Js9xfZfCyqknHu/623ihDpWjs04k1mOxFdy5n10l9dwIvnRNTTlLOsDVbJPJyIc4KAFhgTa5w3hAmRGT0Ri5lDOnNdzmyIiOr4IqscAraIOPMOVskDLhKSYSO1c0uOdzQ/H2PmtFAkbUc2YIxDFLUPH4qIA6btLVWLiJ4Dtrpupe6JqM5vfSXLq5x5zonovCkvmomuJkz4uDybBNk8a9yoSZyI1gsaIuGQ6cwkJXrC4DhLI97of06IMP2FdHE01Q2+XqYbKtzFDONK9X61+6Dcqz770SbSpq0MqP6mJSJ6OhF14ZDlzGQ91PAgmntdv3tTu5w5Ve82u5y5eXQWESVyaMJk7WzLI06cSwkIFaySjQKVM3c7EUcooo3zRvK1yCGyDIWsTQ4OY7KsA0xmaMXIpidiZCdiPudEdBNvAHMxjcEq5HAw2jDMORFd3MtoAqZEhxORovbhQxFxwMw5B4M5EdWkJY1TxUzaNH/mgl1Olr6cOYzLs+mxKNAIHdMEg6M6LiU6rwYpZ662cR9FRJIAdeMyygTGI/W5TtMTMc/CuG8AfWxFMidiV+mxlKGOS3Mixi5nVvNLEciJqBb16teodcQmGOOt0uqDU79xmeXMpA9tH9XqUeUM+QodQpjna3QnotX8v4AK1nBr4zInuGlOxJjlzO0+CCNYxWUfdJdSI7LlrRMx1ntm7Eem3i/3HpYqWKUpYQea9yt+ObN5bzNxLCMFzKqALFWbALKpKPRyZq0Fwthx3NLdy6IJVtHKmSlqHzYUEQdMaTkVfFdnC03kAhCkb58L+nGpCRTgUbpipzOnKmfWnSrKLROgnFlP70xRGmaXVfs6S4qybep8n+dklRAXGlEq087VRL1h1Rjo2wcMsMbWRO4bfQKvXlvf/dBbe4Too+u2D+2iTq6JiK5jvP4ZBBDkmuGKem92TKobce90Zr2cmQ2KyDqELmfW3dD6Y7py5urcliJkObPWExFlxHRmM4DEdx8qgUG9YWZIwihiSEKp74d6n5SI6NATUZYdPRGzNE7ErKOc2fV1VefQUd//Fxz9D2+rtkUVkayBlNW5DNSiXz4BAEzE1Omz013OrMaheK0dhgBFxAFTWjdCvtbx0pq0puoT09UzC3CfZC5NOrMR1uAv0Dbvf8LwB30/VsbVYO07KdRFSDoRSQrUaZQLgckozbk1F5zlmUhabbN61EuJfV2Arvug90wCfJPq1diazmFpLhJpIqLjG2ZXGqhrxjSB6Kb+ZCgRkU5E0gdpLXALT4fTov7fQNyxsHGAWenMrsEqRsmt1hMx5sS5lLIpZ65KqmvBT5QoHcYu011ZuxrzcfWIEkWk8cMouRSmE1H1N+y1vfo+t1tEjJk6LefKmV3FG6Ad2x/x9T/AcR+/FP8x+3wy4wbZHBjneJYD9fk9wcxpjDcXMtRFg05EFygiDpjWiVZ93/QPdLz+2KJkOidiux+ZfnPnWbriux1f9ONqy2c8eiJ2hD+kcHW0KdHVcONbzqyLiPeuMp2ZxEcv30yVimv3qFVDoc/41ZZIh1mgcUFqYpuxD4HKmVP1elR/TggBESCsQe9fCWjBKkmdiJUo4VvOPGVPRNID/dwC4N0rWx8v9O0Bce93lVgUqidiYQtdmmMvXk9Eu6S67fnn4rA005lFvVm9J2Kc8cMIVsn8RV/lXuxKZ0bU1GntuGrGtTPRRfxTl6eV6X4AwDHinujVDmRzoZ/jIssaJ6JrwI8xZljBKjFd2UOAIuKAaZ0qgcqZrfKpPED4h9d+iLY0DXB34CyLE1EXffNGmPAvZ861kstpgH6EfZGWiOjvRGyPgcEqJAWFJnSNG5dvop6IAUvuzN6BYQS8vrSCQDgh02jmHqDs228f0OwL4C5KzJczJ2xZEbic2XQi8o6erM0i52CocmZ1r6v/rRjY5cyqP55zsEpRIhOaCzCBE7HqHagG+Tbcpdq//sc151JCHdgCIBMxeyLq5cz158+jJ6J6j3URUYmjWVQRUTalpIoJZs3v+qLmi7msgmO2YZXOL7ImZhuGkSEiupzfXa7hxsXMYJVeUEQcME1ZmLWa6jpgS2vSqh5jrszqISiZMFeInVedl8SJWGgTXVV+HEQQ0MqjU5S6qf1YGVWDtO+kUH/+oRkHfBIfvTy27Tca93NYNItEqPfFP+lQnzyHCP9woStYBYBXWVqXOBq7hErvUQvoop+rExHm9pSYHfm4pJTN56YVEf0muUZPRJYzk3Wwz4VQ6cx2FY/PNl2wy5mlZzmz0UtPExFHIqITsdSETMuJCJcAklJq2zNDEkYoIvZExHw5s+qN6FTOPK23ob0+CYJVilIiF/M9EQG3OVdzLa7Tp7dj1asFCxk+1TmunIitiDhxdCJ2hSC1wSqSonYPKCIOGD3tF/DvYbho0prCpaL+foieWfZkcjmCVUL0RFSib9pSN3UI28Zh3JB2Cq5v6RwhfWlFxKw5t1ajlzO3iw76Y5jegabLO8VCUaiet4DZZ9HXAei7D3aPYt+eiGo7qcKz9M/b9qacmT0RSTz0FghAiHRmGNsz7jMjTjCbQAvVE1EJSo7pzJDa84TpRIwnInYLmQAgHcQxo9xWuRqb45LR7nm7SiTLOk3bKZ15DSdizJ6IhmurRomILpeuRkSsP4vbxCrLmcmaGJ9Bo5y5cPoMdjoR1ZghGKzSB4qIA8ZOrFM3Qq43C4U1aU3RE1E/uYUIk85sPy1dOXP1qCej+kwIdSficqQz107EgMEqAEuaSXyMoI5EAr0ujAHtOO8zfpnJyGnCBPQJvBCi7fUYYkElS+hE1JKv1b7oP+9L666svm97c6ZxxALAEYGciPoxsJyZrMfikKlQrQK0lPiI51cjFmVmT0RXJ6Ix487MnojR0pn1fRdZO4kHIF3KmbuSVmsxMWZp4lrpzE7l58W8iJgiWKWUmC9nFrVLMoATcRsO0flF1kR3+c6lMzv1RNRaKmRmOXMeMdF9CFBEHDDqfqHpiehdzgxjO7kquY14U6Xv+8alMycuZxYC40z1D/RwFRnOxjSlbvp+rCgnoufdqv2aMKGZxEbvRzhRrQISOcDa8b3et0Bimx5aFXOhyF78CuEc7OqjG/tGcZHo53pc9uuULHVa+9hvVyKitxOxfX6KMDCyuZhzDnqe47obWn/02aYLc+XMnj0RjUAOLVglRxFvUUV35c2VMzs4EUtp9lgEjHLmWNcu2VHO3PRELFyciLXbTxNZhVAiYuRyZiwoZ/YIVmlFxFVIabaqIkRnzuVbpzMHCVZp3MttsArTwg8fiogDxm6875vOvKiRe8ybKn3AyDNRJ13O/851mz7b8UUvP29Da9z3RR2Gns489Wx474I6BBWsErqc+d4pE5pJXMx+o0ocT+Nsyy2xzWc8tlNJR55uORdCt+EAzNLvdnseO+mAvQjnG4Rjp3P7lke7on/eVE/EgwF7IrKcmaxH6GAVW5QM5YjuS2YFq8i6PNY1nVfoAl42ansixuwdqL9+mRWs4lD2W5S6wGCWM2cxU6dLK7QGmhPR4WKjHJtSn6bnKvwhnogoO8qZm2AVFxGxvj5lsu2JWP2c4zzpZq78WE9ndglW6QhjYrCKGxQRB4x9YxWq2XRu3ail6omYWeJouHTm+AOI3pze6IkYqJy5KblM4OpgOTMZGoZrOHE6c+uWCSC22UJXgpYVc07EEKnT2vvVLH5FHgvnnYOe1y17ewEWnpz2Q/t7O+qeiP5ORJYzk8NnkUDveiq05dGtBTHEIk1fRDNxrs6roE5ErR9hTLFN2k5EIbzKfo1yZiVICs2JGKsnoj6Qq3Jm9ejSw1KVM2siq2iCVeKNid1OxHrfnMqZq8esbHsiAvErA8jmwUxnbp2IE+d0ZiAXthOR5cwuUEQcMOrcErbY5unYm0uXTDDB1PfDt3Rl3onouHMe6ANhrpUfhyjhy7N0/bKA9vVtnIje6czmMRykiEgio1o45Hq/0dgOMMsZHiKduU0Qrr5vBLeYLSvsNhwBHZZC+C+mOe/DgkU932AVZb5JcT0GTDfK9rHqiejZskL7vDGdmaxH2ZzfYdKZ9XsnRdunOkE5s3IievZEVNuTEPVgqJyI8Ur4jKTiAIEx0ihNNEMSYrqK5sRR+PVEVNtT4SzVZmu3VMRy5s6eiPDpiVj3titNJyJ1G7KIUheybSeiw+em7BozNCciO6gcPhQRB4yd3uh/YwVjO3kCR4d+o9NOxuZ/1wf79UjRm0O/GFfOwRCuonm3VAonojoG1RPR9yacTkSSmkIbW8epy5nnRMSA20zQP9B2WLaOvQBjYSa0xTSPnXTAdkv5loo3JdpNOXuagB/9s9H2RPQtZ27H+Nip52Tz0Tqoq0dVLROq/3e1zfitHZQTUYlHjdjmKiIpMSszewfmiJdIKu1wF/g5LIuyIyShHgtHEV1F0k6dhhaK4hAYo7ZnOhHrnoiIOOeS807ElazeN5905lpEXGE5M1mH6jOoneOjFQDARLj3RJwrZ9YWVOhEPHwoIg6YuRsrdT3zdiKq7cVfmV2rnNl31XnR9zGQxnG1r62PMNGVzpzGiVg9NuXMnpNCe1J5r+eElZC+6D32Wpdv7PLY6rHt21V9H6Lstw3PSrBQVO++3esxjCtbJGnDAWguT2tRz/X6aTtRxwneK8B0r26r3ea+TkT9vfFp6UG2BnaVTKjWPbqImMLBnElTRFRim7sTse6zV5dHNxNnUSLWraG005nh57DsLGeujysXZbTxUHaWM9dioovDUomIhhOxdlhGD1YxX8MVUQerOCbjAm1/zu3ikPO2yNbA6HsqMiNYxWU8rkKQ1AfRXHhgsEo/KCIOmPbm3u4d6HdjpSZhowQ3VfrfsidjgylnzsL07TL6gOVpXCqAVs5cOxHtYJS+2OEw960yWIXERRfbxqNEDjCr9DjXJrzegQJzY7zjTjogbSHT8zojpTQE11Qiol3O7BtMthFiqwu6K3dbU84cLlgl9vGQzYedpuzroG57IrY/SzFutKKfWXLn7ES0gloasS1iOnMrjonmBZYeZb9lubg0MY/YE1HqCcwqTVulMzsdV7U9qTsR8/giYill2z+uZiLcg1Uql7lEVlYOxG2qnJlrRWQBZk/EUVPOPAmRzqzGQgarOEERccDYN0KhV2fzAH37+mLfLFb7IYzf9cXe/RTBKkY5c6CeiLrzpXUipkhnrkXE2ono+3mx3ZQsZyaxUadRngmMs7TBKnY5M+AxebZKZNVYH7MNQmFdt/x7+bZfG07EyON8I/pl5msb6no88nQ2uqInequFooOewSr6MfguOpHhM9f/2zud2RxbAX/R34UM3U5E4elEtMW2EYqIwSqaiKh+pkQ3p3JmXRAwSxPziIJA2VGm3ZQiO5QzK1em7ChnzmKWM5fV50NnxSNYxd5ek85MJyJZgNSF7Mzuiejmhs3n+qgyWMUFiogDxp60+E7Gmp6I1qQlRTqzfnPXljO7bXO+J6LbdnzQV+FyrSeiz4RQd9+MEgkdgNYTMViwiuVEZDkziYxezpxKoC+t8TjTRURvYar63jdB2G0fUO+DVfbreUxAda3QxbuY/W9th6Vv39u2nBn19uIv6gHt9TLPRLNQ5O9ELLWveUNP1mZxObPr9mBsT/86rhPRFMd8xDYAyErNzaNtN+bEWYltpTb9VGW/wqHst9D7pVlJq1lMQUB3B6oyZuWwdHAOdpcz169TRNue4dqqWcnqcmaHc6EopSEiNunMHOfJAgo7CMUoZ+6/PdOJaAWrCJYz94Ei4oCxy5mzRkT0257tfIlazty4VNqbOxHI0dH8jQQDiO1EbHoi+pQz6+nMud9E3Ad1aK2I6Dd5t3si3kcnIonMTBtbR4mCVeb6F2pjosvpZZT92n37IqqIjdhmpQ6HCM7KtKR6IG7rCj0hGvDv5bu4vUialPBcCGwbsyciiU/rRKwevcuZrXMV0N2NbvvoQlaLLUKJh0r8cy5nVU5Es5x5lKScuZ1++oijUkpkyqVkiYgjlNHuec1ej3V1VBMY49ATsVDlzB3pzBGdiIWUbTpzvS8T1OXMDufXrJTN8wGtnJnuL7KAsoQZhKKciK7BKnqfTyvRfYSCTsQeUEQcMPPlzNVjaJdKVCeiVW4HtIEx7g5L83lJypmt1OmgiaRCYNL0REznRFT9sgA/MZPpzCQ16twaZenOrdIS23zLmbtCq3wThF1Y5CoK4kTMRBDHptt+1PsQqNfjfHuRNOFZjZitOREPerjDpZTGMawm6ONLNhd2mxv16NvixkhnTrCgks05EetyZkcRsemxaJX9Vn3APHa0B0pQk/r0U7g79gpbYADM/maxeiLWImKBrJl0KRehdClnlovLmaP2sNTTmSdHVA+iPlZHF9i4Q0SkcEMWYTgHRWb2RHQ4D8wei/PBKnTFHj4UEQfMokmGs9i2YHsxb6psIRMIn86cpJxZOy6hl9x57Iue3jlKms5sljNX++H+mbGDVSgikth0nVuxxQ57QUV3zbiMhXbZL+CfIOyCXaYd1ImoubyBROJoICe/3V5kXIvZqVKnq3Jmfyeivft0IpL1UKexsMYtb5dvh4gYc8wQVk9EeKQYSynbMtg59028sl/ZUc7sm848F6zSOBGLaE7EstNh6eFEVNvTnIhZXcYZs/zcEGlH2wC0TkSX82tWlFZPxEMAJIUbspBST2DXnIgTzBwXzBeHMWUokxiJNisUEQeM3qsICNHI3dyeb08nn30wetUET2dOV85s98sK5URM2ROxTWdub4Z8xEz7uUxnJrFpk89b8SbmYgpgCpnVvmjimGOvIoWwSoljugTsFOPMuzSx/TrPhFHOnMJF35RcBrpu2e1FUqUzZ0JLZ/YIVrHPI/ZEJOsxt2C+EenMnr23XbD7drVOxP47ofeiE01PxPjpzOgMVsmM3/XaXGm5lAAjWCXa/Xw9bkntuJp+hi73Bl3BKnn8BFmjh+F4OwBgRbiXM5eyKkNV5EJijCJqmwCyuSilFsYj7GAVl+3poqTZAiGP6MoeAqPUO0A2Dnsy5l0+taDHYsybfNvNAfinM9sDRpKeiJYgECIVVd/mOGFPRLUfquwT8BMz53oiMliFRKYzWCVyiqy9oOJbzqw/xU5njjkm2gEkvuKY/lrkQkBqwkDca1e7D0DI63H1fZvOnKgnYgZMaifiQY9gFfv1YDozWY92LKwe/dOZ1fbmF6vjljMr0c/fidhdwtf2DowltpW1AFpq4pjq9ehSpl2UcmE5cx61J2ItrHUExriUaXeJiJkmdMR7v7TXd7wDgF9PxMLqiQhUbkS6v8gi5hLYtWAVlzFeSrRpz11ORC5cHjZ0Ig6YdjXVXJ317R2obqaUuy2Fm6O7nNlxm3NORLft+CAXTjDdt6mXu7XhDymciNVjqCRb9dwdk2rQZzkziY0+Fvo6ylyxS+6EEI1zxunm3uodCKR1m6vrVshyZiFMsTXmzeJ8exG/6+ci93rshSK9/DxE2JrtNI/t8CWbD3vhwT+dWY3v7c9SJNUvciK6BKt0lvApx56IFybQlTrs5USUEtmCdOZclNGCptRn0BBHVTmzQ09EJTxKPZ05bx2Wsa7JpZSt4FI7EZWTMEQ6MwCsYJU9EclCpLSCULRgFdfWPXPpzAmS6ocARcQBo66ddn8r7xsra7U3iZtDmwiqibOvo8PX0ehDYYmjvoIv0B5HngHjBL3NFPrnpin99Cpnrj7AR26vVqOYzkxi09UqIHovuo6JbjNuODY8V4RKEHah1MYtIFywSttvtv1dzJvFuetn/eh8XNZ1y1eUdEUXM0MEgtn7n6KPL9lc2OXHvvdPXQF+vm0VXFDBKsIKQnEuZxaLnIgxy5lVsIo2ECtR01sctUsTi6SBMa3o69ByR20va0VEw4kY6bgqB6sqZ66ciOP6e7d05tIIVgGA7WKV7i+ykFJaCyq1iLgCx3Rm3ZU910eVwSp9oIg4YObKwgL1ickt50PME079LaHd3IUKjEnR/8veB7vpvo9Aq0/uUvVtA8wSdPUa2yXJfVCTSiUi0olIYjPTBJxkveis9hL6107NprX9tx3RKRaK7OuWrxNRHYsI5Jjry3ywSjUmhyq5bMqZE/XmzDMRpMWJvf8p3PNkc2GfC/7BKtVj531mxDFDTZxF40SsHoWDKFV0um90Z5vnzh4mslzs2HMRETvTmY3jiqW2zQertD0RfcqZO4JVRMxgFYkRTCdiG6zSb1tlKVFKLChn9t5VMlCqlgVqkLfKmX2DVToS3Vlaf/hQRBwwhb06G6pPjC10RbzJ70pn9g6MqZ+nhLY05cy2qyScoyPP2nLm1VkKl2X1mAnR9MzycSKqHlm7lBORPRFJZIoOEVHKNOWxuitbuRJd9kN/it2PMOpxWeKob7l4l9iawmFppykrp6Vzr0e7vYgqZ46dEi7nzwWfm3D7PUnhniebi3mXb5gFc+M+M8GCii0iNhNeB7FNamKbvb0RimgT5y7HnhL9XOx1Rs8+q79Z3J6IKk25K525/3Gp5+gioqgv8DGDVQzBpRERpwD6Xz8bw4ZVzrwNq3R/kYVIvaRe5MBoBUAlIrrID2Z5dFewCj+LhwtFxAETenXWLrltSpcinm+2GxIIMMlUF7Y8ZTlz9RjqvQLMyV0brBLf1SG192wcoDejeu6ubSxnJmnoEk6AyI49a8wA/MQx/TkhHdF9mStN9HYVLRZb0wTGVN+rMnjnkkstIbzaXnxhVP97mfB/r4B50ZBORLIe6hRqg+nUz90+h3YVD5Dm/Jp3y9TpzA73cYXsCCCpxbuY7pumd6BR9qscew4Oy1JC6C4lwHIixjoulc7cdVz971GFeo6ezpwgdbos59OZVU/EvueXei/0dGagLmem+4ssYFE5s6sTsSjR9lG1nYiCImIfKCIOGNvdpm6svPvEzE0wYzoRq8euMhPfdObWiZhCRDRvWkOUVuvbbJM7Ex5b1gq1fuXMZk/Ee1cd+s0Q4kHXuQXEHTsKS5QC2km0a4mHvT1focuFuXLmQKWJeYfYGve4TOdg5jkmLwpqmUYPVulyIrpfj23BelbKJAt7ZPNghwj6L5jD2A6QZsyY64no0TuwKglUac+1869+jNoHTKUYa+JYI476pjM3rqLqMaqI2FHO3DgRXZyjcr6c2ez1GKmc2UhnrkXEupy+7/xEje12OTOdiGQtjM+gaMuZJyJAObPVEzGmQD8EKCIOmHYyVj16N5u2BLwk6cxdrpJA6czjRE4OfR+aMIEA5Xbq9dDLmdOkM7eT3TDBKtVzj2Q5M0lEqZ1b6ZyI82OhTzlpt2Mv/uKD7djzdUPaZb8htum2H9WjvVDk3MvXEjrGTY/iyD0RtfFdF2rdBZyy3l77s9j9RsnmYlHoX6gQQSCNezlTop9Qop/qiehQHttV9ts42+KVMze9/oxyZnfH3lypI2AcV6z3S3SWM4foiaiLrfFLLqXUek6qYBXHdOaiWFzOTOGGLMJoWaA5ESdwS2fuLmfWF1S8d3nLQBFxwNghJL7pzGqy0KY9Vz9PMXE2eyLW++c5yRwl7Ik47yoJV85cpSKnCX+o9qN6zDOBceYvZq5aTsRpIVnyRqLSlUgLtDfJMehyZfssqHSFVqUIm5oLIPEMVukSR1P0erTFUd8QEru1R55A8K32A83f14Va18+Mej22jVsHDsd3shYLw5i8eyLqY2H8RfO5YJUmndk1WKW77HcUUWxTZb+6Y691WLqVabflzJmxvag9EeW8ONoIgC7lzMq9qFyjgOaWktFEN93B2joRq56IffdBVa3Z6cwUEclalKWVwG4sfjhsTxfGrVYRDFbpB0XEAbOo2bTrCWKXR6ubqpiBAvbNor4/7o4OJSKm7Iloi4jVz30Gsy4HYFonIjAeBShnnikRsb25YkIziYmamIzmnIjxzi97UQfwczCroSbU9lxZWM4cMlglpThqt6zwDART8+ZUPRG7ypmrn7ttT4mg2w0RkTf1ZDFzAr3nuSA77jND9Knui90TUQVruDgRC723XWY6G3MRM51ZOeza11aJo5lvOnPCkARZqCCUeSeiDOVEzNoE2ZjBKnZPxJFjOrO6b1rJzNdjuzhE9xdZSCk192qWm6XHTiGCa5czs7T+8KGIOGCaSaHVg8nXsWc3vAfiOdy6StN8J7rqBnScpXQiVo9tv0n/st+udOYUIqI+iQ9Tzlwdw46VUfN6MVyFxEQXToQQ3m5o331Q+Cw+2MFZQBphyt4PX1d2Ow62Pwvh9O69H5Yw4X09XpKeiIv6g7oK6l1OxBlnmGQNbFe2ChvyXngweiL6bdOFxomY12KUUOXMLmW/MN081YYBRE5nbgJItF5/Ho69znTmJMEq8z0Rm+NyeL+a5+g9ERO8X6VeLq7KmeFWztyIiILlzOTwmRP99LJ+p9Y9XU7E+AL9EKCIOGAWudt8Jy255aTw2WZf7BVnIIAT0UpnTtkTMZRrVH9uVUacLlil1EXEAOXMypWyMsoatwr7IpKY2JPMptQtgbNN6GOhx7hhlxEDacQ2Oxk1WDpzoBRrV+wxPpQTUb0+qXsi5pkwnFuuu6H2f5S3oiSdiGQtFrWD8W2B0HWfGXPMGNUT3cya6HoHkHSkGEfviahfuDL345oW5cIU61wU8Vr4lGukM3uIiF3BKlnEvm2G03O0DUArIvZOZ67H8W2WiLgdq1Fbi5DNRWmPXZ4hKGYf1Xn3MgXtw4ci4oCxJ5n+5czVY9OrKkEZ38aUM1ePyq2XtJy5cY2aP/faptCciKl7Io7UpNC/J+I4z7B9Ug38TGgmMVmGfnSFJSIBfu62tXoHpihnFoFExK5ejylKE+12IKFSp23hJHpPxLI9F/TPjnNPxEJb/ErooCebBztEcCPKmaOPhdo9tUpTFh7BKsUaASQxeyKi07HnLrbNCq3XY6cTMVaddlewirvDsnn/M11EjO+wrJyeqpy5ciKOnNOZq2OaZOb9+opYjboASzYXpnNw1JwHWYhyZmuBhuXM/aCIOGA2qpzZ7ukEuLsOeu9DR8Nr73TmUpUzKzHSYwcdsUWJEM4mPUFWuSxTlIUVmpjdTgr9y5lHmcCOWkRkOTOJSSv6V9+nKPtdq4ehmxPR3AaQSkQ0y499XYNrBqtEnLjYYqZ3r8cFPRZjh2fp47vu3HJ9v/R+o+q6RRGRrMWce1n43cut5USMNmboglpmumW805mtnogxwwRkfWNadjjsXIJVZmW5Zk/EaMEqHT0MfZyIYo1y5lzEe7/KUjaOWExUOXMVrNI7nVm1quhwIlK4IYtYVM5cLX44bM/oo2r3RCwoaPeAIuKAmSvxEH6TlrnJXRInYof7pv7S1Q6vXo9xk86couS3egxVeg6Yk8xxgmRBhe7AGQUpZ66diFo5M4NVSEzs1g55gvTzVrzpEv0ctrc0ASQw9sM/WAXGdvSvY7r25vvehipnrr5XY2t0EdHqD+p7XK2ImLU9dDnBJGtgV934LKYA6y1Wxy37BYDMFv1c0pn1ifNcOnNE901zXPPlzI3jrQerM91VpERErUw7msNyvpy5EShc5kdlXTJsOBHbBNl4TkS9nLkOVqk/f/3Tmeu5ljBfj21YBXUbsojKDduUQhqBUE79v7uciJ5hLVsViogDprBurLz7xFiTzBATBvd9aH/mO8lU20zZE7G5aVWu0QA3rHpAgXqN0ger+O/HdFZtb5JnjRORIiKJiZ3onqLHXldgiE/AS1ewSpLegXO9fOt9cBT81jyuiDMXu59vqF6PmSVkx+6JaC/s+b62ek/EENcLMnzsRVjfqpulcGVr7jUVrAJRlzWj//lQlHI+WKUR29wcPU7IDidiI7Y5lDOX5Xw5s4jvROxKU5ZK1HRyIlpOKe3rkWOghAuFUc5ciYi5VE7EnttqglVMEXw7DtGJSBZSSolMb8UQoiei7URUwSqiTFKNuFmhiDhQpJRzfV2EZ4lH1+qsmjDEulDb/bL0/fENjBk3PRE9dtAR2+WpXCU+KyK6A3CcNDSmeswCic5TrSfijkl1E3yQwSokIvOhVel77Olfu/R1tdtfAJrDMqpjz3IVeS4SrfU6xdTb5sqZfa9bjfnGKmeO3BOxmBNwqu9dr116T8TWuc67erKY+WAV8+d9aQT/lInuhhOx2hGfnoillBgpMahxNrY9EWP1Am/KfjUnos9xzQq5Rjlzil6PYcRRtT0h9FVCrfw8onGjKWdWPRExAyB774OaJ04sEXEbeyKSNZhzUXv2L6zctZqzUW3XY5tbFYqIA0Ufj3Prxmozr85uRH+rxlWUoE+WwnbfqHHNR5zV3Y1N+V4KEbHUJ4X+n5c2WEVowSoUEUk87GTcFD0RlXjTuaDiWOKhbwNI49izrzPNuOy5SLQsZdp2GE+o1OlU5czz54Jf6wy9J+JkVB8TnYhkDdRpbJcz+7YKECnHQt2JaJXcuaQYl3KNdGYhUboIXS6skTocLJ05gSCgej0aqdO1AOhyXOo5cgmCVZrXt3YiZqgE6b5zJeUynyhX2WQnAFXOTOGGdGOMXSI3g1UcPjZzPRa1R6Yz94Mi4kDRT4L5ZtN+N1Z6+VzsZu5d5cyZ5yRTvRwpeyIucjb57EvTC0y0jo7YLhXAbFIeIhVVdyK2PRGZzkziYQtTbU/EeGKH3ZdR3x+fdOZu902847Kdg+qYXK8xXSnWKY7Lfn19F3bs8IdU7Tjsc0Htj+tx6T0R1f0FnYhkLexzwTvR3RL8jW3G+ix2pjPXPfFc0pnLjomzNtjLSCJiK7bNO+xcnIjTouwISYgvtrU9EVvRrw1W6X9c6rUQ2byzMY9ZzlyUGAuznBkAxpj1T2euz50J6vv1lV0AKhGR60RkEVIvqc/acmYXIRuw057TLTwMAYqIA0Uf3BuHf6geTB3lbrEmY51uyGaF2G2bdn+zFOPHXNP9AL3IdIdISieiPskcBXAANTcio4zpzCQJdliHr/vKBbvHnr4/TjdWHcEqrcPSdS/7Y5cz+44Z3WFc6Y5rbqHIUxxV1+NUfW/t3pz+lQFtT8RR7h/ERYaP+qTZrQK8y5k7xtYUTsRMiWzq0SGAZC0nYvX3Ii3EKoedNv0UjbOo/z7MihKZUBODlD0R6yAUXRwNUM5s9ETUglWirX/pAmhdzgxUIqJ7OXN9bNuOBABsZzkzWQOjnFlLZ3YNGJJdTkTPPotbFYqIA0U/B0I5Ee3eR0B8J2J3al716Fvu1vQhTFHOvGAlPUg5s9B7IsafjDXCryZm+qzmr+pORFXOzJ6IJCK2MOXrvnLBFpH0r100F7vcFkjj2LOvM/7BKub2jG1GDYwx9yOUE1G9X+MEQjYw7xz1TmfWeiJOEjh8yebDFuiDLZh3jBnRkjtr4amQojkeqcQ2p3LmxenM1R+KJCJ2OBH9eiJq+231RMyERFnEuTcU9WcmlMNSqDEv194j0TqwYoluRpn7aKX5cuLQb7JJZ1Zi8bbWichyZrIIo/xYT2d2FPzKsmNBxVOY3Kp4i4ivetWrIITA85///OZnBw8exMUXX4xjjz0WO3fuxAUXXIA77rjDeN6+fftw/vnnY8eOHTj++OPxohe9CLMZSxJDoZ9YeeAbq65JZqwy2a6G176N99VN4WSUrifiwnLm0E7EFOXMZfu58enZpphqPRHpRCQpUD3abCditAkmFoh+akHFpSdihyiZe4iSrtgLKr7j+9rlzBFFX0uY8O3B24iSWRhR0pXQIUN6T0TlRFyd8aaeLKatUKkefatT1gzwi+xELJC14YiNiOhWzryo7BdAvJSppidil9jmII7qIqElIgKI1utRdjgs23Jml56I9Xy4o3dkzGAVWU61vz8C8gkAVc7cb1ttT8T69ajLmZnOTNbCSFPORlo5s9t5YCyodDgRWfhw+HiJiJ/+9Kfx5je/GQ9/+MONn7/gBS/A3/zN3+Bd73oXPvrRj+LWW2/F0572tOb3RVHg/PPPx+rqKm644Qa8/e1vx9ve9ja85CUv8dkdoqGfV+o+yHdCaJeZAfHde50rxIHSmVshwGcP3Zh3NvnfsBbaZDxV0/35/QjRE7EWffMMK6Nq4D8046hP4rEoJCPm+dXZXsJj8WHNEr4EPRFDBat0Ln4lCIyxX1/vXo/269QkaUcuZ1bnQqBWHF09EelEJGthjxlqkdnV3dQK4+3Poo/xpUr7zbSk+jqoA/3Ph7KUyEW3+wbQRKuNpuwoZxbujr1CFxE7jiuew7JLHPUJVrHeKyBNr0f99dNExImY9j6/lInBdiKuiClFRLKQuQUQ3TXo1BNRIhOas1FtFyxn7ouziHjgwAE861nPwp/+6Z/i6KOPbn5+99134y1veQte97rX4fGPfzzOPPNMXHXVVbjhhhvwyU9+EgBwzTXX4Ktf/Sre8Y534JGPfCTOO+88vOxlL8OVV16J1dVV/6MixoAcrpx5saMjWjnzGqVpvr2l2p6IKcuZzQmhz9xJFxlSNd2XUjal9VkmvN2wRSmb547zLInIQYg9FqY4v2wHGNA6Z4KlMydMMW6DOjzFtiZptf1Z9NJEmG0dAH2M93PQq16EsVuLzO2HXfHgep+hXL65ns7Mm3qyGHuB2zed2S7RB/wXM/rvxLwTEd7lzCqcoHYgZlkr5pWxeiKqsl9NwKxLdr1FxMaJ2Dosy2jH1VGmLdwdlqrvpejosZgLGa8PvSHSWk7EnudCc+9u90TEIQo3ZCGlhJnAronprunMi5yILGfuh7OIePHFF+P888/H2Wefbfz8s5/9LKbTqfHzBz/4wbj//e+PG2+8EQBw44034vTTT8fu3bub/3Puuedi//79+MpXvtL59w4dOoT9+/cb/8hi9BWixt3mKeCoTaYsC+tskt+UhfltM206c/WYWW4OHwdG01JFCzSZRhbb9PckRLCK3mB/PMqSTZzJ1qbQBHogrRMx167iPi6wLlEyRMBTX+YWVHzLfte4ZqQUfTPPMX7+dUrTE3HRueAqthjlzPW2VllfRNZgUb9R99Y91aNRzhy7tUN9UGY5swpWcRDbuibOAKTaZqSy37acWVvVaSbw/fdB6iKhJQhUfy6OiCg6yrTDpDNrJee6KzGWOKr/nSxvnYgu6cx2T8SmnHk1SaAl2RzMiX5az1Pp0PN0rXTmUcTk8yEwWv+/zPNXf/VX+NznPodPf/rTc7+7/fbbMZlMcNRRRxk/3717N26//fbm/+gCovq9+l0Xr3zlK/HSl77UZXe3JKZ4Yz769pYSHU6VWE6BNkG0/ZlvaZq6AR15ipE+tCV31fe6MCqlNF7zw0V3S6n3ScrqNdRLIDcSuzdncyPu+HkxREQtvZMrRyQmtvuqFdviiR1daco+ybidi0QJHJaLeiK6XmO6HPQh2ir0xS5n9k2+Vi9HI5zkca/FzX5Y1+RQPRFzrScinYhkLeaSzz2rbuwxKMQ2eyPbcma1H0pEdOmJWJZWOIH6M2IEYBotnVk2jj3NiejhsOwsZ9aENxlNHJ0/LngItI0oqQuHmkBZRlKzlUhbKjFbcyL2XSgqbBFRC1YpuFBEFlCVHzelHOZ54HBulVJCQK0UWcEqIl6/0SHQ24l4yy234L/+1/+Kv/iLv8C2bds2Yp86efGLX4y77767+XfLLbdE+9ubEf0GXlirs859Yjp6S8WejNmlbtXX9e98eyIqJ2KCAcQWBPTX2HV39PLEkWZXSpEgC1Tjvr8TsX3eOKMTkaShdV9V36cIIOlMqm9cYA7bW2OBJqrDckFgiPMiUec1I2GZ9lxlgKMT0RJHx4n6B9r74d2jWE00swwTJSKyXQVZg6ZlirXw4OxE7FigyWKfX2VbztwsIjdlfA5iW1ewCjTnXCSxrRXUtDYcTe9Al3LmDiei7gaM5NjrClbxSZ3udiImKNNWvTnVa5uPAahy5n6balzmjYhYlTNnQgLFIf99JYNkLgjFCIRycC8b2zPDmKL2Gx0AvUXEz372s7jzzjvxIz/yIxiNRhiNRvjoRz+KP/zDP8RoNMLu3buxurqKu+66y3jeHXfcgT179gAA9uzZM5fWrL5X/8dmZWUFu3btMv6RxXQ1yQ/VJ6a72XScG6u1Js6+6cxqEpaknNkuCcvb43Mud9MdHdqbFtd9036dC+E9wVROxFHdX7ERGOhUIZEoy7bPp/o8N6EWMZ2IHeKYGjZcm00D4YJaXLH7m3kHq3T28q0eU5Yztwsqbtuz36/WhRr3/bKvyb6ir3Id5nnby3eVwVlkDdoxY/5ccNsejO0A/s7hvpgOsOpnWe4uShklgbpTr3EBxuodqMp+O5yIKHubHIxyRnXREKIRvaI5Ecv5nojwSGdWrkyRzTsbq01GSp0uWkcsAC1YpX85s1owa8uZj2x+l80Oeu4pGSpFKTFq+rnmxnng4qAu9QUVK4yJwSr96C0iPuEJT8CXv/xlfOELX2j+PepRj8KznvWs5uvxeIzrrruuec7XvvY17Nu3D3v37gUA7N27F1/+8pdx5513Nv/n2muvxa5du3DaaacFOCzSdRPk65ZpJkEJy8Kk5QACtDKTUE7EJOXM1aNdHgm4h6s0ztHM/BzE7Iuo32TkAYJV1GRS9a+kE5HERv9Mqz50eYJ+dHawRvW1+1holwTq24vb6xHGfoROMQba9y3mzaJdIukbCmU7R0faRTGqw9ISaX0XK9XrUfVEVE5Eju9kMXMtEDw/g/ZCBhB/QaXUnIh2T8TMJZ1ZdkycgWbyHK8nYvX6dYmIIxS9779LPRVZe8OUmBdLRGzCU4xyZvc07bwWR2Tt/LO3HS8wphaz1d8etT0R+wq+qpJopISf8XYUqD9/s/sC7CwZIlJqrRgsJ6JrT8RsgRORwSr96N0T8X73ux8e9rCHGT874ogjcOyxxzY/v+iii3DJJZfgmGOOwa5du/Drv/7r2Lt3Lx7zmMcAAM455xycdtpp+MVf/EW8+tWvxu23345LL70UF198MVZWVgIcFrFXZoEQfWLM7QDxJ5lr9QHzLV0Zp0xnbgTa6ntd9AvRw3KsKQ0xXXv6aymEv+isnIjqvYrthCVE/+yq8zWFmN21qOOTztw1vqfsHWi7PF3H5aJDbPXts+iC7V71D3/ofp2A6rjGeefTgmOLtL7v11QTJScj9T5xfCeLacqZ7RYInsEqXS0QYo3xslNEVI49l3JmLAhWqbYpHdxyTnQEq4jcnMDr97/r0fREFOaAp3o9isjpzKXuRFTChMM+KGeoSq7WtwdEFBELS0QMkM7cuMryCabZCvLyXjoRyULmglD0BQgXJ2LXgkoTrFI4V4dsRZyCVdbj93//95FlGS644AIcOnQI5557Lt70pjc1v8/zHFdffTWe85znYO/evTjiiCPw7Gc/G1dcccVG7M6WpKu/lXefmA4XYOPoiHRjZTeTr/bHr3RFTYLGCXsi2qVuhojoXH5ePeZCGJ+DuP3NNCei8HciqpXMyah2IiYIfiBbm9Jy1wL+zhe3/YDxtwE/V3a76ND+LEmKseUCCuUqMlz5wvxdDAprYc+3tYPdY1E/vmpRJY6KqPQ9+1zwDcIZ51mzjVXe1ZM1mHciVo+u/b+7glV8U+J774MSb2TWmGV8nIizsmx7KWbzIqKL0OVEZ7CKKiWUvV9fWRZVTZ2wCuuEup+PG6yCjnJmHyciMt2JqPV6jJQ6rcRKiQ4R0TGduemJmI+wKrZhG+6FKOhEJN0URijUyBy/nHoiWs5G7TEDg1X6EEREvP76643vt23bhiuvvBJXXnnlwuecfPLJeP/73x/iz5MOZNcEU/VP9r6xSudElJ0TQl+nSvXYOig8dtARe6Kru4F8G9TnmYAQVV/EWSmT9G0D6oAX72AVs5xZlZEyvZPEwnAi2v3tEvQO7HKGu+xGl9iW5riqx7kee57jYGcf3QTvV6gU40U9Fn226cLctctTbGl6Imatg55ORLIW6uOuBHrf4KS2MiTdmKE7EdUYr1xpLunMs2KdcuZITsS27Fd/bevAGNG/lFAWlYgoRaZFtbQiZTwnolZWXeMTrKIEX2GUM4vq84AyYup0d7DKxCmduW5VIaf1tiaYZROgALKCTkTSjdSdg3XbghJZ1UPV4Two1nAiMlilH717IpLNQdcE079XUfhSYtd9MNwywvxdX0rN+QAkKme2BNoswITQDmtpwh8iCm5m6afw/gyuWiLiOIEYQLY2ugbfCCcJHLFdQSg+E91Ox57q9bgUvQP9F1MUKcrPG2d4sJJLs0zb6Hsbs2WF7aJvxni37ek9EVXbiilFRLIG9v2Tb//v0KFVLnSXM6uJbv+J86wskYn5cmbVXyyWiIimh6HmRKwHsQxlr9dXStmW9WZWOXMWN1ilEQpFGIE2r4U2Q0QEUNaOwFjBKmJRObNwT2fOjXLmbdWXLGcmC+gS/ZRY73JulRIY2WOhFqwS8353s0MRcaCs1RjaXWwzt6N/Ha0n4hrlzM6rzk05c7WdFOOHuhh3CbTOzlF1wW6cKvHDH+xEb++eiE2wijlZYE9EEgsjLMhygSXpiaiP8R5lumoMMvroJkgxlpY4ql5j19d2TcdmgvfLTpD1vW41AoPu9E5Qfm6Lmc5uc030HTUiIm/qyWLaypvq0fveqaOcuflcR/osKvGrRNaGDudtinFfZl2JpGjFtixaT8Tq9TPLmSuhbISi15hclBICyoZqtW+IHRhTdjgRPdK0VTlzVgeZKNrAmDgOS2k7LPM2WKXv+VU0wSq1EzEbYZpVOQjsiUgWYQahKBGxPt9dglX0McFyIrKcuR8UEQdKl0vFP1hl3onYTlriiDidE8JA6cwpXDcK21VSfe03eW6diPb24qczh3LfqMlkk87MnogkMur8EaKrkX/MVgHzDjsfp287trY/S5M6XT3ariLfdhWiy5WfwGFpj4W+vQO724tE/Bxai1XeqdPaGN+UM3ORiKzBXCVHoD6q5rkV9/6wK5058+mJWEiM9L5iishlv61zSBuPrWCVw2Wm9UoTVk/EJjAm2nF19UR0d3nm9XOyke1EjNzrsbSdiKqcedr7mtw4EVW/x3yCmXIispyZLKArCKURER3OLaHfTzQNZ+lEdIEi4kDpStoUnjdWxRqrs9HTmTuSNr3LmSM3zu7ah1AhCVLKuTRQ5d5L4ZZq3Td+ooQqa1PBKk1PRIqIJBKNI7vjXI0bWlQ9hnJlrzUGRR0zFpYmhin7BbSQhATlzMHcUh2VAW0PwQTvlwpW8SwlbSaaWk/E6YzjO1mMXXnj6zTuFhHhtc3eFK0TsQmMUeXMLj0R9XACI0E4cjmznHdDisxtAj8tyu4+j9VG6z8X97h0hyVyd9E3r8NHsrzbiejiwHJCBavUgqgKeukr+AJaOrPRE7F2IlJEJAsoixK5MB3HPiKisbBg90QUdCL2gSLiQGlvqrrKY123aW4HiO8E6+xVEzidWUr3VD9X7IkY4Dd57gp/8HW+uGALmb7lkXZPxFGCYyJbm65ztRkHU4g3HaKfy/Blp/0Cacp+7RYIG9ETMYu8+KXvh9070LdMO2XQGaClM1ul/c6VAVpPRLWtKZ2IZA3mwph8g1U67jOzyAsqqtdfgbaXtNBEqb73qLNFglsjIsZOZ55PMc5Ros+pPiskMlXObIuIkY+rDYzRypmFeznzSJUzj00RsajFPBmth2W176U6rvp1HaF0diI2pfP5CNNcORGZzky6kfr5o5yI6nx3WSQwnIgdwSp0Ih42FBEHSme/LN905s7yqbjOh7Umzr6OjpFWxxd7DLFLwgC/3lJGKrIKVkng2rMn8L5lhG06M3sikjSEPle99yOQK3sZnOZA+HTmTrEtYTlzk6ach2nDob9fSdK0rSRbX7FF74mYwllJNh/SOrcyz8VKeyED8HcO994HrZxZDV1Z7QLLRdl70dxwIurlzB4Jwi50iW16PzJXJ6JdztyIA7HuDTuciG1PxP5Cx6h2IuZ2T0RVzlxEEn0bJ6ItthQOTsTqNco1J2JROxHz4lCAnSWDRHcOKodxI9A7nAeyoyeiXs7M6eRhQxFxoEhrIqZ/7Z3O3JFyGWvS0nVzpw7R97jG2mw89kpEl8vTZ/Ks3+iq7bSu0fh929R75OtcnS5wIrInIolFd9pvgt6BnW5zOO+HGlu7UoxjtniYK0307W/W8X6lcFi2Ts/60deJ2PU5TJBmPF9+bv689/ZU8/08YzozOSzUKdSIbR6ObKD7Xtd3MaMvstCDVWonouaW6e0CKyRGKhW3I505XrDK4oToUc9Qg6lRot2dzoxIPRHR2RPRvZxZvVeLglViHZeYK2du36u+w3LV01ya5cy1E3FUspyZLKArCKU5D1yciLooyWAVHygiDpSupvvBeksldOB0BsZ47oN63kjbZuy+iIU1cQb8RF/9OXbD+5iuDvtz6Ctkq95YkyZYhT0RSVyWxbGnTuO8Y6HILZ15XpTMkowZ1eNcGJPn+J667Nd2Ivr3bases9RituWI9T0u9Z6MMtGM70xnJmthpyn7J5+j3l46ERFNOnMrjqkAktypH93a5cyxRMROJ6ImtvUKVjFKtM3prBJcXXqmudB1XJmrE1FKTUS0glVUT7hovR4L4++qz0suit4l9YWeEA4A+RgFg1XIOhifdRWYpERtl/O7ozyawSpujNb/L2QzYq/MAiHTmee3GevGSq18dfZE9A1WGbUX/+UoZ64efUoTgfbeapRk4lzvQ9Mvy2+Su6gnIp2IJBZNc3DNuRx9golu56BfOnP1qI9BSZyIdu9Az6COpmdfcidi93G5jsdrpzPHFLPt4/Ib45uSt6ztBUcnIlkL2dzvhlmsXIZ7XdmIiNr4nteCH8re96gLXXvReyJWO24EkHgEq3SGxQDRA2NEVzmzqxOxmDZfjsYrxq9UObOTA8sFablXGydi0VtsmZUSY+ihFuPGiUgRkSzC6P/ZBKtU54FwOA8MUbLp9an3ZeV88nChE3GgdE0wvFOMO5wPuUr8jeQUCD1xBtobxrG2khnbidjlHPUR3PRBMLcEvKRhAp69ippy5iadOf6kmWxt1hRvYgarNM7B9mc+IVNd5bGxwwSAxUmrrm0YusZW396sTvth9Sn2TmfuqAxog6YiljNbnxvVWth1jNediKqcmT1vyVosEugBt4WCznZAkatu2p6IC5yIDi6wkehwIqqwlqROxPp+rmcy6rRYXM6svncRGZyQHY7IxuXZc/wqWxExt4JVVJl2rGAVVc5cdgVQOLhhJ7qIqPVEHLOcmSxAdJUz+ziNZdsqou2BQSeiCxQRB8rajaHdttkVrDLynOD1JfTEWd+mfuMZ29mm5nxmKWH9O4/SRGA5eiK2E+daGA0UrEInIolNO160P0vh2Osat3yEqa6WCr49TF1YJAh49/LtLE103s3ezLuyAzkR9c9hwveraVnhKbbMjJ6ILGcm69OKftWjLqz73D8ZY2Fk97KUSrzR0n5VKamDgGO49jQRUUQvZ+5wDmatw7LP+zUrJPJ10pljOfY605lVObOPE9HqiajK21XPzA2nfv2aHpOaE9ElnbnpywkA+RhlVh1fpgmnhOhII03ZDlZxuImrz51FCfGcTx4+FBEHSpdr0DtYRc5PnmM7wbpK7vzLtKvHsZbOHHsMKbteW+F+06rfiAlrMh5zQlZapYT+TkTTNdoeE50qJA5r9YaN6ZiSHWOhXznz/HH5XjNcsMW2UCJi13UrxYJKqP6wnanTy+A2DxTgNspEI9xwfCdrYZ8LurDuNRbq98+R73XVxLnUpmmqnDmHZz+6jlLiDHEdezKb34cRil5hytOyRCa6nYiqJ6KIdFxd5cxZ5ujy1IIf5pyIqowzunPU6ono5ETUypmzUaXS53HLzsnmQyo3rO4c9EhnVi7erpYKDFbpB0XEgdI9wQgzaRGdTsRYIuL8cW1EOnPfGzRf1hYm3Mtx9JvgccIE2ebm3nOCuTpT5cxheiwS0pdlSKkHdOegLvrBeT/WdprHOy5pLaj4lh6vGQgWUZuy3U2+Y5cdQFNtM76YbfecVG5I92CVtieiuibHbBNANh+LWiDov+tDVzlzqp6IUpum5R7BKjNdRMzadvi6uzHG5Hm9YJU+79d0tlZPxETlzHpPRE2Y6EWxCgCYytyYlwCt8CGjpTNbgoveE7HnYc1KibGo9zuvxFGR1cExsVK0yeZDzjsH2/T1/vc6aoFmoROR5cyHDUXEgWL3X9K/dnbsqclCQudDZ8Nrz1LCJp1ZcyLGL2eeFwR8mv837ptlabrfhLv4TZynVrBK05OTIiKJxJoulQQ9EUOVM3f2vE0hjlpjYVtGGGZ7gL973QVbmMg8XdldZdqjyD2KAU30tRaKXMdksyeiKmemE5EsxnYv6+eEjxNRv8+M3bKi6YnYUc5ciW39tlclGdeCWkc6c7TJc5OCsyBYpceBmcLo4mCVGKYA0SFmNiJiz5LLclaJiDPkRoAboIuIcYNVpBKeMw83bCExVp/BfGw8CpYzkwV0uXxRpzO7LBIURSVY6ws0jRNRSJS83zhsKCIOFPumSv/au5F7QgdOc3MXcKJbNpMWPVjFdQ/dCN38f+1+WRFL+KyJru/EWU0mJ0xnJolQAk3KcRCYd+wBfmN8K0q1P0ux8LConNnVXdc1tqYQR+398HciLhY6Ujhi1TXZd1FP74morlkUEclatD3A50VEl1Oha+HBN5iwNx1ORCNB1KEfXdM/0OjbpzvLYohtyomoDVwqWAXSIZ25Q5SE6QKMclzKYZnNv7Z9eyLOZpWgNkVumBsAzT0Vq9dj7RCUVortyEF0NtKZawdiPqoeMzoRySIK6zMINOeZSxl84/IW82Or6za3KhQRB8ra5cxu21wr1CTWjZXa9y5x1LfXY5a1E7Lo5cxWSRjg1yR/reTOqD0RVcmdlRDtHqxilp7rk+bY7xnZmhQd55ZvYJDPfnS5l13GeLnGIlFcx54pjvlet7rG1iQOS6vk0ic4C+hO0256xCZwjs6VnwfoiTim05wcBu3CQ/Xom87c1Sog9pgh9QRRhSq5ExJFzwFxpicZdwSr5A5BGS40QQidbsii1/tlHJNVztyKo3EclqKrnFm4lTMX00MAaidiZouIqowzck9E24ko+ovORVm2IqIqZ26ciBQRyQI6ehjK2onoks4s7RJ9wDhvo7l8BwBFxIFSasKYwtchsJajI1qz6TX2weWwpJRGQIFv0rMrXcmoPj14uvq25Qn6B9r74RussmqVM+vuUboRSQw6Bf8UrQI620t4tEDoWHhqy7RjBpDA2I9Q7Sq6jiueICDnypnV2CVluPdLjYspAmPs1GnnhSL2RCQ9sft167qLy+fQXsiovvb7XPemnsiWHWW/AFD2nOgWejKu1hOx7XEXy7G3ONylbznztCi1cuYFTkQR+7jmg3D6ljPPpnVPRIyMe9xq+/VxxnJLNQKOKSKOHN2wdjmzqNOnhWQ5M1nAWkEoLsEqZUdfVjoRnaCIOFC6nIj+aZDmdoD4KZddbkifmzv9pciEaG5CYzdWbVwlgSa6a5WexxQ65l1Fnj0RrWCVXCv1oFuFxKBLvGnGwQS96DpDpnx6Ina4l2OeWnZIQtNjz1HIbB177c/yyOO8/mfU39bH+lDvV7Ool6A351yatuM+mOnM1Zu2ynJmsgBToK8ehRBtD3CP+6euEMFY+rya6JYd5cwAUBb9Js9V6W+HWy7XnYiOO9uD9YJVepUzl7LzmKqNtWXaMe4NO52ImSrT7in4aiLiOHk5s/V+aa7R3uE+hVbOXIuI2agWWulEJIsouoJQVDmzS7DKOk5EioiHDUXEgVJ2lP36uFSABY3cIwtTawuZ7o49oBLtfG48feh0eXr1N6seu5vuR0zutIQO34TDRT0RAYqIJA5rlZHGdSJ2uM2F+xjfHcbl14/QBTswxlfI7BJ9Y/cO1CfGzVjoGeTV9TlM0RPRduY2i18BeiKOE1yzyOZC/5h13he63D8Fvs90oVNE1Ce6PUXEwggh0UXESswZiUjpzF09DPVejz1O9ZneE9FyIma6uzHCokpb9jsv0PYtZy6LypVXIDeEbEALOIkkIiqnVxus0vZE7PtxKTrSmfP6MaNwQxbR6URU5cwuIqLqy9nt8o51bg0BiogDpTPF2NN9sQwN6u0kSKA9RrdE0vY5eSaam8bY7fXUPU5XfzMXYaLLiZpkgmndjHsHq9TPa9KZtc9iTBcY2bp0twqI68gGuvueek2cu0SpPK77BphPMfYNVuk6rtjlzPp1Ri2mG05Ej3Jm3WGpnHtReyIuWCjyXaw005k5tpNuyg6BXv/a5dxaK7Qq2kJRl1tGm+gWPSe601IiFx0iomjdcnF6ByqxTZucNL0e+zkRZ4UmjFo9EZHHTZ1uxVH9OuPYE1FLZ7Zp3FiRRDfRpDPX+9I4PGe9x/hZWc6VM+fj2pHoUJZKtgiNiNiRpuxSzlx0bE8bZyloHz4UEQdKZzmzp4DT5VSJ7UTs6h3oMyHUn5ML4eX+86EtZ25/FsJhaZTw1R+AqEmrc66iQOXMSkTUPggx3VJk6zLTRA5F2wcu3n40An1HorvL6dW1vTyBE3GunNmzjLB78cv83UbTWc7s6aJWY6ux8KTCuGK6za0x3kfIBtrPWp4JpjOTddFPHWHc77iPG2sFE0a7NwzuRNT6B4oOJyIiORHX6YnYZx9WjRJtK1hFaE7EmOnMou03mbs6EZtgldH8L6MHq1T7rl5Po4dmz3Oh6EhnzhonIkVEsoAuJ2L9tVP/wqZEXx+DMkjUJiI6EQ8biogDZa2y35AN6nPVyD3S7LkzNc8ngERfxc78eor5sJbL08dh2ZXOHDckwRSem2AV53RmJSK2pXNq2wxWITFY0wGYoOw3VMuKTve6VkocK/08dFBH9+sUN2TKaJvRISJ6JcgmbC9S7Ye5YOVbnaCL9E2wCsd2soBFTkSfyhs1jHdVhkQvZ17Q/L/sea2ZFt3lzNDccnEce+rF1cNd3AS/tcqZ9d59UXsi6v1F6q/7OxGrcuaZ6BIRYzsRVTmzKSL2TdIGVLCKWc6cjWpHIkVEsgCxRjlzsJ6IQDOGuGxzq0IRcaB0pjOHClZJGNZRrjFxdrn/0S+CuRDNscWaMCvawJium1b37RnvVR5/gjmfzuzpRKzF6smo/WCPONEkEenqRagctjEDLboWHnwE9bXEUddtumCXM+tliS7jcpNi3emwjF/OrD43uljrsh+d5ecJ3OYLg1Vcy5mNnojt9SJ2n2KyOTB7IrZfq2HMqaf0Ggsq0RYru0r4tK/7BqsUi0JItACSOMEqtbOto5y5r5A5K9coZ47dE7FjP1Q686iniChnqifivIioxDwRzYloJXrnvk5Es5x5NK57I7KElCxAlh3neFPO3P9zI5VgvSD5vK8jeitDEXGgdDkRhUepG9CdIBzbgdPllvFJZzbKmbWeiLHHj7WCVVxe27XSmVP2RPQVslcbJ6ImIiY4LrJ1Was8Nm6gRcdYGKAnYtdCBhC/ZYV6TfWycbcy7a7rVv27yIFgQDsWCiGCuM311h4pxsKF5cyOtwS6E3Gk9feYsl0F6WChE9HDld3Ve9v33qX3PtSf91LviycEZvW0TdbhG4fLrCgxgiUIaV+7OMtcaF1F3U7EPvswLSQy0SGMAo3gEM1h2YijWm81/XXuMX6pnoiFfUzQXrfIPRFhlTPnoug9xptOxFpEpBORrEOXE1GdZ6KnQA8AslgwZihhsmdv1q0MRcSBspZjT/99r22u5W6M1Sam2YcwN4t6j0UhdBExdjlz9WgExng5Eee3p1yAMZvU2xP4pmeXsxNxXkRMkYxLti5rlcdGdYB1tZfwSWdWY1DH2Fr9Pq5rT4mZmSFk9h8MW3G0/ZnPwpML+vvRdU32Cc8y3q8mzTjhGO95DdV7Ik60cT7mMZHNgxFa1OUcdBLo1fbmz61YY0ZTRmo57FSPxL59u2a6EzHrciLG6R2YyY590PsX9nh9p0XZXaINWMe18QsQWUc6c5Zr+9RD9CuLOlhFjDv+kEcvOAey0hKeNedq38qAznTmUV3WDDoRSTeiXBys4nQedJyr1cZUyFScsXAIUEQcKF0uBSMNsufgL6XsdDeOGlEojktAiWNmCl/9O4ebu/nSOfV3UpUztz/z6enTtb1x5Peqaz98V/PtnohAml6PZOvSXUYaf/GhazzOfJxtHc7GFE7E0hrjzcUvh+11ubI9FzNc9wGwXl8P0bf5HGrv/9gzydoFu81J5rmoYzgRtReL4Sqkiy6Xr/61T2uHUOeqC60TsVtE7FvOvDDJuHEBxklnVmKRyLuciP1Kqo2eiIvKmUUZpydix34Yx9hD9FXlzOVaPREjlTNntns101Kve76u06KccyLmqpwZdCKSBdgl9UDbAsHlPOgKVtG2X7V2oIh4OFBEHChdrhLdQdh38Nf/e1c5cyyXQFeZSYh05rwREUX9d7x2szdruUpckjZl1/aSNt1XE8zq587BKrPqeRPDicieiCQehSZyKGKPg8B88jng5wzvcnmPtItGtMmzNYHXX2c3V9H86+QjMLjQOptMd5NPT+FOt1SCsdD+HKr1HSc3bCmba+8oz4z3LKaDnmwepCHQd1Wo9N9m9wJN9Rjt3FrQ/L8REXse2ExPZ+4oZx6JWOnMtYiYBShnNvo82iJiW6YdNZ1Zczfl+jH2ciLWPRG7RES1/VjhD/XfaYNVqseRg+hs9kSsxEPVE3HEnohkAapVgFHOXAv0TuXMi5yITciUpBPxMKGIOFDshEvArzRtUd+Z2D2YijUclj5uDrvZffxy5rCCQFep4yiF0GEdlxIlpHR7vxonoh6swp6IJCJrnVtx+42qc6v9WYjWDl0ubyBmCIn62+bCDgCnBvlrpVjHdiLq+wC4L4AZgWAJHZbA/Ocm18JQ+jKzjksIoTnoOb6TefSPhX52+bQsWGuBRv/9hiI70pnR9kiUZU8nYrkgnVlEdiI2IuL8PmQ93W3T2RrlzHqJdBQnYt0T0RA6tPfOyYk43xNRpVpHK2e23y/NieiVzpxVTsSxEhFZzkwW0fTlDBOsstCJaASr9N/sVoQi4kDpbLqvT8Z6Dv76/zd6IkZ2PnStELfN6V22ZzkRPbblw1qhBl6lbsYEM4VLpXpUx+VTUg90B6uwJyKJSdcEsy3hTNEqoN0P9aVTCV9HeawR/hF5oUiN8d5OxDXSmWP3ecwtEdG1DF5/HfIOMTtm6W9h3Wv4tOHQzx91LHmCc4tsHtbtiehRzmyGFrX3HFH6Iion4sJy5p49EY1y5gXpzBFOsawJIOl2IvZNZ+5MnLa2GePesO31qN2b5m5ORFkcAgAUnT0RI5czN0m28z0R3ZyIVrBKIyLOuFBEOlkzWMWrJ+Iaie4sZz4sKCIOlLXENqB/iYd+PqVM/O2cwAfoHdj0c4pc5qZoJrpdbplAJXyxk7QBLWlVTTC1XoYur3FXT8QUvR7J1sUeM4C0yefhxozqUT8uffuxRPqmnFm5w32DVdZw5cde/LI0xFZ87umwNCoDtLu4JJ9Du5zZI6ncdiICrXjDYBXShdTOrc5FWJ+xsKOcGYh0fnVMnAGgaHri9XMiFsUMmVAHpouImvsmYk9E5PNCZi4kyh4LINOiXKOcOY0TURczM+eeiNV7290TMW6wipBWCbyR5t1vW7OixFhY5cyTleoRBfvekk5Eh3NQLUJkDuXM6lwUlntZODqitzIUEQdKZzmzhwtM//8pJ2NdE3gfp8xcz756s31Tx3zpLE30EDS7AmhSlDNL63NofAadRMTqOZMuJyInmSQC6mMWSrxzZa0WCC7jVyu2mT+PX/qLej/C9DfrcmXHdle2lQFhnIj667BsKeE+C3F6ubpym9NpTtbCvsdQqG9dzvGuberjR5SxUAWrLCpn7ulENNKcO9KZo4lttfhlCGzaMZY9xFHDXbkgnTlWT8TWYamJiPo+9RER63RmmS3uiRi7nBlWOfPIQXTuciKOR/UjiqbSiBAd2eUcbPoX9k8Jb/qJLnAvM1jl8KGIOFBsN0f1daBy5s5eYJHSmbuCVYT5u17bsxyAycqZm/KZ+RJJr8CYxOXMjZhplaYBbu/XdDZfzjxisAqJSJcbWn0GXXr2udKVwO4zZnSFMenfx+8f2LEPXq7s9mdZZNG3S/AF2mtp37HLKGfu6IkYM6m+uYYq52Du7gCbGfcZ1SN7IpK1aBcdzJ/7lNXbJfrV1373Lr1pRKLucmbZU0Qyyp87yplj9UTMVY+9jnRmwBI712FalMibVOTF/c1iLDC3PRHNBe6Z7J+mLcv1g1VEpGCVXKVpN05ElXrdX5yddYmIk8qRmAmJ6ZQJzaSDcn6hQBgO6n6bU8n3thOxFSbpRDxcKCIOlK5+WYB7cIiZgKdtL1k5c5h9WJ5y5nlx1KcsrKsHV8rwh6Zfli4iOtzYrXYEq9CpQmJSdDkAlyD5HPAMY1pwzYjtsiw79qM5rkDBKo0DMJLW1iX4Au5BKOsFnaX8HDbCqMN7pcrVR3Woir5d9kQkXZQdC7CAbzrzfMWL3ps1ioO5SWe2RESh0pl7Ci5ybSfiyCEowwVVzix0gUwTAPuIbdNStmXEc/3NWnF0GmHsEB2OyFwIFFBBUz3er8aJ2NETMXKwyqJyZlcn4shKZxZ5e4yrq4f8dpYMkuaz3lHO7NSGoe7zKewWCJHDmIYARcSB0tUvC3DvE6OfUCl7Inb2t/JIZ7ZFrnTlzNVjp7spUGPwFJOxReXigKMTUYmInYmknGSSjafoFPxTCPSY24+m57rTmDG/PSBdOXOocvEuV3brAIwzZnSVaAPuLSv097fTsRnVbW5ek33eKyU8hroOkuHT5Vyuvnf/HMqO81X/TEYR6Zt0ZtMt0zgRe5YzF7o4Z4SaaD3uIhxWU/a70Il4+GPyrNDSmed6ItbtECCbCpaNpA1W0YUOxyCcYnFPRK9ACQdUsMp8OnM/J6KUErNSYmKlM0MTSmerU/8dJsNDhft0BKu4CH6iCSFYEMYk4vSHHQIUEQfKujdWvZ0P1eOi5tXRGtSv1d/KYRfm0pmF+7Z86CxNDFCOo79O46bULaLQYbmK9LRXl5J69ZQReyKSRKzVlzVuq4COFggeTuq2b5/583TlzF0ibf/JYJcru92e8272InSp+KJFPdXmIcUY3wSreCzqqeMad4zvU47vpIMuwQ8I0ytbP12FEF59FvvvhHLf2E7EeuLb04koykXlzKoPWBz3TeNE7HBDAoDs40QsJHKxoL+ZaAWBGGNHU868wInYS/QtKjGt04kYuZy5eb9yU0Qc9RQR1X+1y5mhORGnUzoRyTyt6KeJ6rmPE7E7WAUMVukNRcSB0jURA9xLPBZtL7ZLoOgoM/Hpb7WonDn2KkR3mnL9uwCBMdXXKZru13+7qzSx537oyW16OnOKMm2ydWkF+vZnI48JqwvlAhHJL5F0fmwF/AKeXFjLRe0i+nWJrT6vkwuhQ2vWX9SL2BPREmm9nIid/UY5vpPFLLo39Wpzs6BEeuTx2e6NSmeGvxNRSrlGsErb4y5OOnM1NuUjTSATuhOxj4hYQqieiGsEq6z2dG260DgRrZ6ITuXMpRIRl8GJqByWyjnYOsD6fFzU/fvISmfWBeTZbNVrX8kwEZgvZ860dOY+Q3xZymYMEvb51biX6UQ8XCgiDpR2krFgQuhYzryo1C1eOjPm9iNEOXNTbqv6EKYSEQMlba6VzhxzMiY7Js+uE2f9dehyqrAnIonBWs62aOPggmCN3GMRRAn+ixaeornN1X50iKNugTHmNvSvYwWQKE0vVL/JxYt6CcuZ6yHZ573SeyIqmrAYtqsgHeiCuo5aZ3QKY7LCghRRe2bX4o3dE1GqiXQPEako2xRjCWGVvLg5y1zJu1xAWj/Dskewyqxcq5y5Fbums3jiqJHOrDkR+5UzVyJimU06/lAtIiKyE3EunblfEI/6bE1sJ6IQmNWv0XSVIiLpoJwfM4SWpNxn3JppY+F8sIqeVO+zw1sHiogDpSuREnBfne1yhwDujeFd6SpnDprOXD/G7onY7ZYxf9eHzgTZXJWFxUzuXFz62VtE1PZbn2QqQZFOFRKDrs907HFwUTmrj/umK7RK336sY+tcePCYvBcd18JWbHXcyZ50Cc/VPrmVaS9a1FNtHmIuqNgirc9nsKsnYs6eiGQN1q+6cVl4COscdkGVH5d2ObMSpXqJbbJJ2cWc+0abOEd0Iho9EQEUynHZQ2ybFlITEddyIsYIVpl3RGYCbTlzDyeiqJ2Ic++V9rMskhNxLk3bEFr6iTdARzkzgALVNgs6EUkHbbiPnqiqlTP3+BwWhhNxcaI77zcOD4qIA2XdPjF9nQ8LVmZjOzq6glXUpEXK/uKfLQiIxtXovau96ApW8XMVzb9OSVwqa/Uj63lcel+bLldRTHGUbF3U53bUca7Gckvp5/DIWFBxF8eWxd3WlaYcIlilu8di3BLtxaKE4/asO7gmnTli/0B7jPf5vHT1REyROE02D4sEP3Uv5+REXHT/HNWJqEQ/c6KrnIl9RCndfTNf9ttOnGO041BiZpab/f7UcZWyx3EVZSMIzA2GQgtWiXBvqEQ9YZUzt8EqfdKZldC2BOXMsD6HtXgzFgXKHn0Z1TljpzMDwKwWkGdTiohkHiUiCr2cOW/7F/aZI+vu5XknojYWspz5sKCIOFC6nG2AT7DKAudD7MlYV7CKtk++x6VKYGIPIF191nwcGGttL4VLJcQEXt0ITvLM+FyzZxaJSVEsdteWDgsZLujncBbIvdwltlXbjC24YW4/fPZhrTCuWG0r1m0v0lN8Vv99cel5Ore5z+JXV09EBmf9f+y9ebgkV30e/FZVb3effUajfd8RIBYJjMAYQwi2sU0SnM9rgpc42Elw4jjkI3782XFw7MR+smDsJMTYsTFeYmJDwIBZZIwkFgFCSEK7NJoZzT733pm7dHct3x91fqdOd9c5dU51nep7e877PHqupm/f7q7uqtPnvOddHFQoIvzKDFtZJuLg7UFQ/tw2BtmZhzMRebGKWYuxLysgqbmdmRbwfiA7LjMlYjOHlEqfQFAi1tHOnENMeKKd2eC4vFherDKpTETKoBsowTE5B9l9W95QOzOAyCMlomtndshBTgQCtzN7ZgrqMCpWIvqI3aalJhyJOKWQ2ZnLLjJlkyqfK3BqUqnkHJe4mDd9GcNlApMuVqnKzpzkKREnkC2VZ/0sS2TTIrIRyBbObtB3sI88dW1DUEHUQbaJipF8JWKZMSP9WVWDcBmIBGwVOapAvip7UkpE+Xtr9nh55yCQFU7VvakHZO+vP8Z4TI6GgUzEMZq5HaYfeXMnoBo788j1WuN8l0ii4UzEmNt+q1IiUsZdXe3MRCIOKRFpOWpkZ44ze+ww4SZmItZoZx4mJsoU4XASMZCTiH5N7czcBs/tzIIaLIm0N02lmYjI7MxOieiQBy9PlS0WqxhcCupMxHrHjGmAIxGnFFz5ANnurKmdOX9S1ahzZxYZkZS3ICzzOjL1DQYet24+ir+/OTbtsVRFOYuxOhUdeTa+ssrBfk7ofvpvl5nlUB9ylW0CsV3HAnNAiZiniByLbBu8vU7VnviyqyL9eO5tBRsZZRHH6vfWdGNH2qTNxsJ+TWP8QMHPUDtzqXNQkeXrNokc8pCpcgdvHysfVqJuHOcxzV8EkYj5dubYQIkWRjplAvW0M2d25iFylB1nYnJccZKbsZc+QVYA0qthPPQl2YxxiWIVL5YQoxCUiDUVqwS8WGVUiRgg0m5opnVHc7idGUDE3jOXieiQB25nFjNCvaxYxWTcSkumiPAfzofNlIiORNSDIxGnFLRrP0z6eSUXT1LLSO3NnZbszDwTcfD2uhApjqtUDhipiiaovgEKrIQllYjNocmnUyI61AlV3ihQkxIxJ5dRfE3lcsCK7Mz2J1XxgBIxZywsRQjIxyCgHNll/hrSn6Ok33ibesPEyaTyKwGMtjOPYWduDGQiuk0iBzmk4xb753hj4eDtfBO+hvUlV99IilU8o0zEuLCApL52ZrLH5mciJiaFMVGcS0oBGLAm1kEIZMclK8IxL1bxcpSI9Hl5NSkRR6yfAvHSMCjjyZSI9HllxxYzO3Ps7MwOOfApJ1WiRDQr+BFyVIcb3QVi0sWn6MGRiFMK+s4cXmSK2V1mj5evfKAJfpLUsxjLy+0S546mE8aI72IPqSjqViKScrQiC18eedecQHOnKt/M9HXQRHCYRHR2N4c6oVK2AfUqEYfH40xhZ/6Ycc7YCpS33JaBONZ5OZEVVRerAPV8XkUEremENW/TaeDxat7UE597HKKFh++7TEQHTWS5y4O3V6GIlVmka8lSpUzEESUiU+wZZSIq7MxUQOLVUCYQx/A9NnY18u3MiYFNO81EJCXicCYiWROTWjIRPeSopSC2MxsoETlpIi9WCWBQ1DIGqJ3ZD/KViLrrE97O7I0qR0mFGjk7s0MOcpWIPhUnmY1bkU7JlOeUiLpwJOKUgisRJflxxsoHSQbTpBZjValKhu1T3M5cM4tYpWIPUOeA1ZqJqHgdpucgDerDmYjO7uZQJ/Ku1dqViDlki/iayiwG6WXLcsDqIOnFlx3kKD3LjMuqsRWoR3VepBw0VyKCPZ5kLKxpAjygRBzaiCvzPUNjfN615ZSIDnmQZSKOo8rm+bATVGVnmYj5dmYY2n55tt2IhS9TItonETPiyxspVmGvy6CdeSATccTOTIRAVE87MycR823aJu3MPlMiImiP/i6gYpW67Mx0XOz9FXb3GgYEDo3feUU49NlHkVMiOoyCE/TimMFVg7GR6CeMEwSykimuXk5qi4TZ7nAk4pQilCwy6Z9lbb+yRVCZxyyDvMwkcaJXtliFHmPiduYKyDYgv52ZL8YmnIlY2s5Mk5AROzNTWLpB36EG5DXI+r7Hx446xkH+GkYWuenPccaMkUbSGpWIMjvzOGUduWPrGBEYZSDNWCtZ1CBTNtZt/RW5lOGNuDLni0qJ2HdKc4ccZPmgg7ePo0QsjnYwfkhzEEk0ZLnLlIgmJGKxnTlAbP+4BOJzRIlIx2l0XAololdvSQK3SA6RtLxYxWD8IvumF4wqEVFzsQonRxvstXgeEiFHU/e7hjuJcj6v2Cc7s1MiOgwiSRK+oeJVYGeO4kS4VuXFKnWKbbYzHIk4pYiki8xyEyuZnXlQiWj/ossjEf0xiMzsuDDwuJMqVvFySMSq7My0wOxPOhOxZL5ZP6e5U/y3UyI61IFIWvBTn2JKpjQfpzCkqEG4jvFdJBHFr65x3ttMqZTdJh5jPYUxEtIvGO/7eNJ25rxilXHK1rJMROF7q+bGaYfthSKCvqoxA6g3V9qjYpXhtl8iAU0yEQfszEPLPoFEtO7AEV6z78ts2gbtzGGc2/abPkFGdPVC+58XLyAZVlhyEtFEiUgkYmv0d0wR6NdcrOIPWElF9are42RKxFGrdpaJWI9F22H7IE6QXwolEH4mc42BsXBEiZheqz5iJ0rRhCMRpxTZImPwIy5r8ZBN1OovFEh/SsnRsjbtYTtzzUpEOq5GBWSb+HgiKTmJxVheoUDZiXi/oFjFZSI61AEi4WVW4no2U6B8DXbUN/WN78OvY6yxMCfDcoBErGGyGEnUUmMrEYcerzlJO/OIErG8GrYhHFjDKc0dFEgk45Y/xoYwjwuQRjvUQCJKilXKKvZkSrlsMR5Z31ARLb0yJaKJYq8fx4I9VmJnRoxeHcUqoGIVCYlo0M7MiyRylIgeVyLqP944yNqZR1VggadPPHMnUY6dOfHSzy6OnBLRYRCDbcqjdmbTVnm1ElFsdHfrSR04EnFKwSfjkkzEcVuMhx8PqEf9IFVglG6dHpyA0sPWnYkY5rRp+2Ms3lX26LoWmIBYapDdVr6dmYpVnBLRYXIg0ikYJrNrXGDSeCFV34yhApON8XVmPQKDYwZxSuWa6nPU697o720iyRmPgfIKy8KilpqLVarK8nWZiA6m4BumQ7fTNKFcPmz+9TpOwZMpvIJiFRMSMYpjNGQ5YHzhbGYLLINISSKaH1cYJUJRR36xSgMx+jUUq3Db7xDxR+SoSTszZSJ6jVElIikdfdgnEZMkEezMwufFzpkmQuN25obSzuwyER0GIZJ+g8Uq2SaBybjVH4h2GN7VFezMbtNSC45EnFIULQjNg9xHySAgVbrVSbxlio7BF1I2j2y4ndnjSsQxXmQJqLIey4xlue3MpOioVYk4OhkvS46SErExTN6QwtIN+g4a2OxH+NcfehCfffREqb8nAq85QStpLFEicvVNiTVTyJW+k7Np57X9pq+BFmLl1W0i4Vb39xZ9HiPlDyXfW94QPuFMxLzYlHHIdGU7syMRHXIgsx6PswkrywDnmbN1nItJvp2Zk20GSrR+VKy+CRDxzQ5biAS7qh/IjkuPbEuSBGGc5JJSAISShHoyEXMVe8iUiCZfykGisjNnWXC2kSQpCQsAgXBcXgkLPJ83YVRlSRmLiStWcRhClGT244ExQ2hSNpnvKtuZhTHDZSLqwZGIUwpZJmLZ0PO8ll1Co0ZyKpYc17jkKP194JV7nHGRt3jK7Mfmg1luK/IEWozzmhPLKgddJqJDFfibx0/hA184hPd85olSfy/boCFyu44FJlciShTZpfLouNK3mgiMMhDfOi9n48HY9ivcf+TzIsKthuOKJKREo6SySZZRTN8ZdSyagXxbNd/UqTgTsU4FvcP2AZ1mI6rcMcatvDgYIIsHqsfOLClWoWWbAYk4sHCWKhH1SzLKImEKu34SjMzhTW3atKncktqZa7QmJpnl0h9SD8Zg7cwmSkRGIvrDxwTwwdZPYvukb5IIWY+jSsSGgQU+y0Sk5mnhfeIkorMzOwxiQIno5dmZzTMRfQ0lYq8G9fI0wJGIU4qi4HVj+1ROrtS4j1kGsmypoKSCcJhso8e1/eUsIkmS3OzAsYLB+eef3TYJW1iUc96UJTqIOGk1Bj/8utU3Dtsb57vpBH2zX26SwCfDkmzOWjZTktFNB2A822+PlL5Dg2udWaqJTAFUUjWYV/xBoMOsw7bC7cyy72PD1yB7n+oe4+McRSQf3yvKRHRKRAcVZJmIZfNhZZEKQDYO1XEuZpmIw6SfeSZiP4o5GSRtJPUS67nSpESM4Y+MhaZ25lFlW76d2a+DEBBec2O4WIWyHg0yETlx1xjNRCRiMfBi646pWFCBDRTGDDR66yoREwBim3ab/y5TItaT8+iwfSBugPiyc9A4E5F2iRTFKm6+oQVHIk4pZJmI3MJVsoBkeGcWqE8JliQJ33WWZtUY28IGyTZvDOKuLMTnyrNxlZnX5SlVRLK3LpI0b7E7brGKXInodo4cirHRTyeqZdVaebltQL0EDhFfMiVikphvhPBF2UiObn0kPd9MkSiATL9jBos/Bn9Xp+pcrmwqp5bqSVSjk2pn9gdUoyj9GvIzEd0mkYMc2bU1eHs2JzR9PLl6uazjpQw85GciUpOtWSaiwsIn/DuJ7LbjxowkCnNIRBi2M/OiPSmJmGU9WldmC6rQYfUgL4wxUI4GSarW8xvtkd8RkWKaBVcGcSwWxozm0TUQQfdSiKIka9IGAEGxmfhUrOLszA6DiOIEjVw1rFCsYnAdhGImojTaIXbOB004EnFKkVfUAZRvH87C6Ud/F4xhuzV6DQprWvXtzKVfpjHCgYVuDuk3TpmAMLNuCqvo2haZBWSmCcKCTEQXhOugg41eOiEpOwHPlIj5Y2stBVMyJaJwvRuT9KGEmPLKPV4ZyAtD2GsoOb6nj1HNxlMZ5BVMAeXzA0lZM6zKps9uouO78NkZK0fzYj2cEtFBAemYUdLOLItUEJ+j1nbmkdwucztzPyq2MwNAYmC5LYOIFWdEyLEzG7YO8/gNKlYZbp0mVZEXc8LRGmKxMGb8duaAvQd+Y9TO7PnlbJxlECUZgRPkFKsEBhb4ME7QhkASCkpE/tk5EtFhCHEisR8PXAf6jzfQzjwyFjJyvI4xY0rgSMQphSwTsSyBI2uXBOqb5IuTwWEFTtnJ3XDo/jiNfmUhPlcjZzFW5n3NywkKgvIEQ1nkKYvKKxHV7cxOqeKgA1Iilj1faHIRSG2/9ncw88pCgMFx0XTx3Jeo1+tUImak1ODtZYtVxLFzksrRvBbj9N/l3ltSIrYkSsS61OZ5xyVakU3Pwby80bo2KR22J6QRN2XtzOI8czguIKhRiSjLRGQLX89QicgtfBL1DaCvAiwLygWM4I/M4U3tzFkmolqJGNSgRBTJ12CknZkpLDULY4CsWMUvKFaxrkQUCZwcK2nD07eShnGMlkgiiipLUpjFjkR0GEQoU1ELJSgm10EoKBtVxSp15UpvdzgScUpRlIlorESUtEuKj2lbCSauIUYLY9Kf5e3Mw0rE+gipASViDtk2ViNpjqJj+DltIq+deXwScXjhXH/rtMP2xSbZmUuSErKoiOy8HuPFVfQaAGjbjAihlJhiv68lEzH9KSNHjcuYhCyspi8h3Gok26QKy4qUiOIYX6/CctTOXOY1dNlxtZvZg9AxOWWAQx6y/OfBa4FOybLuFCBnHPLqmesC8kzErMVY/4smJXDUZBsAxJbtzGSXjvKWnoa2Xz4fLMhEDBBZz0QUW6cbI8UqfJDXfjxqnM5TIpLSMaghty0WCJdgwM6cKRF11ydRnAyeg+K15ZSIDhLEcQLfy1EOcku9mSI3ihM0PFkZU33X1rTAkYhTiryWQ0BU7Jk9noyUBMpbskwRKaxpZcnR4UWrx0nEsq/SHOKXsLgIHMdul2dnHiARa27vHJgvlLQZ8XN6WAHmlIgOBuB25pILweKW8PqUiCNK85J25ijOyp1G4gJKqgDLQG5NRKnXICrbqlKvl0Es2YQrrUQksm2YRBQ+u1ps9UOZwun/lycyOTkqqF7qVMI6bD/wa1wWFTCGnVlahFSjEnE4E7GMnTmMEjnZJjx+HNolcWJOIgYjv+MKS107M8WKyNqZhfZW2+3MoUAi+kE+6audiShktgXNUSUikZR1NMjGcYLAY9dXYzSPrmFQahFGCVoeNTMPZj3yrDtHIjoMQZrnKihyTdb9/SgWlIiSCASnRNSGIxGnFNnkPl99YboYk7VBAkImnW0SUaLYE/9t+hJG2pknYGeWWe7GIcfy2pmDCSgR88jnsvb3UGJnDmokbxy2P9a5ErHcNZDXIAvUS3bk2T6BIRWY4cSKMHp9DT6nTeRtOqSvgb23pgUkYf6YAWTjUB3DRqbIHry97HvL7cwKJWKdn1ee0hww/7y6YXptikrEpstEdFAglo7H49uZh8ehRsnHLANuZx46LlPbL5DOjXh24Ihiz+dqubBvmUQMFUpE30xhOZKJOMFiFU6OJt5IOzMvVtFVeQqW3iCnWMUTFJbkqrCFSHjNXpCvRNSd7wwoERsSNaxlO73D9sOAndkbtTObFgyJRS0jJKKgRHQkoh4ciTilyHZnJeqLksUqw+QdIORVWSbeYgnZJv573HZmepwaOUR+XL43qFQp+1kB+XZmz/NKv09lwdu0c1unTRfO+eraZlDvMTlsb2yOWawiU3nXSrbJSMSSpRaDJOIwMVXP+J4+R/qzKtsvV6sMB6ZBtEjbnyzK7czl3ttMsZdPnADllbYmqLpYJU9h6TIRHVSQb6iUc92IHJbUzlwLiUh25qGMPd9MsQekrzdTIo5aZGO2IA8tK8EoEzHOtTMTOapHtqUbKYnCpi2qiiwLHASb9vB5yI9V9/OKRBJx9LMSiY6uZSViIp4P3qgKzKSdOS1W6aX/GFYiEqnoMhEdhjCQy+nn2Jm9yGj+lGYiUs7nsJ05O69dUaceHIk4pZAtMssuxmQZXOJz2L7oBopVRhQd5ezMw++TV6PFjSCbBI+ViSgpwsnypepZkNH76OUoVcorEfMVYC4zy0EHVKxS9hog0mnYzszVcjWch9Ixo6SdWRy7ZY3PdSrbRsf3cipPPmY0Rqc6Zb8zyoBe9oidueR729XIRKyTHM3bJEpfw/jH5dqZHVSQ5n+XzLcenGdWE5tTBqREHLbHwtD2CzArqYxsQ6aWi6zbmdkG3rBFG+bkaBgJiiJASQhYt/2GmU17+PuT25l1VXYCkeY3c0hEofyBlNu2MJCRmZuJqK8Ci8RcziElouenx+lZbgd32H5I7cw0gRptZ/YRG7lJUiWirNFdLFZx8w0dOBJxSpHlx1UzCaILatgyIj6H9aYwiWKPbivzGraCnbmoBKfM4klGItedH5hHZpYlR7mqaEQp5ZSIDvoYt505lIyF/Dyssahj+DWIijCTw6OSGc/LGTNqVPrKW4zTn2Vtv8Pfg4CY5Wv6Ks2RHdfg7bzttaJiFVFtXk8mYvpTHN89z8tKLcoWqzRcJqKDHoqViOXtzMPDxjg51abwGEEmIxHNilWKSMR0MR1ZtjMnjCCLczIRYWjTDuM4U1cC0kxEv45MRKbYy2+dZmO0LokoqP8aOZ9VnUrESFQiDpCIlIkYGbQzJ2hTJmKjM/A7r5E+tueUiA5DkGYiinZmYyUi2ZnlxSrOzqyHUiTie9/7XrzgBS/A4uIiFhcXceedd+JjH/sY//3m5ibe/va3Y/fu3Zifn8db3vIWHD9+fOAxDh06hDe96U2YnZ3Fvn378LM/+7MD4bQO44ETU9JilWry6ID6MukiyQITEDIRTSeMQxPQjGQt/TKNIS1JGCcTUaZ8qVnVkZelWTacXFZo4TIRHUxAxSplldNFyuFaMhGjURvp8OswC5vObL+jGzQ1koiyApKS4zu9T8MbD0C940a2AVaNypNnIg4TDKh3jM+zMwPlCfUeU9bkKhGdMsAhB1kmYv6YYV6sQu6J0XGozg1Ln5GEnp9vZzbKRIxiLTtzZNvOHCrszFyJqDce98WyGKAwEzGxuLnHFZaKwhhtJSL7DHpJgEaOgp4THV5sPRMRkfD4OaUWAeLy7cwCfPZvp0R0GEZKIuZkGJY4BwEgimLBzixXIrr1pB5KkYiXXHIJfuVXfgX3338/vvzlL+O1r30t3vzmN+Ohhx4CALzjHe/Ahz/8YfzJn/wJ7r77bhw9ehTf+73fy/8+iiK86U1vQq/Xwz333IPf/d3fxfvf/378/M//fDVH5VCciWg4CepLrKRAfROrSLIQA8oTU6NKxPSnzQmH7DVUZccZeMyht4o+v7oWZKrMrLLn4HB7rFMiOpiAJt5lJwmyDZU6bZdRkr9wBspdX6pNojqvr0I7c8mNB9XmVz3FKulPGdlWlRIRyIpI6rDVJzlKc6D8PKObl4noNokcFJBv6qQ/zUsE05+588waN1TI1uv7g8SUV8bOHCfyAhJkxGSsW/5REkSk5dmZTRWW/ShGS7QzS0oSfC9BkiRWP7MoTLP+8shRfpuhnTlEI3e9JSqw7CsRhfNhwErKCFpPv1glVcOSEnE4EzEltv3EkYgOg4gSSbGKoBo0ubTDOEHDk9iZ2TwzQOLszJpoFN9lFN/5nd858O9f/uVfxnvf+17cd999uOSSS/C+970PH/jAB/Da174WAPA7v/M7uPHGG3HffffhjjvuwCc+8Qk8/PDD+Ku/+ivs378fL3zhC/FLv/RL+Lmf+zn8wi/8AlqtHAm3gxGku7Mli0O4nVmp6LBtZx58PhGl25mHHpPmjXUSUlneZH5I/njtzJNV7eUVJZS1BBHx2ZK2M7tB36EYZGeOk/Q6yVPzqVCsRLR/bak2VHwfQGR2fckI+vTx6ls4yxbwZbN8+wol4jjFVaaQfV5+ybFLRSLSediv4zwk0rciVW5esYorznJQgcbb4fHYK7kJm12ro7/jY2EdmYhs4exJ2n51FXtAOndqy5pxAV7eEll2g8UR2ZkVSkRNNVqaiSgQo8PfhQL5GjBLc973WxUgJWL+cZkWq6THFCLIXesM2Jn7tlun08+rjwBN8f0VVWCal8KgEnGQRPQDsjM7EtFhEFEsKVbhZLq+pZ4erym1M7Pz2ov5xrqDGmOPqFEU4YMf/CDW1tZw55134v7770e/38frXvc6fp8bbrgBl112Ge69914AwL333otbb70V+/fv5/d5wxvegNXVVa5mHEa328Xq6urAfw5yhJKJVdmFEz1eM0/5UpcSUaJ6EF+D+a7z4ISxLBk5Drg1sUI7c6HNrKYDzMsCK6vYkhHZdWa2OWx/bAgWoDLEM51nowU/9dku+caDckOlhJ1ZoUSsU2EpI9uqUi+nt5X7zigD2edV3vZLduaczyuoL0NQRriUVWzlKxGZet6N7w45KMqUNl0LyjJvgfKxCmVAduZgRC3DCBcD1dZAqYVCiWjbzkxKxDhPiUgkIvTItn4UK9WVomqpgQj90KISkdqZvTwloqmdOVU19hGgmXMO1lusIrFpC5mIuvOMfhRn7cxDRLbP/m1yTjtcGBjIRMwpVjG1M4eiPVqSoxogckpETZQmER988EHMz8+j3W7jH/2jf4QPfehDuOmmm3Ds2DG0Wi3s2LFj4P779+/HsWPHAADHjh0bIBDp9/S7PLz73e/G0tIS/+/SSy8t+9IvCMjalMdVdCjbmeuyM+ctnMuq24YeM1Nq1jeAxBJrIv+sSryWWEK40gKzrgEyziEFyherqFtxXWaWgw42etmqsowil84zWWlRHaVMsoUzIMYg6D+eTnZgHQtnMY9MRNnND/p888i2OlunZUVn/HvLcOzimYg5SsQ6MwSlRThBuWuBFsVisYqLq3BQQVoiWFKJ2GPESW4EQlDTmJFk6huvMaRENMwOBIB+LOQHKopVYstKxCTKWoxHQDZtze/k/kDjdE6LsUC+2i5XIRt4fmFMOTtzH43c9ZaoArRuZw4lylH+GvTtzFGcoEWk71Cxih84O7NDPuI4QeDlKBEH7Mwm0T1isUo+Oe6KVfRRmkS8/vrr8bWvfQ1f+MIX8JM/+ZP44R/+YTz88MNVvrYBvPOd78TKygr/77nnnrP2XNMATo5J1G1lA+pzFR2MxLG9yJQtWIAs+69sZlabHRdvlayRRJS2C46Rv7NVlIh5Nr6ypLMsl7PJH88N+g7F2OhlE9Uyi0FOZk/QVq8iEctsqPAW4wluEgEKlWfJsbAXFsdw1KPYy39/x1ciKkjEWmz16c+ReUbJ5us8OzO3Z7tJvUMOZHOdshvLNGa0GqOEUG1KRLEhejgT0dQeC7LwFRNuse1iFWZXTXIUe3xBr3lcaTszKYrkLcZAVq5iC5lib/S4EsPWadHOnE8iinZm20pEIn3zScSGQTPuQCbi0OcVuExEBwlC0c7s5dmZY6MxPhLHjWE780Cxitu01EGpTEQAaLVauOaaawAAt99+O770pS/hP/2n/4S3vvWt6PV6WF5eHlAjHj9+HAcOHAAAHDhwAF/84hcHHo/am+k+w2i322i327m/cxhFZp8aHPzL2pn7kXzRUrsSMS8HrOTkbpiY8ksufsaBjBCgz64Uicj+RNb4PMlMxDLtsYA838xlIjroIkmSQTuzoVorjhN+Tg+PreNcr6ZQKhFLXF+hRnFWHRsrMpVn2WIVHsORR46OUVxlCmmO5pi231yioy61FOTnoV/ye6abk/XolIgOKsiViOlPY4I+GiWy+WPWlYkoEGm+xHJnpESM4kwFlku4pceaWM6kUykRTQtjBtqZh8kAYIBw8BHzDQobiEmxl2PT5iSi5nHFYQ8+gH4SYDbXzkzlD/UVq4zamTMlYql25qFilaCZnpOBIxEdhjBQrCJrCDeZ68aStmdAKFZxSkRdVJYyG8cxut0ubr/9djSbTXzqU5/iv3v00Udx6NAh3HnnnQCAO++8Ew8++CBOnDjB7/PJT34Si4uLuOmmm6p6SRc0inJijMk2iZVUvM12oUB2TKO/Kxt4ne06E4mY3l6nElG+EBv8vQl41uPQe9WouZ05zvnMyiqAQolaymUiOuiiFw0GgZuSHCIxM3wecuKkjkxEhSq7jIKZE/Q5i5Y6bb+y7EAe7WD43srUy+lj1qlEzN/UK11AorAzN2sks2WxGZliy+zxMiWioCKi7yw3vjvkIJtj5F9bVTafl81ZNIagWguGilVQop15sNQij0QkJaLtdmZSIuaQbX6mAtJBGMUF6sqhTESLH1oUSxR7MFcihmE3/Sm1M9fXzpxQEc6wclRUImpeX2GkUiIyEtEVqzgMIR4oVhFIP6F9PTaYaERxgqZXlIkYu0xETZRSIr7zne/EG9/4Rlx22WU4d+4cPvCBD+Czn/0sPv7xj2NpaQlve9vb8DM/8zPYtWsXFhcX8dM//dO48847cccddwAAXv/61+Omm27CD/7gD+JXf/VXcezYMbzrXe/C29/+dqc2rAiyTMTSxSpboZ1ZVaxSshBleJFJj1NnJmIh4VvitchUm5MqVhlQIpZU39D5NawqcpmIDrrY7A1ONkzPGZF0lJZk1KkAy1VlD95HB32J3Va8rY5MRGkhWMlNIlU7c50kYnGjtynRkU6Cle3MNeykZ1bS/Ndg+nk5JaKDKQrzRsuSiMoxw/K1JRCE3khuV7ps8w1IxAHVXg7h5rHHTKxnIsqLVTzezqxfrKJUV3peqtpLYuuZiKSwTHIUlomh/TzuZ5mIhcUqlu3M1NY9kvXIiczIoJ05RttjJOKwEpGdk5SxmNtK7XBBIowTNHLbmbNrw2TzIxSLWiSlVQFcO7MuSpGIJ06cwA/90A/h+eefx9LSEl7wghfg4x//OL79278dAPAbv/Eb8H0fb3nLW9DtdvGGN7wBv/mbv8n/PggCfOQjH8FPtbgETAABAABJREFU/uRP4s4778Tc3Bx++Id/GL/4i79YzVE5FGYimjfWyW1hdSnBVMUqpRWWdFwNykQsR0aOAxkhUEU7s2zRWpeqgzga8Twcd3I/aiN1i0wHPWwMTbrLEtmAQlVWp+1XkWFo8jJUxSqZErE+Uqoqgpa3M1dYxlUGcoVl2eNiCvoJk6PSYpWSr4EWxXmZiG6TyCEP0rnOmMUqeQR9bXE3A0rEfMudmRJRVO3lCDVqViLmFZBwElFTidgXCxLySEQgPa6oxzIR7Y0fvFglJ+vRVIkYhVk7s6pYpelF2LStRIzVduaGp9/OHMYJ2pJzkOzMlF0ZDBPnDhcsYpn9WPx/3bxRpN8XDR6DMDy2ZkpE53zQQykS8X3ve5/y951OB+95z3vwnve8R3qfyy+/HB/96EfLPL2DBmSLlrJ23Z5S0VGPEkxVrDL+rnP692WVmuNAZk0U1ZVJknCCUwcy1SaRwHXtsiQ5x9YoSWRmSkSXiehQDsMkovE5KIxxE1UiKlTZZcawvmqTqC4LH6ovmVK2TpfMTCsDedbjeN9bebltzRrtvzLF+7g27XZzVInoirMc8iCbP/klN4Tp2srNUaWi3VozEfNVYCaZiGGkbmemxXRisBgvA3p8VbGKLjkaxgV2ZiCzJ3p2MxETZTszZSLqPT+RiCGC3M0vUY3V61kuwpGRo4Jiy6idGRIlIpGIXoheFKPTdCSiQ4ooEZWIo3ZmwGzcGixkkher2BwvpgmVZSI6bC0UBZ5XlUcH1Ld4pkVsvp2Z3adkiHZWrJLeXq+dOd/CJ/7bfDG2NQg3et3iR1a+ITx/cl9XJqfD9sdGb4hENGTGiMjwvVFFdJ2KKdmYId5mlokozw70a7y+ZNmBZVuMVeRoUGsRTr4isqx6VSe3rZ7zUEbgDP5eB3Gc5Cos3SaRgwrSDfPSRDY7B3OvrZrGDGGsDRqDWg+vRLFKGCeC9TfHzhwQiWiXlAIpEXPtzGY27X5UkPMIDBSAWG1njsmmLbcfmxSrACmJmCscEJ6jZ91+LrMzUyaimRJRRiI2WDtzE5EjbxwGEGkoEWODLM1IaWcWlYjuPNSBIxGnFLJMxLIWD64CUwTv92sqVsmzM4/bzsyLVTjBVfplGoOTo5JJMFB+kSkqOoB6G2TF5xGPreyisC/J5ay7LMZh+6IqJeIwyQXUmJeFbMzIUyqUyVKVXVvic9RB4hRmBxoXq8iPi5e11HpcQ5s6JUtrVMUqdW6qyBTvdH2YnINiZllbUKLQZ+fiKhzyQGPy6KZO+tPYdaNqPq+rZCpR2ZnNi1UGVXujhBvPRLRdbBHJi1VMj6tfVKwCDLSt2s1ElGc9ctWloZ05hOyYsufo920rESXHFZRVIuafg15Adma7ZK/D9kNqP84hEYVzUjdHFRhSZQ+TiNR87sVuPakJRyJOKXhAvcRmVJZsy1Mithr1KB9UxSpVBe9PxM4sURWJBIFxthQF748QbvWqOuhtzCtWMZ3cc1WR5H1yShWHImwOk4iGY5ZMeSXeVo+NVK5EpEvNZMwIJdeW+Bx1NNbLjqv8+F7czlzPcUk29cZtkJ1g0RmQWUWHCZwyKrBuP1s8isfFx3c3qXfIgUzlWzYCQX1tpT+tl0yxRXGY+KMbRWzhq5sdCFAzrpxE9ImotJyJmCn2FJmImgrLQYu2jHBj6kbE6FtUuMWK1mlT+3kSpsRg5EnSxoTn6FknEdPHHzmuASWi3mP1I3mxCpGSDUToh26cd8gQxQkCL6dYRfh/02KVhszOLIwXNjcdpgmORJxCJEnCB3ZptlSF7cw02bItQ+dlMXkL3THbmVtDduY6FpYEqRLRG4NE7JMScfDLP1uQ1TNA0nk2QCKWtdSTGnZIfVNnkYDD9sb6sJ3ZUK2l2kwpa7ktA9mYAZQj6fuSvFHxOeogcWTZgY2SZJuqEKxOO3ORwtKU8MtrMSY0a1TuZZmIg7eXUXnSxpfnDX5ebnx3UEGaKT1m83le3mjZzQxjMDVeBH90jGeLZ5N25jAuaGcOMiuxzevMU5BtRCL6usUqcYymqp0Z4IRbo6Z2ZqUSUdfOHBGJKMkFFD6/qN8zeJXmKCpWCRBpfycPZCIOf14+2ZlDR944DGBAiSheE56HmFFYJgrqMIrR4KTkMIlI40XkNi014UjEKYQ4CZA2iBqO0xnZlqdEZCSi5cE/5gux0d+Nq+ighVeZZtNxERbkZQHm9moeUC8h3OpS7cXJ6CKzfCNp/vvkgvcddDGunVmWvwUIpNQWyUQ0uRxIpaEiR+vYWKHnkKmKzG2/CnK0hGKzLIramc2/t+QNsvSYNttICfJ2ZnMisyuUxYhZYHWr5x22F6TXVul2Zp2ogHqUiDFGlYje2CSi3M5svYCEF6vkKfaYwlK3WGVAXalWIgas9dcWuBIxb0lt2M4ch930p0yJGLSQgJ3b4abZCzVELLOfcyVirE2oh6KdudGRPJ6zMzsMIkrkGYY8g9QgH3Yg93W4BVwoVnHrST04EnEKIU62g4rsU30+URs9ZdosO6bb15/UlAG3EqrszCWzpUj54JWceI4D6UJMOE7TAY0+i+GJcN35Uhnxmx1L2c9KZk3MbIk12IwctjU2R4pVyqlhJ50dKFPfAOXU5jS+5Fn4yhJ4ZSBT7BGRZJzlq1COlh2HyqBYiWg2vvci+edVZyaitJ2ZCFqDz6srsZG6TSIHFWSZiOXnGbTxoBhba8pEjOGNjBmmtl8gHQd5sUpDbmdu2C62UJCIohpSB/0oFmyJsmIVKkqwa5OlTMQkj/gzLMIhO3PoyRqnPcQBswP37ZKIibSdOVNsVdHOzDMWLZPYDtsPsaIIhUj7xCSGIRRIxBE7c1as0o+SWgtWtysciTiFEAf1YcKtvJ1ZlYlYjxKRW2NzLXzpz7I5e7xYpUZ1CkFm4fN9L8s3K7mbPqxEbHCVSj1f1HmZWWXJlsxSP6xEzI6xzixLh+2HUSWiYTszL1aZLCml2lAps1HUl1xb4m11EPSy7EBOZBoXqyjyzWrMvy1qZzZ9a3th/vgO1Kvck2WEljkHszKwQYKhToWvw/ZDJFEvZ9e32eOpogLKtqkbg40XEfzRjXuvRCZikRJRKMroRvbEAColou8Z2pkj9TGlD5aRAvXYmXOW1IaFMWRnzrNG8/sEqZIv6W+YvExjJEVKRM+wnVlmPxfszE6J6CAiHGhnHjwP6RpJDMasAevzsJ2ZilWQ8Od2UMORiFOIASWirLGuJIGTZwujhYwYjG4DsWLhXHaHmHYnh4tV6uSilIQAb53Wf7wkSaQT4brzpfKUKmMXqwwrEQXCweVmOagwdjtzLN9MqVWJSGNGrlpm8D46yLIeJ6xE5BsqknHLdMxQKOjpM6yDnCo6LhMyO4xiTjrmWy7ra6uXlZ2VOWekZWCuOMtBgYzIHjxv6J+mapKsWGWUxCmbzWqMJLMzj2wu01zVxM4c6bUzB7CsBEsUBSSBabGKRjuzlykR7dq0w4HnG/idYSZiwklEiZ0ZyJSIzPpsC3RcI69FyETUVyLGaEuViOnn10DkMhEdBpBmIsrszOx6S/SViAOqxeF25qFcVpeLWAxHIk4hxAmOtLGupLItj0SkhUx3gkrEcds7uRKxxvwvgtKaWOK4Uhl2+v9kNSc0aYFZWzvz6CKzbC4jL1bx8xeZQH0KS4ftiY2q7Mw5pFTZqIgyCFUbKiXGMHof8u2x9Rd1yHJPjWM4KPO2UY3tuywKc9sMhi1xkaVSS9VDZqc/h7+Ty9jPeSZicziCo96NL4fthVBybY3dzqwoVrF+bcVZscrIhpVhAQmQvgdNWSMpMJRJZ+/YeLFKzvenz49LMxNRpWzjD0okYmJ1bphotE7r5rYlYY89lpxETFimoGc5EzFTjg7bmbNMRN1LIYySjESUKhHtnn8O2w9xIioRh+3MlDdqYGceUCLKW8eBtLzJQQ1HIk4haILjeTk5MSUnVioFDrczW86y0FHsma4Hh4tVJtPOLCcReb6VwReruMgcKVYhq1tNX9S0QBc/stK5nBJLvbh4cAtNBxU2h5SIprlxKjtznYqpvKxRQql2ZklpEZApeiaZHVhWDSnbeBCfo1bSt4JMRPF7Npf05aSb/QlwkRLR5JzJLNrDdmaXieggRyTJRCzdzhzJS4v4JqztzUqFEpFUgyZKxH6kaMYFBpRl9RSr5BBkvDBG7/l7ohJx2JbIH5Oy9uwWdiS8dVperKJrZ054O7PkmACu5POiejIRR+3MmcKzVDvzcLFKXZmcDtsOodjOLClWSUzmBrFA0g9zCUORCn13LhbCkYhTCK28LONMRPlijBerWL7gZAUkQHXFKtnip/TLNIaKRCyT2yUW3MisYXUsMIEs60s8tjKT+yRJpLlt4mM7y5uDCsN2ZtNd71CjFbmOa0tGSgHiWKj/eLxMQGGP3RrZgeON73mPWasSUTJ2mXxWtMjyPXXBTx2KDh5XUQGBI4vgoPPPFWc55KEoE9F4zFDkjdaV/z2gRJSRiCZKxChGW6XaE4oybJI4XImYq9ij49JtZ47V6kqAH2sLffQsjoecHB22RwJCJqLm+8pIxNzHIjRm0oeesJ25fDvzkJ3ZJzuzy0R0GEQcRQg8do4NKxENm88BAESMK67VwGN2ZjffKIQjEacQqoUut7oZjtMyFRggKhFttzOnP3OLVUqqZYaD9+mh62xlUpGIZchRseVyxGZWs52ZFn1+jp3Z5JjE+w4To57nlVYdOFxYWO8NKxFLKtvyijomkImYpxwss/HA80YrHFvLoFCxZ0r6KrIegzqzHiXfyWU2dVTFD+lz1G8/H357g7GUiPm5kYCb1DuMQjZ/stHO3K7JdUNKxChHiUjScJN25jhWNJICGdnmhVyJaQXsNSfDNkJkDdEBEq3NglCnWKWZkm0d9C23TlMm4vjFKqT+ixR2ZjRTJZ9vWYkobdMukYkYxjFanszOnJGSjkR0EJGI54OkWMXEzsxLkPJIRJ6hypSI7lwshCMRpxC0HqlqgQmIi2f5xMq6EpErLEd/V2bXOYqTkYB6v+Tu9TjQsjOXWIzlhu7X2NwJiHmP2W1lssjE15tLCLjwfQcNjCoRy9mZq7pWy0KmAEtvS3+aqLayuIq8a2vymYjjKuhz25lrtDPLjqsM0dEb2vgaRrPGMV5mZy6j8qRilWESsemKsxwUkOXDjtt8nnd9tWqa69IkPk5G25l9Q8UeAPgDJGIO4cbJth56ocVrjJNtowt4T2hS1hk3elGcKdtkJCKzzXbQs0sIqGzahnZmRKndcqT0QYDPSMQg7loVPPDCGEV2nO73ZxgplIiM2Pa9BL1+Hw4OhISuB2DkPKT4gMQg2sFTjEE0eW5QO7PL5yyEIxGnEGrLXfqzdEC9YmJlPROxYjuzOKkYbmeuc62iU5JQRomYZ8cpq+gpCzrPPOHYymTHifYhVR5dHU2rDtsXm2MqEUkxlm+PnTzZBpTbCMnUN3LFXr1KxGpa5XsKBX0Z23dZyI5rvE2inIZTiGO8/QMrsjObzDNk31uDSkSnDHAYRCTJ6y6b/91VXF88uqc/OSWiZ5gdCABeJJKI7dE7NGcBADPoWrVq8wV8TiySJxTG6HxmYSRkpcnszIwcnfG6tWQi5rUzw7RYhRG+sS8hRgF47Lja6NmNreB2ZpkSUd/OHMWqYpWM0An7PTg4EOJE3qZMxSqeiZ05VsQF8AxVp0TUhSMRpxBKe2xJJWJf0Upal8WDL1jyyLYy2YFhDolIJGuNSkRaaOUtdMsoLFWZPhPLRMyxMxsppYSJktpK6gZ9BzmGlYimaq3+VlEiJvmkVHpb+Q0VVXZgrTbt4ezAsoVgCnK0UVLdWAayQp4y31uZUipHko9ymzRlEUmUiGVabGXFKuKcwykRHYYhm++Ou/GQ5+Sg5vBJZiKWaWf2WJlAAm9UVQZwe6x12y/ZmXNUQH6QKRF1xuQwjtEsameuW4mYR44aKhGJ8M21WzL4rUw5umkxRoqyHkdUW5Sh6UXagovBYpV8JSIARI5EdBAwaGceIhHZeZgYkIheoshE5HZm1s7sRCmFcCTiFEKmekhvK7sYky8ya7MzJwqyjR2XyXpwUImY/v0k7MyhghwtF1AvbxekBVm/psUYvY95mYgmC0w6/3xPTeA4O7ODCkQi0rVhqtaKFJspkyDb8hytZTYeVGRbrS3GEpK2bAmKihzNGp8nV4STNSmXsDNLMhE7TVJL2c0oBrLv2xElYgnSV/a9JT60m9Q7DEMaFcD+aV6sIp8/kcXZ+rWlaGf2GeESGNiZiZhKgtZoIymQKRG9bj3ZgTkLeE84Lp1xoyfaY6WZiOlxddC3uz4hEqOKYhWVUooekpG+bfTtqmKpCGd4viMoEXXnBXEUouFR1tZQO7PQrh2Hzs7skCERVdTesJ2ZVL4mSkSNYhVQsYoTpRTBkYhTCGXo/pjZUnmZWS22g7gllIgl1Tdkt+UkYo1jBydHFZ+XCTHRlSg6AGHRWtNiLLO7ZbeVISW4ElaSA0a3uwwLBxU2mJ15sZNOIMyViHJ7bJ3lPpFio6iMTVdl+62XHFW3M5te3zRuqMjROhwr0uMao4BERiLOttJze61nn0SUfSeXUXnSYnhYQe95Xq0qX4ftBdkmbNkNYVUm4iSUiMNjl8fGfBMloh9Tzp7E9luTYo/UeElOAYkvLOB15t+bvUgoVpEQbqSw9OzafnXszKbtzNLPCpmdueP1+OaLFUiViFkmou7GnjKXU1DHhqFTIjpkIJVhDH8kBiFhFJZnUKzCz0OFEtF3xSracCTiFELLzlzS4pGrRGySEtF2O7P8uMo0iPZZgLQ4WZykElGlHC1lZ25OvoCEXrb4mZWxHvNMzpzPHqjXSuqwfbHJFCQLnXSCbnod6GzQ1Kpsy91QSX8aFRfxdmaF7XcLtDObjss0bqjKmLbGcUE7IL+IRJxrpxPh9Z7+xLosZIrYcYjs3BiOoL5ry2F7QRYHU3aTgMimvPMwUyLWk4mYp0T0GGFmkolIC+dEmh1Iir2eVTEAt/TmLOB5O7Onl7O31guL25kbpNjr8e8CK1C0TnPSV9fOXPRZAUCDMhFtKyxJtSVvZ9b9/vTjbvaPYTuz5yFi+XbOzuwgwpPlcqKcnRnUPp9H0rNrNWtnduvJIjgScQoRSiZVgLhwKveYucUqQT2ZiLImSPE2I7KNFs4NkUQcfK46EEsWYuLrKWULU+SA1bUYixR2ZpO1O73epmTh7DIRHXRAdub5djoJNo51UNiZ6ySyY+WGCkU7GJCINL43Jq1EzCdpS8dwKItwJpH1KC8N0T22rkIpBQhKxK59JaK0nZlvVuqPx10FOdqosbTIYXuhqIyprBIxb67bpqgA23PdiJSI3qhNu1QmoqTQgiAo9ro1ZAd6OWSbb1isstbVIBGpWAV2i1UQKZSInqmdmSzfKhIxJeE66PGNUSvgNu1hEpFlImp+VgAQDORyjpLIMVM7Rs7O7CAgpiiGPBKRbjOYZ+hkItLY6pxtxXAk4hQiqlilkiSJUoFTl8WDHn44f0m8rYwtTJws+iUIrnGhUiKWWTx1FUrEurMDVZmIZtZzOXmT3u6UiA7FIDvzArMzmy4sSLEXTNjOLFO2AeXU5jQWqrIet2M7s07r9CSLVQabhzWViAWZiHOt+pWIw9/JpZSIihiOuhX0DtsHsvnuuK6b3GKVRjbXNdmkMUXESKkI/sh3jccWv0Z25qSIRBTamWtQIqqaURuItTaD13oRWrxYpcim3be7PlEoLLmdWTPD0tPIRITQzmyT0CbCRWZnDqBfrMJJxKCdm8sZsceMI0ciOgggO7OCRPQS/bkOtz7nRSAIWZ8A0HeilEI4EnEKoWNnNlk4iZLe/ExEn9/PpjVMpUQsd1yjio5J2JlVJQllyFGVUiVgt9WRiZgkSRa8L3xkZXLAsuKHfDuzW2Q66KAqJWKerb5MSUZZ6OSoGl1fCqU53WZ74QwolIglWowBIcOyoo2nsihqkAX0v3P6XLGXo3oBMMfO7fMTVCLyTR2TTERlIZjbJHLIR2FUgCmJqFDEirfZJG9IiRjntDN7gVkmYpIkCGKhWCUPXLFn2c6skR3oIy60i/ejGL0wRoOIuaJiFc/2cUkUe8hIX10loqeRiUhKxLZnt1gliSWkr1isojnG83Nw2MpMz8WIysQpER1EkKVepUQ0KFYhwtHLtTMPZSJaVpxPAxyJOIVQZweaT6zEXcE8EkecWNnc7ZOpHoByNu285s7MzlzyRZaAqiSBZz0aLcbkio5mjWSb+BR5mYhlGknzLPqAs7s5FKMfxXxDhDIRTTNP1Pml/sB9bIJI9dyxcIwNlbzxfaaVjSO2bXxFhIC5ElFuTSxDdJWFLGJkLCWixM48iUzEkXbmEvMMWbGK+Hgu6NxhGNJMxJIbD6pN2HZtJKKgRBwmEX2zduYwTtD0mI1Ymh2YFXVYLVYh4jOHbCNiMUBc+N6uM0dBsZ25nsIYlRKRrNu6GZZ+UnBMQPZ5wW6xisft5/ntzA3NJu30sVgmouS4yM7silUcRCSRnETkmxEGmYi+oiFeHIOAxIlSNOBIxCkEkX55hItfYuFEBSSAJCdGIKts7oopFXslJox5tpWsnbm+wUNVkkDEhMnr0StWsb8YEycXXo6d2WSADhVkgPiYbpHpIIOYHUR2ZpPMtvT+ckVsGYVtWaiUiGWspDwuIOf66gjjo9X8JQgtxtKSBEPSV2Fn5u9TDarsonZmQH+Mz2y/k89EpHNsmOig71GTMZ6y2HKLVZwS0UEC2cYD8R5lNx5ylYjCOGJT2RZTmQD8kYiJIDDLRIziBC0i2xpqJaL1YhVOSuVZCbNSg6LvGdogKbYzZ8dlsyQhU1iOnjOcRITepo7SbkloZjbteopVhpWIjGzRLMEBgEaiVsPGjBxPnJ3ZQUSisDPz5nNzJWLumCFsbvhI3HpSA45EnEKoMhGzha7+44m5AHmLVnFB3Y3sLVyUxSrsTDYh2/LysrJMxPoWK7KddACgm4wWY6pilQlYLoF8JaLJe5zZSPOHrGaNx+WwPUFWZs8DZpm6znRhQZMKlcq7FiUiKcAUubdG15dCidgIMkvdpuVWUiL9qlIi9hTHVVapVAZFWY/ifYpQ2M7MSMQ6lIhSO3NgPsbTBmSeTZvIbacMcBhGUSZi2WKVPDLb8zx+3dlUgEVUrJL4GP6qoXbmQJNE7EcxWuizv5W1M2eKPZtuoqydWa4q8jWUiLRB0vYUTavAQGGM1eLHRF4YwzMRde3MWsUqWeu0zY09L5GQvoISUXfNRZmII83MDERUOhLRYQDczjxKqieVKxGzMT9A7NqZNeBIxClE5XZmIRTey1m0ep6XBU5b/KJW2ZlLKRHzilUmYGdWEQJj2cJyi1XYYqyGwVGcvA9kIhLZYjBZ7RfYmV0mokMRNnvpOTTbDDgpYUpKZUpEOUFfh4o5Um08lBgzVLZfAJhhraQb1pWIkkxEC0rEMhtPZSE7Ls/zsu+ciuzMs9zOrL/AKwtpsUqZch+nRHQogaK8UdNTplcwFtYx100oE9HzR+bcmbItATTmu1GccNuv1M4sFqvUQErlF5BkxJS2ErEoE3FAiVgHOVqFnbmA8AWywhjPthKRHdewCkwoVtEZk+M44UQ2AlkmYvocSeTszA4CuBo2b+MhHYtNilUoLiD3+hpQIsZGa9QLFY5EnEJkC5YcC1cJsq2IwAEg7M5aJBGVSkTzRYayWKXGxYrSmjiOTTvIUXRMyM480M7smU/uVXZLwGUiOhSDCLCZViBkg5pdB32JUk68rQ4iW6c8y2yMl5NtANBmJKJ1OzON8cOKvTGLVfJJxBozLBWfFx+7NI+tq6lEBOojfYeVnjzL12jzi6mLVDEcThngMARZ3mgZIjuKE35/2fXVrmGuS3bmBDlN5eK8TkOB048yElFqZ2akVOAliCxm0vkaij0TJWKhnVm0adskBGLFcTGCM4Cewo4rpRoKErGmdmZfphwVmrR1Lq9QsNR7knMwYcrLOLSvoHfYRmAbKrlKRMPSojhO4IPyYfNIxOw5GojQd+vJQjgScQqhWrCMpVKRWEmBenZnY42Fc6kygUb2eMR11WlnDiVqDqCc/VilRKyT6BCfws/JRDRrZyZi1CkRHcqBCJVOM0AQlCMlZNl24m21tv1WpF4OFccFAB02ltRFSg1zfnRMSaJ/XEmSSAkG8TnqGOtlSkQgy27TPReL7Mydps+/x9YsW5rpvBlRgZUgcFSFFnVm+TpsL9CYPDwWDjSfG0YFACoSMSVUamlnzs3YExa/cfH1HcUJJ9uKlIgAkPTXDV6pGZRKRKHUQFeJWFysUpcSkcJhR48rbi0AAGbiNa3HypSIqmKVVM3XQZ9vvlgBfV7DhIugRNT5/oxEJSIjrEfg7MwOeUiqK1aJkoQ3uufmsgrPETglohYciTiFUNuZ2X3K5NFJJlVANrGya2dOf+bamUu0M/dyFi1lmk3HRaxaYJZajDFFR87n1Sxp4ywDceI+bjtzn79HEiUiJ1vdoO+Qjw3W6DjTDPiGiCnpnCli5ddqnSrfupSIMzUpEbNMRHl2oO5xiXk2ucUqNRXhJEmi/LxoTNP9zikiET3Py3IRLZeriFEnIgJDdSUgFoLlZSK6TSKHfEgb3T3zMWOARJygnTnmjaQ545aYGapRKBDGMZpFtt+giZiWg/0No9dqArUSMX1+30uKlYi8nbnIzlxPJqKnOK6QkYidpAtoqDyz90hlZxbbme0fl69QIup8f4ZxjDZXIkrszIErVnHIgZadWbOpPkr4mKFjZ3bFKsVwJOIUQmuBWUKJKFOpAPXYmZXFKhUtnH1B8VIXiHTII0fLEG6qRWbWYjz5TESzhnDNTERnd3OQYFOwM5dVrmZj62RLi1Rqc79UXIC8gARI1ZtApnK2BZliTxwbdd9fkczNLYypKQJBfPz8iJH0p3axSiQvziLMsVzE813bSsT8zZ0yeZPKQjD6rNz47jCEWHIOiv/UvcZFy6tsLKyjWCVhypo4x87siwoaDQVOKNqZZWSb5yEKmELMIokoLeoAjJSIa2xca0DPzjxju51ZcVwJIxEBAN3VwscKYqZEVNmZGRHX9uwqET1ZCYWYiairRPTouPJJRP4cjkR0EKEqVuF2Zk0SMY4REImYd325YhVjOBJxCqG0TpXI/FOF0xMmXaxSZtHSY5NAUWFJD11HYyeB5q1VWSS7vF1QnolYh2KP3kPPw0A4uEiMJprvMxECsnOwUZIUcrhwsN7L7MxNbmc2uw74eThhO7MqR7VcU/3WKFaR2WMbJUjEfqhWItYVgSA+fiBpvwbMLZcyJSIgNjTXVIQznEdXZvOLilVyC8Hc+O6QD76hMpLLKdiZdZWIUXZt5ZUIAltAieibKhE1SESAk4heaFGJyJWD6mKV4kxERiImVNahViK2a2qdzlMi+kET55KUzMTmSuFjUfGDL8uvBAZs2vVkIsrbmXU2FsM4QZvbmSXHxZSXiYZF3+HCQRaBkDPfoetNk0RMS6ZUGxkeJxLTYhU33yiCIxGnEFqZiAbXBs/L0ipWsbdoURWrjGPhE5UP3gTszJFk4QyM1zqd23IZ1FcmQC95JKvIEyf3eo+VqUbzz8FGjSUJDtsTvFilGZQu1eB229yMvRqLOiK5erkMgVO0UUTEjvViFZkSsYQ1sS9slOSRrXWQAcCwElFlgzcjEfPGdwI1NNvORCTyuZJMxL78uMq2cztMP2T5sANjhilBr9gwr8V1E1OZQJ6dOVv8JjpKxDgWmnHl6raYkYh+uGnyUo1A2YEj9lhgoFilOBMxgodMVSRXIqZZj20vRBSF2pvWplApEX3fwypY5qQGiRgQiajRztyG5XZmOq7hkkZSInoJzq13Cx9GzET0JO3MnFh2SkQHEZFciUhjhqeZiRjGQiai7PoSrPrOzlwMRyJOIaonpYqViDTpqqdYZfR3pWy/ORY+nolY49gRScg2oFxJAreFKRZjdeywcOXoMIkovN+6+XEhbwjPPwfpMSM36DtIIJKIZVWDPB82z85cqxJx8DlFmEY7FBWQAJmd2b4SMX8DTDxO3bFQtGjnqYraNWx8AUNKxAoUrKJaSobZmjIRM9I3P8PSZJ7RVW5+uWIVh3xkje5yElH3tNFR+WbFKvaurUyJmNPOLBBwSaRnZ85ajBVKRJazZ1WJqFmsUpyJGGZ5iICCRMxKPFpJz9oGHycRcxSWvudhNTEgEYuatIGMRPT66PbskW6+jBwVzsGV9WLSuR/FvJ1ZVqziOSWiQx40xgxfs505ihMEXgGJSOOQFw9sRDvkw5GIUwh1sUoJO3NBcyeQKVVsWgZUduZx2plbA3ZmykSsX4mYW9Ywlp150kQHfV6DtwclJvdciSg5B53dzaEIm8zaOdsK+LVmutOozCKs8RzkOaoVbDwMFJBIiouyYpW6MhHlxSq6729W+pF/TK0JKBFVG0VVqqXmWvUoEbNMxHwraZks37wYDq7ydfYiBwFxnHDHg+wcBEqUFk04uocUhknOEs33PPST9BqJNFRbunbmpGFfiegryDZRiViU87fejbJjAhR25hn+vzYbmlWFMYEPrGIu/UcRiZgkXIkYqJSIAjka9W0qR2UkYvbvcxok4mA7c/5nxTPqnBLRQQCdg3nFKtkmi74SkW8+5JGSwMA45OYbxXAk4hRCNrEHSrYza2Qi0qTLZvC+0s5cop05LweMHrreTMR8xR5QjhxT25nrU3TQU4woEcs0rcYFSkRnd3MoAKnoOkKxiun5EsWZum0YtRL0lKOa8zqySAa9xxooIGnIlIj12JllJK3neXxs1s4OLCiLIbLKNomYEb4F5VmaY2FXQy012yYlom0SkbkeJHl0Jpt6KgV9043vDjlQ5Y2Kl5p2O3MkPwcJddiZOYmYo0T0ffAm5UhDiRgNtDPLiamEEW5BZJFERPqejdhjAZ5Fpq9E1CARfR8J+90MegM5uVWC27THVSL21uAjfY1Je0F+P4Ecjeto0x4+LoGAWdvcLBRdhLGohs23M3NlmCMRHQRIy32Qkdu6duYoSoQIBAmJKCiinZ25GI5EnEKoGkTHamdWZCJyi4fFiy5WqIDKtDPn7TqXaTYdF6oinKCEwlJdrFKfokPWpj1AImq+jiIi2ykRHVQ4t9nHl589CyBV1dF5ZHodkGovb2wtUxhUFpy8qaCpXlxYyVR7mRJxMkUdgPk1XjRm1EEGAHJ1JaG0ElFZrEJKRMukL1eIDzXjUs6j9vge8+9cVSaiG98dRKhUvp7ncSJRv7SI5WQr7cw1KhHzSETPQ0RLN43Fc1+nnRmZEtEqiahqZxayyAozEbtRRox6/oC9dgTsuDqevXIV1XEFJpmIm8sAgG7SgN+ald8vaCBm50bSq0GJKMlEBFLrfdH3TKRRrMIzIGNHIjpkyIpV8nJUWQmKQTtzpkSUZSJmxSqunbkYjkScQmRqjtHflbEz9yULBRF8QWZxkUnXc76FL/1Z6riEN4ren3rtzAqbNl9g6j+eSolY52IslByXONnXJTrCAlURkTpOqeIwjPueOo1v//W/xl8/dhIA8MJLdwjXgdmiQofkAuxuQiRJwh8/P7KCvQZdO7OoRCzIRLSvRNTI8zXORJSUxdRkZ+ZlPJIoBmMSseC4ACET0bKdWRad0jBUIopEbu7mF2XeuowiBwHi/EEV36OvRNSxM1Mmos12ZnmxSuBnJGIUFV/fqZWU8ugkpRYAb/xtxDaViOlx+bkkomBnNlEiKohRAPBYuUrHYkMzKRHzbNqB52E10bQzb6QbnauYQ6A4BwEg8tPPMgnXDV+tPqSfl0DoNBFheb2nfJwwEuzMMiUitzO7TESHDF6iUayiaWeOhGIVqZ1ZUCKGTolYCEciTiFiHSWiiZ2ZLHwSqxsg5EtZzUSsuDAmZzHGd663iBIxs0jqv69KW1hgtmAdB6HE+un7mTVRl8TpFeSbcZWSG/QdhvAfPv4ojq1u4vLds/jAj74c33nbQX5OmpLpXJWtWLCmj2s/G3b4OQmmOapZdmB+AQkAtGsqVomi4rFQl5gqUtDXr0SUkIiG5KiWEpHamS0Xq8gKeUzPQfEzyC8Eq6/53GH7QHQyqJrPTa+tpo6d2eZYyC18+UrEGGyzW6OEoh/FAuGmytlLybZGDUrEXCuhsHjXaWduapTFAOD5gR300Lc01vvs/c1rnfY8AyUiIxGXk3npXJcQM4Vl0i9uRy4Ln5OjQ+eN5w18XsvravXgYCZiPonos8/Ri+21aDtsP5BVOVe97JGdWe+67kcCiVjYzhy5+YYGHIk4hVBnIpbJDlQTOEA9qg6VYq+UwjJnMeaVsHuPC5liT7zNhBtT2Znpfaoj60FVbGDagl2kRGyUJIUcph/rzGrzi2++Ba+4Zg+A8kUNmRJRrvIV72cDRW2/xnZmjbiKuopVqiyuofvJVEVtYePL5qKFH5NURV2O6GirilVYJuJaTZmII6UW/BzUexw6pobvKRusXdC5gwhxs0ZdJKj3eDrXFm91txrdI1ci+h64EjHWykRM0PJIBaYg3BjZ1ojtkVKBjhLRS9Drq8etta6gRJQpiggsP7Dt9a3Ne5VKRN8gE3FjGQCwgjnpXJeQMEWfV0MRjp+XYSmQLWcLlIjnNvtoe2o1bNCgxwutilEctheyYhWVermMElESgcCeJ0DszkMNOBJxCqFqZy6lRCwgcIB6VB10PecWq5Q4rn7OcZUJhB8XOpmIpbIec9uZ67P9qsgJUzspLcSLMhGdndlhGHnXedPw/COECqVcmQbhMhDHpjyCPmuY13u8ItsvUF+xiio/0Lh1OlSTo+L4aPd7q0CJWNLOrM5EJDtzXcrRwddiaj8m9XxeBAfgMhEd8pGV0iFXRW06fzIqVrG5oUJZhzmZiKmdOWB3K86P081EJNtvM7FfrOI35IQAAPT66uNa64WZRVtTiTiDrjVSgMjRIHfjHjhnrESck5YIEpKANTTbJBGhatNmZIsXFSoRDy9vFNqZA5aV2PCK7ewOFw7IzpzXfE7noGeQidjwCjIR2XjRRs852zTgSMQphFrNkf40KlYpCIYH6mm65EUdOS+jTDtz3mKM3rI61fSc9M3NejRXWHa12pnrKH+QE3+mCoEitZSzuznIkHceliUlVJl94vhocr2aQnzNeUOyqZU0Lxt2GHUVq6i+u/hGgXaju16xCmA3hoPGrsoyEXXamXmxil0lYl/yeZW1kcqOqekyER1yQGOBbG5KU6oqry0+17U4ZiQKJaInFKskGq8htZIWtzP7LcpErMEem2tNzEiCfpiOW/c8eQov/eW/wscfOjZw17RYRZNEZErEDnrW1idciZhDTPglMhFXMC/ddCIkjTqViOoinKJMxCNnN4qLVVgmYgOhXYLeYVvBVxSrELGoSyIOKBFlYyHLhp3xes75oAFHIk4hIonFCBgkqnRJpL5JTkxosViF7zrLj8tEQUhNfM3cduYalYiJfOFchuzgqo5mnhKxnvIHQC8/ztRy6ZSIDqbIOw8bJduZM0IyTw09ej8bEAnK/KiA9Kf5tSVftHRqsjOrVHumjb+ZElFCIgq321y06LYz654zepmITIloORNRVjRkukmkiuAQH89tEjmI0C0t0p3rUvayslilaV+JmCiUiAAQk51ZIxMxbSQtJtyoDbiVdK1tMPOijlx77KgS8a8fO4WT57r41CPHB+46WKyiyHkEOCnQQc9a2ypXIsrszIbtzCvJXCGJSMflW82wVJC+PmUiFisRjy5vCMpRSbFKQCRi5GykDhloLCwoY9JBqFOswhTZNpXL0wRHIk4huO23wHKnHbxPBRmKL7U6MhFjBdlWSmGZQ0yZ7lxXAZX93NReHccJnyjlTYTF57Cdi8itn4rXoassUdlIxcezWWjhsD0R5ijtypLO2aI1XyFSB5k9oETMuRxM1cthAckFZHZm28UqKqWn6VhY9L3leV4thWAqdaV4u+5xcRJR2c5cjxIxlGwUccLX0M4sI0brjOFw2D5QzZ3E27XtzBoEPV13NjfMaeGcSHK7uBIxLn4NoaadmUjETtKzRtYHkCv2BpSIjESkdvnVjWwci+IEm/0YTbIlFtqZGYloMRMxKyAZ/bw6zcAgE1EoVimwM4MVq3iR/QzLII+oFZSIZwtIxCOinVnWEM7OiSYiu6VFDtsKpETMJ7IbA/cpQhQnaBaSiNmmg1MiFsORiFMIlRJRLO/QV6rkqw1E1FmsUkWZAJCRaOKEMdu5Lv0yjaGVYWmYlwVkjaoiRCLF9i6LrJ0ZEBtJ9R6ryJrolIgOMvDyB+E8pP83XVQUEVOm5R9lEAvjRV4OmLmd2USJOBllG1BCsadh027X0LSq286se1xdnUzEdj2ZiDICOrOe6z2OKoIDEAvB3PjukEHl4gDGaGfWUCLanOsmiaYSMdJUImo0GQeMRJzxLNl+k4STiEFTnYkYMjsztcuvbGQkFRGL2krERtbObGvOS0qoPLJtrt3AKszszMsaxSoey24LLJKInk4mIqJiO/PyBloFxSoQlIguE9GB4Cka3TM7s74SMSi0M2fjoBOlFMORiFMIVduvaGfWb8aVK8oI9RSrKOzMJbIDM0VH9ngTsTMrlECm5Jj4/uctyNoNn6uXNiwvMPsaJRTaxSoFREcQuEWmQz7yFoZllU1FqjJ+vVo8D/lryBkHAXEM03s8vWKVyWciGissNVqn27UoEdWZiPT6dI4rSZLcza9hcCWi9XZm9nlJ7cxm31t5ERyA+D3oJvUOGXRLi0zbmfWUiHUUq+S/jphnImooEeNEq4Sk0WFKRHTtKPYE1WRhJiJTItL4tbopkogsrkdbiUgkYtca8auyac+3GpkSsb8GqIhfamdO5qTfFwSPKaaCaNOa/TxIiBxVtzMvb8iViHGc4PnlzcJilezxQkciOnColIhEIuorEWMNOzPLRETXrSc14EjEKYSy7beUEnHr25m9sdqZc+zMEyARqyhJIIuN5+V//p7nYbam5s5Q0twJjFOs4pSIDmbgOYbCeVhaiViwoWJqnyuDQguf4UZIX2OTqI5MxDhOuAJcNWaYKiyV+WYsg6+WTETJ6/ANlIih8B618xZ2DLW1M/Nra8jOXLYsRja+B/rvkcOFAxqP8zbMAUGJaNjOLFPEApnDw6oSkeeAyezMAbufhhJxwM6sKFZpEoloSYkovNb8og4fCSj3likRmepQVCISsbjQpNymIiUi2Zl71u3Mfq4SMcjamQGguyp/IKFYRbWpBwA+Izva6FvPesw7riwTMcZZhRLx1Pk0W65N56CkWEW0Rzs7swPBZ+3M+ZmIzM4MvfOlH+lkIhKJaG+8mCY4EnEKkS0yRz9eUcWnvRiLi5UqW0eJqP94eXY3euwk0Q/jHhcqJaKpTZsWw+2Gn2t1BIAZplJZt52XlWMjJZgqEVWqxvR2187skI8wJ46hLOlcpHypQzFVRCKaEjghJ9vkm0R1tDOL164yssKwdVqlRKwzE1F2ztD3j86EVVzcK5WI7SwT0db3WJIk0nPR9LMqLlZxmYgOo9BVIpqOGep25voyEWUkYuyZFKvoKRHFVlIr83hBLeRLFvAJqev66wCyTZBBO3N623yDfabaxSr2MhE52dYYPa5G4KPRaOJ8kioiqTwlF0yJuJwUtzP7rfTx2uhZOxfJpp13XKIScUWRiXh4eQMAMBuoi1W4ndlzdmaHDL7CUm9qZ47iBA1Pz87c8bouE1EDjkScQugqEfVtYRp25sD+7ixdz6oFpokNOU+JONheXeZVmiPL9Rn9nYnVDcgWwzqh+9atiRrFKqYlCdLg/cDZ3Rzy0c/NRGTEjbGdWW2RDWogs4tywOhyq1aJaL9YRVzo19HoDoiZiBa/twoaZLO21+L3VpdEnGeZiEli7zMLBz6vwddi+lnRscuLVZwS0WEUxWNh9aVFdWyYgxbFBZmI0CpW0Wtnzmy/lrIDRSWiZLMA7cX0NcRriOOEqw7Pd0M+B6bb5hqkRNQsVkEP/dBuYUwgIX3n2w29hmZqZ8ZcoRKRMizbXt/auahqneaZiJ5aiXjkbEoiznhFSkQqVnF2ZocMmZ159NryhIZwHYQGxSoz6PG1g4McjkScQqgyEcWbdCf4RXl0gLAYs6joyAoFRn83TjuzuHAR37O6FiyRovHVxOoGCErEnFIVAqmK7Ifuy23wXImoudPTV1ijyzyew4WBSLB/DtiZx2xnlp2HNEZOqmAKMC8T4FEBCuWDqES0pWwTv49UmYi6Y2FeK/cwMiWifYWl7P3tsAX1psY5Q4v7wPeUmVmdRsCjOaicoGoMkL5jZiLScRUVq4TOXuQgQFUiCAjxNIaKWB0los0xnsjBvIUzIBar6GUiZsUqCtWeYGe2nYmY2/YLcBJx0VtHN4y5nTlJgHOb6f/TvFWbRGTFKjPoWlmfJEmitv2ClaskBeUqUcitzss6mYisoKSDnhVBQByLx1WgRNzoS8f6o0yJ2OYkYif/CYPs8ayqfB22FZSZiOyc0VcixhnhmNcQD2TFKrayYacMjkScQqiUiJ7n8YmV7gS/H6sXzoCwO1uDUkVpZzZY4ObtOov/X1czE99Nzz2u9Kd+QH1xps9sqx4SUWUnNLVp9wtKElwmokMexElAM6eFPSUZ9c8ZTghJzsOZGrIDdUnEqlS+QLYpESf2yovEMhqVEtGUmNLa/Jrg50UqT52FoI5SCkg3w2abdmMrxGtrxM5Mm3qmMRySzS+nRHTIQ1EmoqlDRaudmXJU62hnltqZ2XispUTUtDNTi7GtduYBEjH/uLyZJQDAAtbRDSOsCxsgZGk+z5SIM4FmJmJTyES0cFxxIrQzSxSWs62gWIko3L6KOWUMB4ABhaWNczFKEjT4ceWcN4IKLBZI3mEcYSRis7BYRWhntvh97LC9wElEhZ1ZNxMxzYeVtz0DGIh1cKKUYjgScQqhG7yvTeDQxKox2ZZLVbGKaQYTINjdhOMSF5xWd5oFyBougRLB4Bo76VSsYrudOVSUoZhmWGaqosnZSB22H0SiQySmRFWiyTkTFqj26sgb5eO7JPPUuLQoVCvlgIzoAmzaY+WklHibftajRgxHje3MskWhCSmho5QizDJLcy1KxOFiFVLQa07Cu4XFKi4T0WEUNC+qrJ1Zo/m8FiVigZ25D7YADjcLHyqKBTtzQ0LgAAMKHJvFKmHiw5cIErxOSiIueuvY7GdKRCBraKbv1lnfTIloKxMxihNuZ/Yb+YTmfFtoaJaRiKxUZTWZQYSAz9OlaFAmYt8K6TZ4XHIl4jz7lczSTHbmRsJIRJmdOSA7s8tEdMhABL2Xs1lA6sTAJBOx0M4sttS7+UYRHIk4hSiyT/lctWf2eE0tJWINio6cxbNXgkTM23UO/EypaXNhKSJWHJepAqMooB4QiQ7bdmY6b6ooVlErBBqGj+dwYUAkMQauc4HQMRkzMiVi/nlYZwFJoZ1Zd5NIpzgr8HkUhi21ubj5lVcKZV6ssrXamfPiKoCSSkQNEnHOMqGtKsIh14KpAqzdLBrf3aTeIUPRtWW+CavO5gTqKVbxCuzMG16qlvFYAYkK/VivnZkyEWdstTMzRVEEX27VZXbmBaxjvRcOKPpJiUibIlyJKLMlEhgp0LZk006JCdooyicm5tqNrKG5gERcSeYBZHMJKQTlqI1zMRLszLnHxUiYxXb6WS5v5JerpErEBI24m94gVSKSnTl0dmYHAGlUgFKJSHZm6F3XoXBOy+3MWSaiW08Ww5GIUwjKiSm0eJhmZk245ZImgnnHRZMSk7gu3sQnLDI9zxPaMu0vWJIkUZICxhY+LSViPe3MqsIG82IVl4noYA4iyDxv8PoSN1hMFhZFGzRE0NdRQCIvdymn2FORiJ7nodO0e2yhYjMFEFqnDclRlcKSxn6bWb5F54yJElGnOItASpY1S5tFWT7oKOlrmlFcFMPBz2k3vjsIyOZO+b83nT/RnKU98WIVtZ1500sJJK+/VvhQ0QCJqCpWYUpEr8fJ1ErBlIgRAjmJ2CEScWNE2UYkIlciBgUtq4SmSLZV/5mFcaxuMYamEpGVqixjDp2mgmglNDOFpZ3jyshRlRJxoZW+TqkScXkjs5ACimIVRiJ6cW0uMIetjbRNmfgMhZ050RuvIqNila6V+INpgyMRpxBFixYbtjBaCNkc/GMV2Wa44wzkF6sA2QSyjgFE/Ajyjss3/KwyJWIxiViXnTnPgmxerKLON2sGZu+Tw4UBTpANkc/i2Kh7ziRJUhgVUUdpUbGdOf2p385cvEkE2M97LHpvTXNPyabdVGY91pflW2Umomp8J8y12bnYtaVEzEpehlG6Fde1MzsYQFVKB5jPn3Q2YeuY65KdWWb73UC60EWvmETshyFaHhFuKhJxhv9v2NvQe50miA2UiN46Tp8fJKVWSYnIvls7vsYxAUAjIwVszHmjOEHTI8WerFglwCoKilW4EnGu2MoM8ONqWypWieIky3rMO65gSImYQyKubvZxbjNEC4JKUaZEDIRMREfeOCBdz5NyUKVENGlnbvCxUG1nnvF6vA/CQQ5HIk4hVNmBQNbQbFpqkWdLJdRh8eBKxJzFs6nyIY4zBeCwAocWnXU0M4mvN+/zMl046xSrzDTTwXPd4sIZUBfymAaeFxHZLhPRIQ8y8jkYUCKaqWEBebTDTA0EffXtzMVKRABciWjLql355peiHZ5Aij67mYjq46L3VcdSbWJnpoXoeUskYiT5/gSy8b2qGA7T+AuHCwOFmYiGc109EjGba1hrC6cSEkkmYtdPVWi+hhKx3+tm/1Cp9hoZiRj1im3SxhBJRMkGWKZEXMeZNYkSkY1n2iSioNizMecNhYbs3BZjUDuznp15GfN8o18J3s5sSYkYRZwcVbUzz7O3f3m9j48/dAzv//zT/C6Uh7hvRvg7WS4nL1ZxdmaHFHEMnmGYRyL6vFhF084cxUImotrO3EHX3vg+RdDY7nDYbshsRmprkGk7s7qxLgubTpIkN9NqXND6oRLlg9jaOkQw0L/ryEQsIhHLFqsoScRW+rv6ilVUmYjVKBFdO7NDHmSWes/z0PA9hHGifc4MXKuS87AOlW+xss2M7Cu6tgik2rNlZ6YYDtl7a0oiFuWoAqIS0WIMRyRX7AFmG3C9qDizjTDPilVsqWL7kfw8NP2sija/Gk5p7pCDwhJBw7luV2PMEK+9XhQrHTpl4VEOmNTOrJ+JuLEplK+oCLeggRANNBAi7lZPIiZxHx6AEIE0agnttFhlwVvHMxISkZSIba4oKrAzN7IWYytKxDBT2ck+r/l2AycKlYjLANJMxDkdJSIjO9qWbNqRQI7mWj/Jzsze/qdOruHdH/0melGMb7l2D67Zt4CjrJn5sqUAWGZ/I3mPSBnWdO3MDgxhHGflPrnFKhmJqMM7hAbFKjPoIU5YNmhRtMAFDKdEnEJkE6v83/MJvrYKTD8TMU7sqcFUNr6yoftAjhKxxkxE8TPI2003X4wVF6uQQsV2JqJKgVM1IWBKSjpcGOBqtJyxi8YzXcWxrOlZhO3cQCAbM4os1bq241Bj4Tz4uBNSIpZUWCrbmQNmTdwCSkSdz6tX0GIsggjtNUvjfKQxvptm+UpJRFKau0xEBwGFOaolN2F1lIji/SsHJxHzF7pdT1+JuKlLIgLoeenvIwt25ihMx6EYvjynltqZc5SIw+3MbW0lIiMRvZ6VOW8YCY8pIcgGlYir+Q9EdmbMcUeDEkLrtI04jjAsOC5SIjbTz/J/f+Uw/x594kR6Xh5hJOIlC+zvZVZmQFAiOjuzQ4pBJeLoOehxC3ys1YcglgVJNx8oE9FLFdx1OBK3MxyJOIXIwqYLGusqDN5v1TCxyopVRn/ncXtsml1WBJEgHF6QtTiJWIMSUXgddWRLAfXktgFqC7JxsUqkXojT7W7AdxChUmXTbWWUiNJilTpIREUWHWBenKSKHRBhQnaVQahQtgElilU0FJYtQUFvC0UNsiZKxK6BnXmOlIhdW6SvfHPRlLwpyvJ1SnOHPESKcxDIzkPd04a3Myvmug2xqd7SuOFRIVjOwhkANv10oetrKBG7jESMvUCuAmPoM5s0LNiZY1asEsKXKxHJzuyt4+yIEjH9e2pnbsGMRGyjZ2XOG/dFsk1hZ9ZsZ15O5nierRKMRGyjh00L52EsKCxzbfXsXJpjXIz43j5zOiURnzqZ/rx8B3tfZKUqALc5t9B3dmYHAJSJKC9WEe3MOnONUKtYJVMi0t84yOFIxClEXJQTw1UCeo9n0nIJ2FuQxYrFrrjw1Fm38DIB3xuZ0DRrLFYRB75chaVhAYlOJmJtxSqKTDLjYhWuKMs/rk7LrkrKYXtCVRpCt+nmrPULCH+gLjuz+jWYqiFpnCuyM9tWIkYFZGbDUN2ms/lVR5avDSVikWoUqFOJmEPQG9qPiza/XCaiQx6KxsKy7cxFJL31chVNO7OOErHXS0nERJYBJqDvp0ROrEFOmiJm9thYlYlIxSrYwGlZJiIbz1oeNU4XWH8Z2TZjiUSMogKyDcBcK8BqUmBn5u3M8zy3XAmW9dj2LCkRowJylN02l/OrZxmJ+OTJ8wCAK5bYnVRKRK4As2PPdth+KLIzB41MvaqzloziWMPOnGUiAnC5iAVwJOIUolDRYagS0MmWagRZ45qtL4DMxjf6O3FSonNcqsVYs1FfJiItijwPubuzpgUkOu3MM616lIgqO2HZYhXZOcgVYJaPyWF7gS8Kc84b08ZX0b4py16pxc5MSkTJa5htZaSUzuJZVjA1DJMW4TLIFPRqJaLu59UzyDerRYkoIWnbJu3M0dZRIiozEQ3dDkR0zkiywDLC36kCHDJEBRvcZduZi9rPeZaqpc0Hj7Uzy0jEHhWrhPpKxELFHoCQPW7St2FnTsm2MAnkGWNMibjoZXZmuiu1M58vqURsehE2u6MNwuOiMDsQZkrEFUMlYgd2SLdYk0ScbWbX1tJMSuo8fWpQiXjZIrtPU2xYGQJXgHVdJqIDgFToFChIv1YzPd98xFrOmzCKeVlQkZ255UVoIKyFB9jOcCTiFKKwvdOwyVhH0QEMlqvYAL1eVTuzeD8VegqrGx2nzYUlgZfFyDJ9Sk6CVYtMnoloWbVXVbFKkmRN2jLbEleAOSWigwDVOWias6aybxKyvNHJKRHFPKVNrbKO4uMCgLZlglSVsQeYE1M6Wb6ZEnFymYikatJ5DWbtzDUpEfPszDyuQi9eZJVZFRc6+YvwwGUiOuQgVMwJgRLtzJokPW1K2Yp2KCpW6TI7c6BBIoakRCwqIEFGIsICiZhEQjuztFhltJ15/2L6mlaHlIhNjykAi0hERrYBQGzBpi0Wq+RmLSEtVuGZiL1zQJQzJlOxCub5PEIJbme2084ca2Yizgov9d/fegQ/GHwCz55aw3ovzDIR26whfHaX/AmbWQGOszM7AOm43aDm5Zxz0GdxDwFirTl3ITEOcDIbSM9FN+dQoxSJ+O53vxsvfelLsbCwgH379uG7v/u78eijjw7cZ3NzE29/+9uxe/duzM/P4y1veQuOHz8+cJ9Dhw7hTW96E2ZnZ7Fv3z787M/+7GCYq0MphAWZWbQY05ncA2pLoIiWZWtYrCBHxdt01G19xWSxzmKVos/KNAtKr1iFVHv1FKs0FfZzHaWU+DnkPRYwmEWne147TD9UeX+mZTxFrfdA1nxu01ZfNGZ0hGtfZ2JlXqxiKROx4rFQpUIl2N74AoozLE0UnlwppWFnnrNMaIcKFZi4KabzcZ3rpgvxRQmJ2DT87B0uDMQFm4tlC34KN8zZNWtLpUJKRF+mRPT0MhGTJEG/nxI4no4Skeym4ab6jiVAmYiRys7MlIgdr49za6mK7aKllCzj7cxMidhIyM5c1M4skojVk6NETISQz7nn2g2cQ0ZOoJtTrsIzEef5HF0JkXSzMN+ImU07gp9apYbBzs09swGWZpp4yeU78frH/y1+qfl+XHLuATzyfHqMu+damI/Z8c7slD8hI2/aXoh+vy+/n8MFgyhKEHgK+zG7TZdETHRIxKAFeOn4PuNIxEKUIhHvvvtuvP3tb8d9992HT37yk+j3+3j961+PtbUsn+Md73gHPvzhD+NP/uRPcPfdd+Po0aP43u/9Xv77KIrwpje9Cb1eD/fccw9+93d/F+9///vx8z//8+Mf1QUOmi9VZfHI7G4FJGJgV9XBi1XylIiinVmHmArl6kpaWNZR0hEXqIpMywR0MhHrszNXo0QUc7BkiwU6piSxqypy2F4IlYpjb+A+hY9VsGAF6rHVFyvNPU5M6byOTGmuHt9t25l1jgswj+FQKxH1VYBlMTElIrPEne/a2SzKYlNyCHrhPdfJMTy3SUrEfELAZSI65EG7RLDCYjpAuGZtKRGpkVS20G2n+Xp+qM5E3OzHCOKUkPFUpRYMUZASbp6NTMSQSKlAXqzClIgA0OyfAwBctCMly1Y3+0iShCsRGyASseC4fB9xYNGmHWXkqAzz7QAhGlgFy0VcPTJ4hyQZtDMbtDM3vQj9vg2bdsFxsXOz4ye4753fht//hy+Bv3EKAPDG4Iv49DdPAACu3jvPjw0zKiViRrLa+Jwcth9SJaKCRPQyJaKO4yIR80tlmw+ex8/Fjtd1duYClCIR//Iv/xI/8iM/gptvvhm33XYb3v/+9+PQoUO4//77AQArKyt43/veh1//9V/Ha1/7Wtx+++34nd/5Hdxzzz247777AACf+MQn8PDDD+P3f//38cIXvhBvfOMb8Uu/9Et4z3veg16v+gHxQoKuElF7MRaS4mCyu7Mqwk0kEXXWGSrbCrcz15iJWPhZab4UnfbO2opVNNqZtQhfUYlYkIkI2CdHHbYPlLmcpkpEjYKpmVrszGqyDchs1TrWY25nLhjfbRerFJG0xsUqGq3TtWQiKsg2wFCJGOlltgGiEtGunTmPfA4Mvo+TJBFIRHUmolMiOojgY6FkKDRRIiZJks0LC5SILctzRJ6JKGlnbs4sAACCArLvXLePJiPbdJSIRLZ5VpSImZ1ZCj9A108X8AteemwHmRKxHyVY60X8uzVINElEIFMj9jcqd6qIhTEyUD7tg/GV6Q2HvzR4h/46wMjeZcxJs2EHICgsIxsKS+YKlB4XkTpxiJlWgE6cnYt/K/giPv3wMQDA1fvmgPUz6S9UduZGGwnS61Un69Nh+hHFWTtzvhIxIxF11rMDJKKqaIpKftBzG5cFqCQTcWUlDYrdtSsdIO6//370+3287nWv4/e54YYbcNlll+Hee+8FANx777249dZbsX//fn6fN7zhDVhdXcVDDz008hzdbherq6sD/znkIyooVjFvZ9bLRORKREu7s1mxyvh2ZmWxCpuR1qJEVByTeLvuwlmnWGW2aZ/oAPTamU2s54BcLdUIfH7+uVxEBwI/BxXZp6bZsEo7s2WiDSjODhRfh5ESsYCY6thuZy4g20yLVbLICp1MxMm3M3fDuHCBS59nR0OpYtL6XAaqIhzxtqLNyo1+xM/pRakSsb6IEYftg0IlYsnNykIlIhWrWBoLKRNRZmduzaSKvUakJlvOb4ZoevokYhQwm3RoIxMxXcDHkgZjQrcxDyBtaAaAPfNtPp5840i61mw1fE6OFtqZAd5k3EyqVxZltl+1nRkA7o+vSW84/OXBOzClXt9rYgNto2IVAIh71ZO+UdFxCSQigIHCmIPeGbRPPACAlIiMRFTZmT0PcSM9/zwL55/D9kMUi0rEvFxORiJ6kaadWSQRFdcYJxG7zs5cgLFJxDiO8c/+2T/DK1/5Stxyyy0AgGPHjqHVamHHjh0D992/fz+OHTvG7yMSiPR7+t0w3v3ud2NpaYn/d+mll4770qcWhS2XhkpElSVQBFk8bO3OKotVvCy2o6/Bjqoap3kmYg222KIFpqmNK7PjyAfIGaGERJecLAOddmYtO7NAistacQHhuCxnPTpsH6iuc7q2dDcLisZVICPvrCoRCzYeALPIAv4eKR4PsN88XTgWemYbKn0N0rcOJWKR2lzc8CmyNNN7LyqvZbDeps0VrOPFi5AKMfA9aRaYaR6mw4WBog2VbK5b/FjivLVI6Ws7usenTESJErEzl5KIQRICody5db4bZmSbhp05aVDrswUlIi9WUY9dfSIRmRJxrt3grb/3PHkaAHDjgQV4ISvr0CBHPSE/sGoHDmUixp5CiciUhV+JrwUAnHviHnzvb34ez68wsoyRiGvePABPr1jF9xF56fsSW7CfUxGOlPQlEoYpKIdzHv9W8EUABnZmgJOINkhsh+2HQSViznko2Jm1HBf8Wm3k53wSqCnc69UiJtrOGJtEfPvb345vfOMb+OAHP1jF65Hine98J1ZWVvh/zz33nNXn284gdZds8WSqblPZUkXwYhUbIb/Ca81bjHmel9m3uvoL51YOMdqqsVhFRYwComJP7/G0lIiG7a1loVKBmRWrFNtIAVF95QZ9hxQZkTR67pgSE5FGO/NMDS3hOnZmE0WkrtLctrKt6LhM7ecqAplQRyZiEdHREQjBIhW/CYlI56J1+3nOPEM81mISMV2Ezrcb0k0iuuZ0P3uHCwN8zJAVq7CbdSys4kZCcbFKMPI3VcID2ZnzyaTZuYXsH315LuL5zRAt3exACCRi1NV8pfqgUgOV7RcA+s302BaQEmPzIon4RJq5d/PFS8A5JjaZ21f43B7POOtVvsHHSUQFORr4HmaaAb7GlIgL55/Gk4cO4+5HT6Z3OJ667876KcmmVayCLMMy6Vf/eZESsdjOzN5PQYkIAG/0vwggSUlEHTszgIRIxH71JLbD9kNclInIiEVft1gl1lNDZ6VFXed+KMBYJOJP/dRP4SMf+Qg+85nP4JJLLuG3HzhwAL1eD8vLywP3P378OA4cOMDvM9zWTP+m+4hot9tYXFwc+M8hH0WKGVMFTl8jCwwQmi4tMPeialLW7DZnECSvo0SsIxNReydd187MFoxkt8lDXfmBfYWt3qxYpbhlFcgmXrYywBy2H1QbIA3DzQIVIUmol0Sspjwp1CggAWpQthV8z/Ac1QoV9LUoESM10dHwPdAhF9mq6b2f0bEzN+xm3/Lvrpzj8g1IxNWCPERAJPzdBpFDBj7XlcwJTezMNAYEvqfcoAHEGARbmYhqO/Pi/Cy6CbteenIS8VzXkERkZFsQWcjYYwv4qGABHzIScZEpEWdbAW9t/9pzywCAWw/MAueeT/9g6ZKRxxgBszN3UD2JGGmSo3PtBpaxgO7SVQCAF/mP4+w6U/E9kIpwPt+8A4A+iRjTZ2qhiCQxtjOnSsSNHddhI2nhcv8EbmscwsU7ZwQl4g71c/Lzz2UiOqTju6/MRExvayDGmk6BHFfXFih9SYmInnbx4oWKUiRikiT4qZ/6KXzoQx/Cpz/9aVx55ZUDv7/99tvRbDbxqU99it/26KOP4tChQ7jzzjsBAHfeeScefPBBnDhxgt/nk5/8JBYXF3HTTTeVeVkODEWZiDtn0129s+t6BTb9SE+pYnNBJk4CZWtnyh3RIRF77Jhyi1Ua9WUi0iRY1lZnmtmlEwxu2t5aFpFCqVJKiVhIctgncBy2F3TUsPpKxGJ77KygULFlvSwqEwAEVa6OElGzTICTUhNqZzYtVtH53rJNBgDFG0We52mrPHkmookSUSNrsQzovCnM8y147qJm5vSx0s/JKREdRMRFG+YG0T08CqZgHATEua6dsdBP1ErEpZkm1sEy8RQk4vnN0Cg7kJSIQVS9EozssUnBsjNqMRIRmZ15kSkR6fp/4Y51AElKjM7tLX7yRlaUUPUmc6Htl4FEDqt7XgQAeLH/OJbXe8DqUeCpzwIAPubdBQB6dmZkRTiJBfs5J0eldub8TMRg6SLcE98MAHj9wrPptalpZybbuY3zz0EfH/rqYbzsl/+KZ5BOCoOZiOp2Zq1ilTjlPBJZ6z1ByETsuzmHEqVIxLe//e34/d//fXzgAx/AwsICjh07hmPHjmFjI90NWVpawtve9jb8zM/8DD7zmc/g/vvvxz/4B/8Ad955J+64I91pef3rX4+bbroJP/iDP4gHHngAH//4x/Gud70Lb3/729Fut6s7wgsQRdlSu+fT9/fU+WISMUkSZQujCJsLMnEhIpswLjASUWdHQl2sQgqlGopVCj4rsnGsbuhNfMgO1y5YZJq0t5aFSglkokTMSEQ9JaLt1mmH7QNVLl6TWyQ1FdkaZLaoELNPtuk0sOuosvXiKujYrBVnFamyS9qZVZ9XqwYSsaj8Aci+O4viJcwyEdP7RHFixZJTRKrr5t6SnVlPiegm9A4ZdDOldTYe+AasRvO57c0HUt8EEiXijpkm1jiJeF76OOe7WbGKjhKx0Z5L/8eGso2RTUVKxLiVuszyMhGB9LO+qsXIjcWDclWBCFIiWrUzFygR2Zz72dmUYHux9ziW1/vAg38CIAEuuxNPhHsA6CsRifSFBRKRbMrFmYjs/GKZiM25nTjup/0G13RYTqKmnZkUYA1HIk4Uv/P5Z3DiXBcf/vrRib6OOEkQeIpMRN7OHGFN47r22LmaaNqZZ7xeLd0I2xmlSMT3vve9WFlZwWte8xpcdNFF/L8/+qM/4vf5jd/4DXzHd3wH3vKWt+Cuu+7CgQMH8Gd/9mf890EQ4CMf+QiCIMCdd96JH/iBH8AP/dAP4Rd/8RfHP6oLHEXB+3s4iVicoyEuPooWmbxYxcJFJy5EZPmBJkpElZ2ZdqJtWtwIRdZz2oHd6EdaNkJSLBbtptdRAKFTrKKjEODtsUWZiDVYSR22F9TFKkzdpEmw6LQitxs+z2u2ZavPxgz5fcooEXXtzJMiR82LVYqVRXW0M+tswvGG5sJMxPT3JsUq6d9Vf3xVETikRFxUkIh808nlEzkIiApKizI7c/FjZaV0OiSi3SxVr0CJuDjTxHrCxBYqJeKAnblYibiwkKoAo54FO7OmEjFpMxKRtTPPtQI+DwaAa/cvoL3GyI0lzYLNRmZntlesoh6T59n65GtJWq5ym/8Ultc2uJUZL3grz3PXJhEDm0U4BeSoJBPR6yyiN5eSiJc0VlJCmopSVO3MALxWSiK2k66zkU4IK+t9PMgUiI8fl29Q1IEw0stETJWIxfNtnsvqF4yFlKGKrrbI4EKFnmZ6CDrWmE6ng/e85z14z3veI73P5Zdfjo9+9KNlXoKDAkWL3T3z6Y6kjhJRvICKlIg27cxFxSpAORKx1Rh9rGaNxSpFdpyFdgO+lxarrG70C21stJuuykQE6skP5FbSvEzEQJ8QeOJE+kVGCloZ6iBGHbYXVGrYJle26SoRixV7npcGqK/3ImxaKviJNZRtZpmIepmjHYOyljLQb6qvrhBsK7QzA8J7W5SJ2NPPRGwFPv/u6PYjYKaYRDABXVvSUgtN9eDqBikR5a+vYXitOlwYKMz/NrEza8Y6AEL+tzUlYnqdy5SISzNNnAaRiIP5cQ8dXYEHDzcdXMS5ATtzsRJxiWXN++EGNvuRVmyCNmI9si0jEVkm4pAS8ZaDi8DKl9gL1shDBDgp0LaQiRhHemUNZGe+99w+vDWZwYK3gR96/peBzYeBoI3kpu/G+p/dy+6ruTRniinPiv28nJ0ZnSUs7d0BPAscDJYzK7MXAG11l4HfolbcLrphXChccage9z51GjRcPn7i3ERfS5yI7cxyO7NusQrYtapvZ+65YpUCuCt0yiDaj2UTq70LTIl4TkOJGApKxALbgE1Vh7gQkYVoV2VnblksiBlGkdXN9z0+gVpmiy0VuJ25YDe9DuuvahGva3UDgE88nLbwfesN6hY+222kDtsPGfE3nqUe0GtFBsxUgGVgpETUuL51i7N0ia6yKGpapY0RXfVPT6NYRVQU2cgNBPQVrIB+O7PO4p4IbfHvqkQR6Us3FxE453SKVdjJHif6SlSH6UdRHIyRnTnUmzuJ97GlYCY7sy9RIu6YbWI9SVVovY1VfvvJc1285b334Pv+273ohhHOd/sCiVgcEzU3Nw8gXTw/v1ItMRWTnVnRYgykSjYgszPPtxpYFDYYbrl4CVg5nP5j8WK9J+fFKv3qMxFjPYUlEYOPn9zA/fF1AIBXbt6d/vL6N6LbXOTfFdpKRLL/hjaUo0QiSsZlGYnYXsJ3vep2AMDO6HRmZZ7ZCUjWbgSvTYUWXasRIw5y3PvkKf7/h89uTLSosjATUShW0SIRWblTsZ05I7PdxqUapZSIDlsXkYZiz8jOvEWUiOJCRFZCwpWIm/o5YHm7zlyJWMOXWGY9l99nx2wLZ9f7WNEgEbkSsWAibKJUKgtOTigInKLJ/UYvwt2PnQQAvP6m/cr7ztZwTA7bC1njr/w617VIqkpaRHSadlW+lA+rLHgxsPZzy3fBmJG1/doZF4tIqXmDTSLx8VTFKqJ1sR8lucr0caGViaip8jTJRATSc3GtF1khEYsyEVuNAEBYeEw6mYjiXCZKEvio/nNy2H4ourZorNbZEFZtLA/DtoKZilX8Rv51Pt9uYN1LibHNtVWQxvCjDz6PzX6MzX6ME6tdnN8MsUSLcA07s0c2Pq+HI2c3cOWeufEORAAp2xJP/f56nSUAmRJxphUMKhEvXgSeZiSioRJx1tusfCw0tTM/d3Yd/wY/gjfH92BnO8bb7roOePEPDWz46RareC1WRBLbUyJKyVE6n0K2jmSZiOgsIVg6mP7/6vPAhmYeIgBfaMW1GTHiIMfnnzzN/z9JgKdOrqXE/QQQiUrEvHGDKbV9L8F6t3h9zK33hXZmQYkYuk1LFZwSccoQaRSQmJCIXE3me/AKdpGIkLNSrMIeUqUCyuzMxV8+fMKY184c1NfOnDVpyy9FyoNZXjdRImoWq9SgRGwq2pmLVGB/88QpbPZjXLxjBjcfVFshXDuzwzD4OViBEjHUuFYBMwKvDHiju2I87pSwM+ddpyKyYhVbSkS17ZfGLJ0A7TgWC8GKMxEBe6oiEyVilXZmANqtz2VQRPpSbMrpgtgUnXZm8TlcuYoDISpQZZOCbVVrA5blSW+hYhWZEtHzPPSDlHDZXMsshx9+ICtCOHFuM81E9Nixa9iZM8VeF0eXK1a3FSnbGPzZlLBY9NbRCny0Gj4nET0PuPGiRWD1SHpnXRKxlSos57BZ+SYzVyIW2pnT404S4LlkP/5r9D34992/i+TVPwcsHsQa23RsN/xCtwOBSN+mBTtzXGQ/n92d/lxjyjVuZ14EFi5K/7+7Aqywz6qgmRnAgAKsjkx6h0GcWN3EEyfOw/OA6/en+aiTtDRHRZmIArG40Sse473EzM7cQXdASOUwCkciThnECbZMIbCbTe7PrvcLiTLd0H3A3G5mAq7YUyycSclwXmNHQhW6T5PIOrIQsuOS32cHJxGLMyxpIayvRLQnVVdaSakkocDq9omHUivzt9+0v5DEdu3MDsPoKYpV6LzUDfDmpJRmwY8tWz0npRSDxuw2LFYpIqXmaMzSybwVJn7KdmbhvLC1aMnI5yqKVcyUiDMWx0R+XJL3l2JTThbEpqxq2JnF966OzT2H7YGiMqYds/obsD2mONkKxSpZO7P8mogYidhbTxVgR5Y38OVnz/LfH1/t4tymWKyiQyJSoUAPhysmEZOEyDb1+xvM7ACQKhEpR3D/YjqWXL9/Id1MWnkuvbMuidhOScR5r3oSMS5qMWbIyznsRZkNk37qWpkBwGdt2s14o/I4jsJMxDkWL7R2Iv25mSkR0V4AmkzFeuLh9GdBqQoAoJWdf87OXD/uYSrEmw8u4iVXpJ/XJMtVUiWigkRsZBENOmVQ1M6c+1giOJndc2VuBXB25ilDqGFn3jnb4oHrZ9d62LfYKXy8IpUKALQC1s5sYZJPtlfVy6BF5pqGErGvKlygduY6lIgFljAgmwgX2Zl7YQz6+AuViGS5tKjaU9k/A07gyAfoMIrxV48cBwC8/ma1lRkwy4FzuDCgyuVsGCoR+xoFGYD9gp9IQ4loojTuc7WmXqN7GCcIo+pDz6MCss2sOCv7TFVFCb7voRl46EeJtUWLjhKxo6FE7EcxP1f17cx6CscyiBTFWYC+44HszItOiehgiKJra2k2Jc6WN4o3YE2KVazbmbkSUX6dx81ZIAT6m+kiX1QhAqmq6HxXLFbRKFYiGx+zM1eJQlKKocGUiAveBv8ee/FlO/HL33MLbrtkB9A9l6nedDMRuRJxQ6vF1QSepk17vp1/3Msbfcy1GwKJqL8s91spUZe2yCaFUSsmSGI6LsnnNc9IxPNp3FCWibiYSkYXDgBnngSOP5TermFnpvNvFt3CDTWH6vH5J1JV6Suv3oOLllJe4PETkyMR47igWKU5i9hrwE9CeN2V4gekhniDYpXjztmmhFMiThmiqJhEDHwPu+aYSqBggl9KiWhh8Oc7zoqFs8kiU6VQytqZ6yMRVeQoKRGLSETxuOcVqg7ArkKFoLJ/6rQm3v/sWZxd72NppomXXVE8AZlhky+bxKjD9oKqIbxhmImoY48F7F9bWqSUwWtQNVgPPKZAXG1aWDwXNa2SKkXPoi0oEQtIX9pwsaZE1CCf2xpKRFEB2mnpTd2IbNy0cC72C1RgukpEnWIV8b3TJf0dph882kFybWVzp+I5IV0jRLyrYL9YhbUzS+zMAJAwpVfISMS/+FpKItKm8/FzXZzvhpgFu/7YwliJBtn4ejiyvF5wZzMkmoq95uwOAINKRN/38P0vv5yVqjB7bHsptc7qoJ1aM23YmWNNO/MwOUjfS2fXUoKbFPZzErIxD0FbzBCs9vsry7CUKRH3pj9754D+xkAmIgBgkeUinngk/amjRGyK7cxuHl83vvB0ml9559W7cR2zMz8xQRIxjCIEHvu+z7Uze4hZ43eztzr6+yH4zM6srUREV6uL4EKGIxGnDAMFJIq1E+UVnSrIK+oryLZhtCwq+Oi4ZJNFQLQz6yhV2K7zpDMRNZSIfDedWXJ+795n8Jpf+wwOnR6c5FGhzGwrKFZL1VGsQgR0zmuh16dSldz7VCqtf831e7VUT06J6DCMvoYSMdLMPOlr2FIB++3MOi3RJkpjXSWiGJFg4xorGgtpEaYTwUDfQZ5X/Hm1LOeb6djPdZSIRHL4np5aCrCbE1t0XNkco4BE7FKxilwp5XmecL06EtEhRaESkUhEjSgYGld0lGC2x4xAQ4noMRVa0j2PJ0+ex8PPr6Lhe3jrSy4FABxf3cT5zRCL3lr6B50dxU8sKHCOVJ2JWKRsY2jN7QAAtL0QS82c95eamXWtzABXIs57G5V/dyWaja/zgp058D1cvjslKoikWOd5twZKRGZnnkG3+viUItK3s5RZ5M+fEOzMjNhdOJD+PMcUslokYnb+OTuzXTx7eg2v+bXP4P2ffxoAsLrZx6Ez6ZryRZfuxDX75/n9bEXzFIHUsAB4icow4nZKWjf7aXbjZ755Aq/61U/jvqdO59yZ7Mx6xSodr6cVhXEhw5GIUwZxUqXKkCOVwKkClUCoucAEhImVhQEn1lg4zxm0d6qa+Fq8nbmGTMSCnXQgmwgvs8nGn33lCJ45vY7PPHpi4H60GJvPyV4ZxmyTFuT2vhxUyi2dUgvK4rjloF4zWFZoYS/n0WF7QaWyI7Kqr0lKRLGcFBdhW4lYpNgTX0ORAi1JEmWLugjP8zJ7rIUxvlCJSMUqGnEVYllMUZZq27I1Ua+dmd7XYiXiTDMoPCaC1WKVSE3gcCVioZ05Ha8XC9TzpkVIDtOPog0VnomooSahDRed0iLrmYisnbnRkC92/U66yEfvPB54bhlAavu9/kCqIDqx2sW5boglMBKRZQ0qwRbPba+PEyvr1RL2REpB/f6255YQJ+nnubeZM3asliERU7JtDpu8wKQqJGSRLLAzi5mI+xfa2M3iHs4ygpte15xJJmJLVO5VrEQsIn09L8tFXDkMROyzIiUilasQtOzMlInolIi28bnHT+GZ0+v4/S8cAgB88/mUhDu41MHSbBN759tYmmkiZg3NkwA1nwOQqwfZ+dYOUxL7L79xDM+d2cAnHjo++nhh+j3gFUU7DCgRizegLmQ4EnHKoLPABPTzikLNBSZgt7FOp1ilTGZWnqKDSK9urUpE+XENF6ucWE2b2J6VKBGLrMyAWEJih3BLkiQrSsg5d3RaE6kVjHbEitBxSkSHIahUdnRe6i6UVEVBImwrEXU2VLjSuIBQj+IEJF7Xyb3NSCl7GXvSYpV29r4WfWaqzNth8HyzaIL2c05KyF/DhgHJQbB5LoYFn9fe+TRTSWVnTpJEq51ZfB7dIiSH6UdRGdOOmVQptd6LCjcJNgyKLay2MycJt/Cp7MwBIxG93jqeOZUu8q/eN4f9LOP8ubPr6IUxlkooEQHAj7qFUQQm4KqiIjtzI8B5pK9jd5CjhuRKRM08RCCzM1soVskUlup5t2hTPrDUwU5GcJ9dH1QimhSrDCj3Kh7jeeu0RAEGAJhnlubTj7MbPKCVvtcjJKJRO3Nvy2ciiuNJFCf453/8AH7r7icn+IrMcJq5EJ88eR7nuyEeeT4l4W68KFWSep6Ha/elY8ykGpp1SESPKVzn4vPohTHOsHXy8XOjjeW9XjqeBU09JeIMnBKxCI5EnDIUhdMTds/pWY2osa5IfQOY2YlNoaPYWyiViTj6eLyduQY5PbdpK8hR2k1f3egjjhOcYBO7Q2cGd4fouBc0lIi27cyiWiSPnNhTkJfVj2I8zSbG9EVWhNkaLNoO2wuqTNeMlDDLRFQpygD7LeFaSkROqKvHsIHrVKOVdMamsq0wEzEb14oszSob+zA4IWBp0aLzeem0M2/wzDb9RWY9ytH893jPQnFkikgIqzIR0+dxSkSHQdDGg2xeuNBpgKZWRdlWmZ20+PqyWqySZI8ZKArymkQihut4hm0oX757DvvY3Oo5Zk00UiI2MhJxpupcRFIiqkgppOTFeaRk0q7GKBHAMxHL2Jlhw86sl4koOoQu2jHDCW6y2pcpVhEVU9UrEem4FK+HlIinn0h/thezgHeyMxMM7MyzFo6nSvz23U/ill/4OL70TJoh+I0jK/jfXzmMX/nYN3k5yVYHrf2TBHjw8MoIiQgA1zIBx6RyEZNIGLMlJKLPMlQXvXVs9KIRsQ2hH8WIGCnZbBY01fOCqa4jEQvgSMQpA1fsFSkR2UTjdEEmYtawW3yqzLdTsosUcVWCYst0lIg6dmYiCPMWznUWqxTtpAODlpxTa13+NyNKxG4JJaIltZRIzOQROEWZnM+eXkc/SjDbCnBwSSMQHIKF0xWrODCo2uWzdma961y3gMS6EjEpHjN0lcZ9gwISQGyetrdRJHsd7YbPc36LNgqMsnwbdpXnOkpEnaIG0c6sixmbytECZe5e5nY4s9aTfpeSCjHwvUIFDn2WLhPRgVA0f/J9j7seimxpnMRpFs+fbBariDlgKiViazZd7DeidTx7OiUKr9g9h31MiUiXyQ4TJaLvA0F63XbQw+EKG5p1yTYAOO+l5NiOXCXic+nPpUv1n7xN7cybvMCkKugel7gJdnCpgx1zQ0rEEsUqYhFJ9ZmIjDxR2bRJiXiKkYhkZQayYhXCFNmZP/XICfTCGF9kRSSiGOdd/+cb22INIr7mrx9ezicR96Wq0g999QgOn622aEkHyYASMf+6CNjmyBLWsN4PcYYVFR1fHRSorGz00WCFVY1CEpHOw54rVimAIxGnDLq5XWRnLsorMslEJCXBuc3qLzodcpS+pPtRUvgFxItV8jIRGzUWq7DnCBTEBM9EXO/jhDAwHjqzjkQo0qEFmVYmomXVXl8gZvJJRFpgdnMXhU+QlXnfvFJ9KiIjOLb+F7hDPVApEUlBpats0o2K6NSk8lWpl0UiM1E0oIuKQp2yjllqSJ6Ass3zPO3Iiux7SyeGo1gFOA502pl1sgs3S9iZTVq6TdEvmGvsnG3xY6aJ/TAozmK+3SjMeQwMlcMO049YY17IN2ELFCV0fWnZmZv2Gt3DUCQR5ba7zly6wG9G69y1ccWeWSx2GlyB7CHGgscW/zpKRGBAhXN0OUcJWBKepp0ZANa8lPTbG58Z/SXZmRcN7MxMidj0IoT96o4JEEjfAoWlOC8/sJQpEem85JmcGiQ2B1ciWigiIZu2qsl2WIkotmWPKBF1SEQ697Z2scpT7HojIk4U4zx9ag2/+dmtb2sWScSvHDqLR4+na64bL1rgt7/5hQdx6a4ZHD67gbf+9n08NqEu0LUVwwdk8wNGXC9661jrRvx6OnFuc2Duu7LRRxPp43mF7cxkZ3btzEVwJOKUQSfEHTBvZ9bJROQkolU7s/w+4pd0kRqyp2xnJiViDcUq7ClUCsslNtlY3ezjqNCY1w1jbm0GBCViuyDvAVkDnDWiQ3jv8lRgu5idPk6yYGkRVKpyjaaVGRAKLbbBLqBDPQhV7cyBWcYaVzUWkG2zNWUiqsZkuhbiRJ3bRdfejtmmFlnPG5I1yk1MUaRsA7JylaLn75koEQPKRLSkROQFJDrFKgolIrOmG9mZG/bbmWUEju97PDZFFluxyvMQixfOrp3ZYRihxrW1Y0aPRCR1tZadmXKzbeR/C+oblZ15dn5H+lqiDX4dXb5rDp7n8VzEBWzAB7tedJSIQNZMin6lduakqO1XwDcaNwEAbln+1OAv4hhYZW2/JezMAOD1Ks53K6lE3Dk7mHVeTomYkR2V25nZ96HyuOYZiXg2bfgdUCKOZCJq2JlbWdv0Vs1EXN3scwKO1s+n1tJ/X7SUXne/9dknC6PCJg1x7f/ZR09isx9jphng8t1z/Pbd8238yU+8AlftncOR5Q384z/4Sq2vMWY51ZHqHCQloreG9V7I57Sb/ZiPi0A6/lPrPbSLVXo43+3XIijarnAk4pQh5JmI6vvpF6vI7YDDmBcyEeOKJ/qxRrFK4HtcgVPU4EnNy3mLzKblRaUIHeUoKRGTBHh8KJtCtDSfN1iQ2S5WIWLG9/LzipqBzydReZZ6Ok6S0+vAdhadw/ZDpjhWZCLqKhE182Z1m5HLgpRtOkpEQE1MkUKMSP0iUHNk1Q2XgJ7Sk5SQRc8fGmx+EYFXdTA9fy06mYgaba+l7MytYoVjWeiQ6kWOB3ItFJWqAJlaXzd+wGH6kRHZ8vsszTLFl2Ymop4S0WLWqOCiaTTkc7nZ+VT11UlSZd2BxQ6/3ikXcZGszI0O0OzovQBOInZxpEI7MzQVewDwmfbrAACXnb0PWH0++8XJb6YtwM05MyVi0EDcSI/f61dsy9RUWM4K4/aBpQ52sPPy7FAmoonSHGI7c+V2Zo3Pi0hEum9bUCI22pn6sNHhr1UJgRStY/1VBqIa79S5QSXid912EDccWEAvivGFp3JUtFsIp4SNPZp3XH9gYWSecmCpg9/7hy8DADz8/GqtVm2yMysb3UmJiDUcW9mEOJ0XcxFXN/poMjszfL1iFd9L0EbfqREVcCTilCHLy1J/tHsXsrwiFeFnokSk7JkkqX6RqVOsAug3NKuUKnVmItJTqI6r1fD54v2x44O7qJSFA4hKRI1iFcvW334sV4ARVER2RiIaKBHZMYVx4naOHAAI52FeJiI7N/WLVdiYUWRntnxt0amt2nhoBD5Xy6heBycRZ/VIxEyJaENtXryhMq+Ze8tbuTU2v6wrETWUo1pKxBIkYqdhkeygKA7F57W3oEDrnJES0Sx+wGH6kcXcyK9z2oQtWgiatDPbzBqNB+zM8utifj5dPM9iE0CCy3dnJA3lIvJSFV0VIsDLVWa8Ho4sV0giJhpFHQynO5fiS/F18BEDX/9g9otnP5/+vPSlgOK9yX16pkYM+hWXROi0GCOd41+5Zw4zzQBX7ZkfyDoHsu/puVLFKj1sVqxE9BJqnVYcF9mZCaISEcjUiDpWZoAfT9sLeZPuVsPTIonIFYnpzz3zbdxx1W4AwBefPl3/i9PEZj/ijkHacAAG8xBFXLxjhs+9qsxJLQIVqyjVy2xsW/LWRsYrMRdxeaOHBjQ3MoSCKZeLqIYjEacMurldpDyJ4kS5Q9s3yERsN3y+AKy6oZmITpUSEdBviF5lCog86wAtKmtpZ9bMsKSJ8KPHBklEauADhExEIyWiLTtzMeEiIxGjOMGTJxmJuN/czgy4XESHFCpFmqk9sq8ZFUFEmy07c1EjKYFysVSvw1SJyLNUrWYiFhfGrOkWqzR0lIi2MxGLj6utoUTcLKFUsVk2pVMYU+R4oO+sRY3vLJeJ6DAMnXOQ7MwrObEpItYN2s9pHOpH1W9YhkIjqYpEXFxKCZuAqWWuEGyI+xcYiUhKRN08REBQIvbwfIWZiJntVyN7t9XAn0avTv/x1T9IlQkA8Ow96c/LX2n+/K3U1dKJNyrNskwMsh7/+CfuxF/+s1dhabaJnbODmYhrBnZ6DrHNuOIxnuznslZcAJkSkdAZIqEWiUTUsDID/HgAIO7VR1aZ4KmToyQiKRH3LLTwsitTwvQLT29dJeJpNu9rBh6+5Zo9/PabhDxEEZ7n4ZKd6Wcjrjltg2ciKklEUiKujyinT5zLxq+V9T4anqadOWgAQXp9zsA1NKvgSMQpg86kCkhJQdoJU1madRtJgXSgycpVKlYiarZOEylYpFShQZ8aJEU0ebFKDZmIGkpEILPkELl29d50wvisMKCf72Yh9UXgiqKC4oWy6GtYP/dIVCrPnVlHL4zRbvi4ZKeGBYKhFfj8+bZDO5qDfaiKoYhY1F0E6mT2AUKpieVilaIxnpOZvQj/43NP4Tv/y9+MFFwY25nbFjMRNY5rTlMJSZtECxr5sLUpEZXFKsWKQfqdUSaixXzOUENtXqxETD+nRQ07s8tEdBgGje+q+dOw4kuGDV6souHkEIieqq8tygEDAD+QX+ud2WzBP4dNXLFHIBEX0+uulBJRsJSe64aVzQ9J2aZjZ/7H33o1mrd+L5LGDHD6ceDwl1MikZOIrzB+fp81NM97m5V+N3sJTeKLj2vvQptnzu0QMhHjOCmpREwfq+310etXS3R4OiTi3N7Bf48oEVm5ik4zMwA0OkiQXstbiUQ8t9nnmamiEvHseh9hFPM19O65Nl56RXqsjx4/h5UtSj6RlXn3XBu3XbqD3y5TIgLApbvStdhzdbY065CIbINksUCJuLIRCkrE4vmGWPKzsqHegLqQ4UjEKYPOpIrAVQKSCT6gtgPmYd4WiajRSApkX8AqJWIvjLk8eXceiSgsKm0QbCJ0lYi0m07kHO12DWQidvWtYTQJjuLEyuKZcqvUeVn55T5kZb5m33whaSzC8zzX0OwwAFWDrLkSUe9atV3wo9NIOvw6PvCFQ3jwyAo+9/jJgfuUVSLazUSUjxmzZGcuuL5p53jnXPFkMctEnFw7MykRK7cz2yQRIx0lorrAzcjO7DIRHYagQ9AvGRar6NiZxQ3LqjeLQpYDFiYFc24/wAbS+eust4krBuzMjEQcR4nopddsZYUdvFil+Fp/1bV78W+/7xXwbnpzesOX3weceQo4fyxVCF18u/HTe+2UdJ3DBtb7FX5/8exAM3s1kYhxkpZR8vOvRLEKAITdikk3HYXlzM5BQqY9REItHMzupwPPQ99PVbRJv94mYBlWNvq461c/g7//3+5DkiQDJCKQzqFI2bd7voW9C21ctWcOSQJ8+dmtqUbk9uuF1gCJeIOCRLyMSMQ6lYhsLEyUmYg7ADAl4giJmCkRlzd6WbGKBuGfRQU4JaIKjkScMugqEYFsgi8LPQcyS69OJiKQKT9IYVAVdBfOeXbmc5t9/Nyffh1/+Y00oJmCjAPf4+ScCJH4sq1GJIVlETlKEw4CkYiHzowWq+gpEYXihZ4FElFDtSWzuj1+IrVsm+QhEmZcuYqDgMzWmqNEZGRVX5NELGqjJcxYbmfWLngRCPXnV9LJ1HCeTWk7swUlYqzx3TWvqTTPWqeLjytTItr9vFQbcaRE1CpWaelP27LsNnsbRXqZiNlkPooT/MJfPIQ/+tIhs2IV9v45JaIDQcehwklEzWIVHTupzQ3LmJcJFF/nm15KuMxhc6BVlduZx1AidjBY+DEuSNnmadh+OV7+4+nPr/8x8ADLRjz44gHyTBtMiTjnbRYWLxqBK/bMltPtRsC/T1fW+1mxj8EmERpZWU7UrTbrUUs56nmDasRhJeI1r0sJxOveoP28UcCOqVuj4k2Bh46u4Ox6Hw8cXsFDR1fx1MnB9/nEuS6fR9GahtZnX3xmi5OI823cevESvudFF+Mfvfpq5drxUmZnPlQjiailRGRj24K3gWNnhj8bwc4sFqsU2ZmBgXHQkYhyOBJxyqBr+wVEEkcu1dVRlImY18wkNIWu7XcuJ3j/Nz/7JP7oy8/hP37iMQDZALprrpX7eO2GSCLaVT3oWhOXhshOksyfWevxhdg5g2KVZuBzi3qlu7IMoYaCNVOpZCRiGMX4m8dPAQCu3a/fzEzICBw7rdPj4MjyBh4+ujrpl3FBIVQUbBDBHWkqm1TWaBG2ieysPEtPiXhsZYMTUMO7yES26ZOIWQxC1dDLRCQlovr6pklf3ibRMGwqEZMk0Wtn1iD7NsdqZ7ZnP1dFnezNmWN8+ZkzeP89z+Bd/+cbPI5Dr1ilvpiRMkiSBPc/exbd0G1g1QWtTES2kVBlsQqQXVvrVZcIss2MSGN51vXThW5qZ84pVimjRGSlAnN++n5VthlGRR06KiDCxbcDV31rWsryuf+Y3lbCygwAYMUq89io9ruZ25nNlIhA9v10dr3HN+bmNObvHL6PHlPuxb2qW6fT40qKjmteJBGHlGyXvRz4l08DL/oB7aeNAkYQh1vDziwqD//gC89irRfB97JIqSdOnOfjEM2jaH32xS2ai0jfx3vm2wh8D7/x1hfiX73xBuXfcDvzmRqLVWKNch+BuO6tLwPI4hxOiHbm9b5gZ9a4xkiJ6HVdsYoCjkScMujaY4GMRJTlFQFisYqeEnHRsp256GUMtzOfPNfF+z//DADgGFPjUB7ibsnCeVCJaJdEjDXVTUuCErHd8HHxjhn++mln6LxBsQpgt6FZJ0uTzj/6PHphjJ/+w6/inidPo+F7ePV1e6V/K0NWGLN1LG+9MMZ/+dTj+NZf+yy+87/+DZ5f2RqTowsBfYUilisRNUkJHeUVIJyDlvJGQ81oB3odYhD48C4yXXs7tTMRSYloo525WL08p6mEXGbk6E4NJWLbYiaiKJpTfSfTxpWKgNowKH4gdBr2CO0s99YsE/EJpuToRwnufiy11+spEbe2nfmTDx/HW957D/7pH35t0i/lgkGoUTJFLg5VsUovjPm4OtvUmz/ZKqeLeSNp8fIsZCTiRbPxQJZjFZmIiwEjEas6PlLsmSgRAeCuf5H+ZO3OpUpVgEyJiM1KiV/PoFhlGERwn13vlStWATL7b8UkIikRvSLSV2xoHlYiAqla0QARU1d6/a2hRBTnT//7/iMAgEt2zuLgjvQ6+SYru9wx2+RrR1IiPnh4ZUu6okQloi5EO7PtmC+CVrFK0EDXT18bbZpcfyAls48PKREb3M5skIno2pmVcCTilMEkE/HgjnSwVpEamS1VU4lIJF7FJKK2nXno+X/zs0/wndRz3RBr3bBwAA18D/Q0tsL2Cbpt2jtmssXwgaUOPM/jO0OHWC4iz0TUKBMABosXqkZf47wZtjP/yz99AB/7xjG0Ah+/+f0vxi0X50xICtBp2lEHlEU/ivF9/+1e/MdPPoZeFCOKEzzyvFMj1oVMST16fdE1p2uPDDU3VOgctJU3GmuQbeLreFKYBA+HYpMSUbahMgxdJWAZ6JC0eUrzPJxlSsSl2eKxsNWwp0QUCa9A8Xl1NBqiMzuziRKRFbZYUMeZtDOvbPQ5Qfrkiex8pLWIjhKRiNYqm1WrxNcPrwAA/vKhY/jKobMTfjUXBujy0mlnVtmZxTmQ7vVlK7aClIg6duZ+I50DXjFk2phvNzDTDMbKRJyvmET0iAQ0Vexd/krg0jvYg/jApS8r9wJYO/O8t1mpkl6rxViCrFylz99no2IVACEjEVG5ElHzuMSG5rb5nH3kadk57YUVNoOPAVGJSPO5q/bO8TnTo8fS+bw4h7pk5wwuWuogjBN8dQt+F2RKRL15HwBecnmuG9ZHqkXU6K4ek3vN9NpeRHoN3Hgg/ffx1S4nPFMSkezMOkrErGBqWbEBdaHDkYhTBpNMRBoUhnOyRPBFuGbBBSkKqs5E1C5WaWeL3KPLG/iD+w4ByDbDjq1uZkpExQBKO0q2rVO6SkQxE5Hybi5nQdqHzqwjEtrddJWIPN/MhhJRQxFL7cynz/dwvhvizx84CgD47R+6Ha+/+UCp5521XGphiq8fXsZXDi1jthXgKmZ/eObU1thhvRCgyqMjMjDUJPp0ij+AGvJGTZWIp7KcmKPLm/x4kyThgeA6ij3xMW2MGTrfXbNtTTszm+RqKRGZWs8G4SsS1DpKRNpoyMMGIxhLFatYGeOLyeylmSa/zuh798mhTClAswzMctbouBBD3f/Dxx+d4Cu5cKCz8UAbCSsbfT7fGgZFujR8j28qFMHWWBiF+pmIcSOdU1wyPzh2eZ6H/YttLI6hRKzazkwkopGdGUgn76/5ufT/L3vFqGVWF1yJWK2d2WfHVajYywF9P508182UsCbFKsjsv0nFyr2M9C1SIioyEUsgYUpEP9wa82QiERcEm/mVe+b4Btljx9PvM7Gk0/M83HbJDgCZUnErgcpUTZSIM62A3782SzOVMRUQ2f1mOiYseWu4yjuKtx7/DVyMkwMlqssbfTQ8cztzx+sV5uleyHAk4pTBJBPxEhaUelhR2U4LK20lItmZK7a76SoRiUQ8txnij770HHpRjJdfuQtX7kknW8dXN3FqrXgApbD9vmXVg64SUcxE3L/ESMRdGYkoZlDOaU5CbGX6AHr5cbRz14ti3PPEKSQJcHCpg2+9fp/0b4owY3HRXAaPswnGS67YhdfflBKjz57eGq1zFwKy8UuuRAx1lYgKVaOIZuBzwsgG4ZGRbQXZjOxaOCQ0uEdxwktW1nsRV3WpNlREcCWiBTuzDkk7p7lwX+bFKgZKRAtqPfHc0slEBORKu00qfihBInbDWEqglEWkQeD4vofdc4OWZiIR6TsZ0LMz6+RGThIiiXjPk6dxzxOnJvhqLgzolF3R3ClJ5PNSk1IVgq3s29hAibhrZ9p6+/KLR+eyP3DH5biozdRcZTIRPSpWqWas58UqJcg2XP1a4Cc+B/y93yv/AlpZsUqlxK8u2ZYDIrjFscOoWAWi/bdaYsfj5KiBErEswSsgZuefvwUyEftRzCNg/p87LsNeLOMa7zCu2jPHRRD02Q2r+mh9vRXji8rYmQHgsl3pMQ07WmxBKxMRGYm4iDX8aPBRXPXsH+HHO38FIFUjAuXtzLOunVkJRyJOGXQXmECmRDy+2pUuoHTLBAgLtjMRNe3Ma90QDzPb6BtvOYADLGj6uK4SsUFKRMuZiJolCWJBwAGWd3NgKR3kjq1schKx1fC5sqYItjJ9gOx9U6lUOs2Any+feuQEAOAFbPeuLGa2mBLx8RPpgvnaffO4gilHn2GkzjeOrOAl//av8Mdfem5ir2/aQaq7Vs74RWNaqJuJqNmKDIh5o/ayA4uGeLoWhklSKlehRsF2w9cmpmiDwsaYoaNE1LUz06RvpwaJaNMmGwnnVl65z/BrAOQlKDSmdUyIDuFzVTU/l4FO6zSQ5SKeOt/FRi/ii66f+1vX8/ssGigRbZTEVIEjzNFx2yWpGufXP/nYJF/OBQGdYrp2I+DnzopkMWhaqgIAMyw7sXIlIm9nLn4tO3ekJOJVOdzNj77qKly7wMbJEkrEWaZErOx6IyWiZ277BQBc9AJgbnf552+LxSpVZiJqkm05oO+nrx9eBpDO33UFG4SYKRGrzhDM2pkLjkvMRGyPTyKSAiyIJm9nfo65vGaaAX7g5Zfjfa1fw8da78Qt/rMjBNzwvy9imYlHlyd/HMPgJOKCvp0ZEMtValKJapKIcTtTIl7jp7mVNzXSnyfObWKzn26Y82IVrXbmdJNzBl2sOiWiFI5EnDKYZCLunG3ySZNsoKOFlW6xygJXAk7YztyN8CiTkV93YIGTiMdWujhNA+icfBeGjtd6JqLm5yVme+1nx3JgKX39x89t8gxIncUYYaZlZxIMZJN71cIZyL54P/1oSiLedumOsZ7XZllMGQyQiEx58wxTIv75147g1Pku/td9z07s9U0z4jjhxRZ5E3PTogadxnGCTTI7SsyUiMOgXWQiEXfPteBphp9nmYg2ypiKSdq5Vja+yxDFCVbZ988ODTtzpkS0Zz33PPUY3xDUq7L8wo0S7cyiwrHqc1HHzgxkJOLRlU08fWoNSZKqw95w8wHcdd1eXL9/AZftnlU+BgB0WIv2VlGZiwijGMdW0znUL333LQh8D19+9uxAnpZD9Yg0IyZ49txGfrbVOicR9edPs5acHDFZ+DSKVdBi142MQNpcTn+WyEScYUrEqsaNcWy/lYBlIqbFKtUrEcsc1x1XpaToVw4tAzAjsQm2lHv6SkRmZw7aQLMz/hOzc7qxBUhEGr+v3DOHS3fN4obgCJpehFue+Z8jysPdQ+vJi1nnwNEtpkQMo5hnRpsqES/dmbnfakGst/EQt3cASIukrvKeBwBcmaTijOOrmZKw5ZESUeM6a6XrtVlv09mZFXAk4pTBJBPR87xCSzNZ3/Yt6n05kC3p/MTszOngcOLcJh/ort+/wC3Ax1c3eQ7YVshE1P28xMUwkYj7FjJi9Hw3HeTm2waTYCLcLBAdpEQs+rzoi5isbqTiKAuahG0Vtcrjx1Mi+9r987hid/qldPjsBvpRjG8cSZWyDx1dqZx0dwD6AjmYR3TQRoF2sYrB2Dpj8TzMFs7q+w0vSK7dlyoxKM+GSETdZmbxMW0qLNWZiOnzqzIRVzf6vLBDjIGQgZTbNkhEk+/jonKVjRJ25sD3uAq38gIIzWOjgqz7njzNrcxX752D53n43X/wUnz8HXdpqec7W2xsF3HiXBdRnKDhe7j54BJeec0eAMCHWc7vtOI/f+px/MP3f8m6Y0MG3XOQxgGZLY3GM5Nry9ZcI470MxHRZo0q54/nPFAMbKZlP2WUiDMgO3NFmYjUYqxTamADlInobVRKIpYujAHwqmv34t9+9y3836alKgAQs8/Lq5pE1LWf77wy/bl0cTXPy5SIjXgLkYh754D+BlpJek00v/kXOBg/P3Df4fXkQa5E3FokIs37fE8/C5vAG5oVPQqVIk7H68IcVZbFeZl3Anu8dF21JzqJeazj+Oomz0Xs+AZ2ZkYizmMTy+u9yuNgpgWORJwymGQiAsXlKofOpIMoDR5FsGdnTn8WHReRaGQZ3TPfxu75NvYzNcTx1U2tUFmeiWh5ckyfV5HCUlwMH1jqDPw8vdbF2TVGIhopEcmaaC8TsUilMvwZ3DImidixWPxginObfU7CX7N3AfsW2ug0fURxgsNnN/CNo+kEP06ynWiH6hAWWElJvaK7UUAZcEXnNGBXEaurvpkZWpC89MpdALJdZJpM7jIgEWmR04+Syu2/Wu3MGuppapxeaDe0YjjsKhH1NlOAzNIsUyJulmhnBjIFX9Vkh+5G0WuuT5Uqn3v8JN9UuXpvuqDXVcACQKextaIqRJBF+6IdHQS+h++67SAA4C8eOMrbIacNSZLgt+9+Ep/+5gk8dHR1Iq+Bl0zpkogSRUkpO7OluQaRiInOtXHZnenPRz+WVVUTeueAhN1WQonYYSRide3MtICfEInYIjvzZqVjiDemwvIH7rgc/+Hv3obA93D9gYXiPxgGt/9aKlYpIn13Xg58/58Cb/39ap6XKRGb0eTJt6cYiXjVnjlgYzn7RRLjkkfeN3Bfrkw8dxw49TguYnFTJ851rUSllMVJ5sTbNdfW5gkIl7BMxMM1KRE9rkRUX1ve7A4AwAv9Jwduv9Y7ghOrmzwjuxMQkaBPIs5iE3ECnLewTp4GOBJxymCilgHU5SpJkvAF5+WaJCKReOerJhGJHC2YWA2TaDewL2Ui3I6tbuKUhhKxZTEnS4Tu5zXXynJ9aIdr12wLzcBDkmQ7ZiZKRFuTYCAjOooW8SKJePXeOSxqBOyrMMtyirbCQvMJZmXet9DG0mwTvu9xNeLnHj85QLR/6ekzE3mN0wyRRMwj/uia01YiambAAfZC9wGBRCwYC4dVNS+9Is3PGrYzm5CIIoFV9bFFGvZYUpqrlO5k1VnSyEMELGciGljgC5WIlImomXk7/Li2Pq+iMf62S3Zgx2wTq5shPvS1NKfoaqaKNUGm7t06CzIC5SFezL6b33DzfrQaPp44cR6PPL/12jmrwPJ6n8caEHFfN2JtJ0fW0JyHUsUqljaKuJ1ZIxMRV78WaC8B554HDt07+DsiPYI2Jwa1wOyxbaRkQ3UkIiPbCggBa+BKxM1Ki8H4cY2hsPw7t1+Ce9/5WvzWD9xu/sfss606Q5BIXz/Q+Lyu/XZg/82VPK9PJOKklIinnwQ+/cvA+hk8JZaAbZxld0jHmpmHPog9WOF/xtuZ3/+3gfe+EruxglbDR5Kk4pWtglOsE2DYjq2DSwXRUS3KPM3SooBtklzvHRq4/Vr/ME6c6/Jxv+0bbGSwTYcFnxWzuHKVXDgSccoQMXWATiYiIJKI6ST4xOomt5aeONfFZj9G4Hu4eKfeJITszKsVk4hxrKewHLYDXLc/JRHJjv3kifNZI6kyE5EtLG0Xq2gel+d5+JW33Ip3velGvlDxfY9bmomwmm/rk3CzFpuMyUpaNLkXScTbxixVAYCZ1tbJzeJ5iPuzBfPlLPvrI18ftEJ80ZGIlUO8dvPOQyKrjDMRDZSINshs3UZ3UVWze66Fa/elYyG3M6+bk4iths9V2ipLcRlwVZGCHKXxvRfGUpX4Css907XqZBtGk/usAKCtUAwmSSIUq5hN24gYqbp9WvfYAt/Dq65N1Yh07pES0QSdhh1FZRUgJeLFO9LxfaHTxGuvTwsH/mJKLc2ie+XsWv0kYpIk2ufgjpl0LFiRkJ0U6WKiRLRVTBeHpL7RuM4bbeDG70j//6E/G/xdmTxEgJNS7YSRiBVdb0S2JZOyM/NMxI1KP7Oqsh73LXT4d5ERWAFE1RmCPitWKVMYM9bzttMxtMXOv9rxuV8H/vpXgQf+cCATkZOIu64CLr4dXtTF35n5Ev+zPfNtYO00cPoJIOrCP/YALmLila1kadZx4slw0VIHDd9DL4px/FwNxCgvVlGfg425dIM88AaJzeu8w3jm9LpAIrLf65zTbNNhKUi/M2QbUBc6HIk4ZTBXImY7C+e7If72f/4cvuu//g16YVZtf3BHx7idmTL6qkKkaVsZVuJxJSIjEYncnGsFyl1nykvr16RELLImAsCbX3gxfvRVVw3ctp81NT/BdswWDOzMsxaViLqt3mI72AvGtDIDmYVzK5CIT/BSlcyiQuUqX3omJQ3vuCq1mH7t8PKWXBxvZxA52Ay8XNskjZHD7cUy8MZxjbHVZvO57saDWKpxYKnDd5GpJfcM25HeZZiLQ7mEVeci6qj26LnT54/w6W8eH8mdo2iHHYZKxElnIlIu4GYY4/fufQb3PHmK/64bxjzn0SS3Tbz/Rq/qdmb96+E11+0d+PfVe+eMn89mzui4yEjELDv6u16YWpo/PKWWZtG9cnYCKg1x2C5SZfNiFWk7czqWmRSr8GK6qjMRY4NMRAC4+XvTnw//ORAJYzIpEU3yEAFOIrYskYj+pIpVSImITaxXqkRMx8FxlIhjPX8r/byqJxHLZz2Og6Cdfje0ku5kcuhWUjVb/+xhHF9Nr4Er98wJpPxO4KpvBQDc3MjmHrvnW8CpR7PHOfEIDjJLM8UabQWcXiMS0VyJ2Ah87GPRYCdW7ZO8unbmJiMROfalqtjrvMN47Pg5rgRteWTR17czkxJR9t1xocORiFMG3QISgmhn/vwTp3DqfA/Pr2ziiRPn8SzLFdTNQwQyEmuzL1eKlAE/roLDmhsiEa9jJOLehTbEeebugl2YuopVMkKg3N+TTTtTIm6NdmZOuBhkIo7bzAwIFqMtsNAUS1UIZGemNeV3vOAg9sy30QtjfP3wyshjOJRHkf2Ybg81rvEkSfgOro5yjwg8Ky3GJZSIFy3NYGm2ycfn586uZ0pEw8kkKZhVDclloHNc7UbAN3jOrPXwj37/K/jpP/wq7n7sJL8P5Z7pNDMDdqMrdBqnCZRdePejJ/Hzf/4QfuB/fAEf+uphAIPEWceQRGxbUsVmytziL6+7BBKxGXi41GBOQehYVPeOC25nFhwbr71hH+ZaAY4sb+Dh5yeTGWgTk1YiigryoGCesViQiVjGzjxrKVM6ZsdVtHDmuOrVwMwuYO0k8MznsttF0sMEjERsxtXamSdFSnEwe2LgJQh71bWme1yxNxly1GdkR9X2Xw+TIUcbbcqi61p3golIkiT9nj13DABw/kz6c9dcK51LkBJxZiew93oAwNVeGs/RCnwstBvASYFEPPlNHj11ZCspEbmd2VyJCGRjaS3KPFIvF4wZ7fndgzfc+J0AgOuDo4jiBPc8eRoA0CphZ54nEnFjMpEdWx2ORJwylFUiHl/t4hMPZQ1v3zi6gkOnqVRFXzUgknhV5iLqBmi3Gv6AJeA6RuA0A3/Avly0C0OPYbtYxUSJmAeyM9OAblKswifBfQvFKppZYPQ5NHwPN160OPbz8sbELaBEfDxHiUh2ZsItFy/hZVemk3xSJzpUgyIiO+BKxOJr/Fw35IQgBWarwBXZFcc6APo5YOKC+CBTSJEa8bkz61kmorES0c7mg04mIpCphR4+usqJv3f9nwf5YpdCtHdqKxG3RjszKSLvfuwEgFRp9TN//AA++MVDnDhrBp62K4AwY6lYxeTY9i60ccvF6fh++e4542MAMhJxaysRs/G90wzw4svTsX0ai7MGlYj1L7DEYVs3E1GuRGR25hLtzFWT2rydWcfODKSqmpu+K/3/L/53IGLHSEpEUztzY4hErEqJiEmTiHNIWJ4dNqvLKeXZgRM6roDsvxWTiET6+rWTiOnxdNCtdax/+we+gpf98l8hWk3Jw/Wz6c8rmXsou54yEvHSKFUt7p5vpW6XU49lD3jiET7v2kp25odZCdYlmhFlw+BjaR0kIjW6FxD0rfkdgzewiIf9OI0FrOPLz6QEcNNkDGLk/BzS68opEfPhSMQpQ2SgfADShRZNhv7vg5k0+6EjK3iWSlV266sGmoHP1WBVNjTrlgkAmRrvsl2zA/aUA0sZiairRLSeiZhUo0QkbJViFbK6NQsIgZsPLuEFlyzh+19+mbHCJg+ZEnGyTVrrvZArNa4VSgT4hATpNXrDgQW87IrU0vwFl4tYKYjIlhEWdLuOEvH55XQisWO2qaVWoWzYc5vVTzx0lYgzQ3ZmALiUtes9d2adq4dMMhGBNAoCqN7OHGq2/dLzU7s5kGbt/ZdPPw4gm+ztmJl8sQpvZ9bI0aTx78mT6ebdNfvmkSTAv/nzb+AoO//KjJE28jlN8ugIr7kuzQi8tkSpCiAqEbdWsUqSJLlKRAB40WUpifjVZ8+O/N12x4AScQIk4oASseAcpHzUM2v5Frz1Eu3MHUvFKglZ+EyWZy/8/vTno/8XeN/rgTNPZUrEknbmgJFSVSsRvZIb5mPD8xA10vlX3D1f2cP6GL9YZaznb2X230oft4LCmDIgO/OM18PqRn3z+C89cxa9zTUEvZRkWzmVrodfd+P+9A5cibgD2H0tAA8L0Qp2YTVT9Q0rEdmac6vYmdd7Ic9f/5Zr9xbcOx9F+bJVQle97Alq69Bvp3bmhTRO5FrvMF/HNz1zErGTpJ+dy0TMhyMRpwymE3vP8/iOhNh6+OCRlVJ2ZiBTw61WuIDWzQEDMiKNSlUI+xcywq1IicgzEW0rEaPxlIiU9UjYKpmIZAMvUhV1mgH+4qe+Bf/fm2+p5HltEqMmePJESgTsmW9hp0DS7F/ocNLi2n3z6DQD3HF1KsX/wlOnXQNYhSBSSEZkB0ImYlFm2fMr6YJZR4UIILW2QN0iXBaRZmTFgBKRvW4qtPjc46dwWqOlPg+0MVO1VVtX2UZq928cSUlEKpr6b3/9FJ45tcYJDV07c5aJaK9JW6udeah1+b3f/2JcvGMG/SjB1w8vAzDPQwTsKPjERnNd18OPv/oq/Oi3XIl3fPt1pZ5zhrdXby0l4vJ6nxO0Fw1t6r34sh0AgK8cmnISca3+7y3xHCzaXKY5LM1ph5HZmUs4OSy1M2vbmQHg0pcBf+/3gM4ScPQrKZF49pn0dyWLVYIkRANhhZmIZI/VL/+rGgkjBpJudUrESSn2CI1Oem63E0tKxLoVls30eGbQ5fl9deD8Zoh93jL/9w6s4lXX7sGP38Vy6EU7c2sW2HEZAOBa70g2hxKViP11XNlIbbRbRYl475On0YtiXLJzplQuMVCs6q4UmsUq6GR5+quzlwO+D+y7AQBwrX+E/67Bogf0MhHTuXInST87RyLmw5GIUwZStuksWgi0CAOyydbDz6/iWW5nNiMRs3KVCpWIiZ6dGcgWmVSqQtgvTPBVzcyAkIlouVglSvQtYXnYtzh4HCZKRJvlDyFvZ653iKGF5qTtzI+xPMThFlLf93gu4s0H0y++6/cv4IYDC+iGMf78gSNwqAZFlnqRXCwqV6Gd5GGSQAYaA6tUYxOiUpmI6et+y+2XAAA+9c0TfFKk22I8/LhVhtMDBlmPQyTiW196KV5+5S6EcYK/fvxkpkTUtDMT2Ron1Vtly7QzA6k69Jp987iaqfbIgmSS2UaYsUAiiteLTiYiACx2mnjXd9w0srmnC5uN5+OArMx75tsjStEXXZoqJJ45vY7T5yfUNmoBSZJM3M4snoNF1xc5AE6v9XLzGynSpUw7c+VKxIgWzoZzp5veDPzkPalKau0k8JX/ld5eUokIAB30Klci+pNSIgJIGDGAXnVKRE6OTsjO3OgwsgPdSkUPE1NYNsnO3OORK7bRj2Js9CPsR7bZs8dbxX966wuzsWVY2bs3JamubxzBnVftBrrngZXn0t8xFdxlYWp33iok4mcfTbOjX3P93tyyQR0s1ZiJ6HElYsG43JpDCDY/WLwyvW3vjQDSchVCp88+39k9xU9OCt9oA0DCY3IcBuFIxCmDqRIRyHIRAeCH7rwcc60Am/2YN+6Z2JmBTIVT5QI6NrAzk8rw5oODGXuiaq8wE7GmYhXeOl1yQB9WIhrZmZuUbWYhE5G3M5c7rrLgE/sJLzS/9twyAODWi0cbp69hOZ0vvDT9ned5eOtLLwUA/OEXn5vKJs9JoMhSL+a3rhUQYs8vkxJRl0RMJ1pVqrGBdPGuSyKKqrWLBCXi627cx2/3PH3FHsF6JmLBIpPszPT9dOWeObz8yjQS4BtHVngAti45OtdqgN7K1Yonxia5gaIS8SWX74TnebiKkR9UzFFGidi20M4cllAijouOpWzHcXFYYmUGgKXZJq5hRPBXpygXcXm9P6BErmuxL0J0pxQtiOfaDRxkY/dTp0YJpDLFKtn8qWoSsYQSkbB0CfDGX0n/P2ZjmXEmYvYd10G/suMLiJSaVCYiAK+dbmD4/fOVzbMyJeJkilWaHWb/Ra/SXF86rqB2EjEdR2e9LndL2AbN/0QlYgshdjUEdaeoRAR4LuLPv7yBn3j11cDpNE4Fs7uBy+8EAOzeeAoAsLoZWom2MUGSJPgsy1t+9XX7Cu4tx1KNmYieZiYiPA9rHlNW7rk2/cmUiLe2ngeQkuzNPlMgL+wvfnJGIvqI0EbfZSJK4EjEKYPuAlOEGLD6rTfsGyi42DXX4gtiXdD9z3eru+h0LXwA8G++4yb8m++4Cd9+0+BAsX9x62Uimiwy87B/mEQsU6xi0c5c1qZdFjw3a8JKRLKvUbC+iH/5huvx//7tG/F3br+U3/Y9L7oYrYaPR55fxTeOTF+T5ySQWerlmYhEup8tmCCYKhHnLSkRRcFk0YbKfLuBPfNt7JlvDWSnvu1bruL/v2OmafRdAdjJRDTJ2JsdshxeuWcONzOy/htHVrm1UleJ6PuetcbBskrElzFSlBRUjx9PiY+tkokYRfoqsKqwVduZSWVyyY78qINptDQTcUqf/fJ6v/bNL9MNc1L1UtSICE4ilihWqZrUjuOSSkTCNa8Drv627N+mSkTP4+UqHa+6cguuKpqQ7RcA/E5KIs4lm5XFcfgTajEmUJvxDDYrjXoIJqxEnEG3ts0JmqddHCwP/mLtVPb/IyRiSlI1TrMcxJPMyrzneq6Ca595DItsLjjpXMSnT63huTMbaAU+XnH17uI/kIAyEesg1bxEf+Ohs5Ae08GrX5DesPMKAMBljTQDkhPEjRmgrVHi2czEU3PYtBJNNA1wJOKUITQsVgEypeGlu2Zw1Z453CKopy41tDIDmRqu2mKV9KeOYu+6/Qt427dcOUIeiIRbUQ5Yy2LYvogypK+IuXaDKz8BYKGtT/jaVO2RnXlSSsRuGA9kJtWJtW6IR5hy6MWXjZKIl++ew4/dddWA6mHHbAt/6+YDAIAPfulQPS90ypFZ6uXnIFkziqwKGYmomYnISUQ7yjaguKyjEfj42D99FT72T+8aaKy/46pdXKVtWqoC2MlEFC/Vog2V+fbgQv/KPXP8eB47fo7nKJkoLG1ZdCKNc5AgEoTDJCJtZpVRIs60qlfwiaUW9SkRibSJt5Ra+zlm66UmzmHQd8B0kYjpMVNJTi+KK89ILYJJ2R6QRYs8maNE3ChRrDIjbKZUeT4mZTIRh/H6XwKIhDRVIgJcDdZBrzLS3jcgBGzBZ9bfOW8D5ytan0wsO5CeXygiqVKJyFun686wbBGJWJ+dmQiiixsrg78YIBGX0590PTElIi9TOcV+7r2Oq+Bw8hEcZJtLk7Y0k5X5pVfuHHDhmII2Z6t2beSBSMRE49pq3/wmYG4fvCvvSm9glvJdUZpLuY+s6gv7042SIvgBJxJnPUciyuBIxClDXCJj79tu3I8fv+sq/Pu3vACe5w3YgC8vQSLayAMbt8UYGCQR92gqEW0Xq4xLIgKDWY8mSsQZq5mI5tmcVUBUKU3K9vbA4WXECXBwqTPSnq3C9zFL859/7SjuffK0rZdXCQ6dXsdr/+Nn8T//5ulJvxQp6NoVCbRh7NC0ZmTFKnqf5yJXY1c78TApEwCAvQtt7F0YHOs8z0vtNwCu3GPelGsjE3GgabWAHJ0VJsD7F9uYazdw8Y4Z7JhtIowTXhC2U1OJCGSfV9X2c5NNvQ47T+daAW5iboCrhsLPy2Qikk3aRiaijpW0KojHXuVieRx85tET+MAX0k2fayVZj6RGf+C5FR6xsF1xbGUTcZxwJeK1+xf4+JqXNWgTpi4OKhLIVyKmY5mRnVnIUq3yfCQSEeOQiPtvBl73C8BldwKXv9L87xmJOINehXbmySr2AMBrMSUiNivb4KPswEnZmUXlXpVjfDCp42LH0/b6OHOuHuKN5mkHgmES8WT2/8NKxD2sIOz8sZRgJDJxz/XAvpvS/z/5GC5mDc1HlyerRLz7sfRYXn1duVZmAt9437A/3nua7cwAgDf8MvAvHgMWL0r/zX62ojXMYz1TIs4f0H8BzNI8h83KNh2mDY5EnDLQYsyElGoGPv71374Rr7g6DRsVlYimeYiAHSsfzw4cg2w7YEIiNuppZ66ERBRs2mbFKul9baj2aLFU1M5cNTpNn28yrVnIetQBZV+9KMfKrMIdV+3GLRcv4nw3xN//7/fhnX/29S276Py/Dz6Pp06u4d999BF889jWtF9zO7Pi2iISUdWKnSRJpkSUWBaHYatYJRIUL+OMGd9120H8/ttejn/3Peat6LNMCVipElE4zQvbmYWFPin1PM/DLQez7y3Pg1EMhz0lov5mCmUXvvjynVxFf3BpZoAEL6dEtEsi1oWO8D5shVzETzx0DD/+e19GN4zxuhv34c0vPJh7v2v2zmOh08BGP8I3j1XXClsnkiTBr/7lN3HHuz+Ff/GnD3Al4qU7Z7CLKX7rLlfh56DmHIOUiE+dVCkRDeZPwrVY5UZspkQcc3n2yn8K/MO/BDoa1r1hVK1ETBI0E1Ys1NDfWK0c7fQcmPc2cK6iTTCfFHuNCZGjnPDtVkpmE+lbe+u0UOyzdr6euSURRPu9IbX4OlMixjGwyQhGIhE7i8Dixen/n3osa2bee11qpW10gHADN8+mj0kb0ZPCk2zcy3NHmSBz79hXIvqsTVlbvSxuaLbmeJTDCxbW8IIlRuLq5CGKj4GURFx1JGIuHIk4ZTC1eOThmn3zfOFSxs5sJROxguPaOdfCD915Of7+yy4rtPHVVaxShvQdhqiwXCiRiQhUnzM1qWIVz/MwR3bL7mQWml95luUhGn5Z+76HD/zYHfj+l18GIC1Z+YsHjlb++qoA2bXDOME7/+xBHnK/lRAWZCICYr6LfAG8uhlyNcZwkZEMNAau96JKieAqs+i+5do92Kd5PCLo+qpy4TygRCwiEYWNElFJefPF2WJ5yTDrcXEmfUwVmVwGJmTbG27ej5dduQs/fleWWen7Hq7cnakRy2Qi2sgSpPOwWSOJ2Ah8/n0y6VzEs2s9/PM/fgD9KMGbbr0I7/2B29Fu5H82vu/x74LfuvvJLWXF1kGSJPjFjzyM3/zskwCAP/vKEXyGWeMu2TnLN2KKcmWrhrGdmVmvnz2zPhJTQ5EuJnbmRuDzeWKVkTBjFatUBSKmvC56VWwyRz34YI8xSRKRtTOnSsSKSESu2JsUiZiu0VpehG6vugb4rFilZjtzo4PYT58zPF+PI4cI5d0JIxGX0jk4VyJ2VwA6f8WMUbI0H7kfOJOWqGDP9akVlhV83JqkCsVJKxFpg9S0SG8Yuu6dKuCRKrusGnYx3dh7/9+5GD/2IsZlLFyk//dsvJj1upXyGdMERyJOGbh9agzyphn4vO3ytkt2GP/9olU783iLll988y149/feWni/uopVaG42znERsdHwPbQV1s1htBuZaq/qhub+hOzMQKbGnIT8PEkSfJU1M1OgvgkWO0388vfcih95xRUAgAfYY201EIkIpMrLP9yCOY46uZw6EyLaQd4529S2u4mK4CotzQNkW0020mHQQrtKpa+4SC1uZ87e26sFu6+oRNRtZibQ7nrVu81ciajxfXzNvgX88U/ciVddO2g3IrUlkOUbmiAjEav7LutXsPlVBpk1e7IK7d/66ydxrhvihgML+E/f90I+X5Dhn3zbNWj4Hj7y9efxO59/pp4XWRHef88z/DVTBuKhM6kS8ZKdM3xDtm47s+kG7L6FNubbDURxgkNn1nDyXBdfZTmVZYpVAEuRMGThG1eJOA4amRIRqIC0DwUCpaGn5reCNtmZq8lEjOMEASOXas8OJAgFEOHGqFW/DNLjIiVizWS256G7cDkAYG7t2Vqeks6FXXFawoEDzKFBmYhkZW7OAQ1hbrGHkYh/+a+AOEx/v3RJetvVrwUAvPqJ/4Arved5VvMkEMcJn4fSXKcsiITshbF1R4DPx8KSBD0jEVvrxxGsHU9vmy+nRNzsx9adidsRjkScMkQlMhHz8J7vfzE++Y67cP2B/JwfFWyQONzOXNPCmWciWs5eqlKJON9pGOVTeZ7HLTlV5yKS+qpuJSIAzDG75SSCcJ85vY4zaz20Gj5uFkgNU9zKIgUeeX7r2d82+xGeOpVOVn/i1alq6tc/8diWU9hkdmaNTESFiuZ5toN8QLNUBUhzGInQrzTWgb3HnjdetMM4IMvfeoVK31AgEYsOa7Y9amcGBmM4TCfKttqZadI5zvh+pUCUlrIzN6u3M2fkaL1TyI7FHF9dnFjdxO/e8wwA4GffcL3We3D75bvwrjeljZ3/7qOP4FOPHLf5EivF3zyeLqT/2euuxf/44ZcMzC0v2TnDCfu67cy0n6I71/U8j286fPPYObz1t+/F9/zmPXjo6ApXJpooEQGh+dyGndmfvBKRk4jjHl8//Q6NEw9+Yzwl1FhokZ25mkzEKEkmlx1IaLQRg8UvdashEaMkQcNjSsQJfF7JrjSzeffmc7U83/luH230MBezqIP9EhJxZshddPmd2f/PHwC+9V9nltrX/GvgkpeiFa7ifc1fQ3d1cjnn57ohaHpu4lbLw1wr4GOubUszL2Mqq/JlJCJWjwLnjqX/v2CeiTiLdPxac+UqI3Ak4pQhy9gb76Nd7DSlQeFFICvfVlQi6oLIL9s7DzQRHkdVxEnEEo1bM0QIVLwo62tYSW2B3odJDPj3MyvzrRcvKQs9inAjK1Z45NjqliPnHj9+HlGcYMdsE//8269HM/Bweq3Hw/a3CvqcyB7Pzkx5iAcNSnKAbByssqzDtEzABrJMxOo3iRoaRR3zA3bmjGC7fNcs/51JqQqQFavYy0Qcg0TcMx6J2GlaaGfWyBu1AX4s4eRIxPd85gls9mO86LIdeO0N+7T/7odfcQXe/MKDCOMEb/vdL+Of//EDtav3yoDG9RdfthOX757D333Jpfx3B3fMYOccszNPSolosFFJuYi/8cnH+EbYPU9ki3uTTMT0/llDc1WopFhlXDAScamRjodjk4hheg510YQ/gTkhh1iUUMH8MBIUe7Xbfgmehy7SXPRwsyISMU7gkxJxAlmPwd5rAACXxEdqyb89vxliH+UhNjrA7vT5uZ15uJmZcON3AT/6KeCffA34598EXvFT2e+aHeD7PoDe3MW4yj+G71j9oMUjUIOalNsNv1QkigjP82orV/F4o3vJ17xAJOIR4Hx5JeJSI1WRVp1xPg1wJOKUgWcwTW6NyYtVbCye6yIRiQDaDpmIL7hkCe2GX8p6nk2CK1YisuOaBNlB598klIhfe47yEHeM9TjX7JtHM/BwbjPccuQcWZlvPLCIVsPHNfsWBm7fKtBRwy4Z2JlNmraBLNbBhiK7bhupCDuZiPrHRQv9wPcGMnt938NNB1Py3TT3h9uZKyYRqygguUogETsl2pltqKUmNb5zVeWElIinznfxgS+m0Q0/+/rrjZX///4tL8AP33k5PA/43185jH/8B1+x9VIrQZIkvETlkp0psfRPvu0a7Jlv46VX7ESnGQhKxK2diQhkuYhPnszIlgcOLwNIBUREUuuC7MyVZiJuIRJxIUi/u8a3M6eL8E20JhbDAQDopGr1Xd65SqIrwngLKBEBdP10bhJ11yt5vDBO0ODkaP0kYmtf2nx8pXcMp2vYnDjXDbEPy+k/5vcDc2nJaKES0fOAS14C7LpysNSDML8Pay/5SQDA7v7zExME0ObouFZmwpKGg6cK+GDFKmMrEZ8vqURM1zY72WaKIxFH4UjEKUPEMxEn99EuWCBxSBBYt53ZdiYiPfw4i8yDO2bwpXe9Dv/577/I+G9nLdnDsmKV+s9DIjkmQSI+dyYlnK7dV07FS2g1fK6a2Grk3MNEIjK15I0XEYm4tazXOmrYHRpNc1yJqNnMTLDR0FxFwdS4sJKJaKBs28WUT1fumRsZX267JF0k7hMa63WwFdqZZRhbiUjtzBWq90ybcasCqSgmpUS8+9GT6EcJbrpoEa+4Zo/x33eaAf6/N9+CD/7YHQCA+54+PZHvqWGc2+zjp//wqyM26+X1Pm9hp/HvoqUZ/PW/fA0++OOplY9IxDM125nLbKiIGaqEB4+kraszzcCIFAYszZ84iTj5TMT5gCkRxyUR++m8aBOtiW6AURHGdd5zOLcxfkZdFAlKxMaElIgA+l5KIsa9ipSIkUiO1k8ientSJeAV3jGcOW9/XDm/GWbNzAsXZSQitTNvLqc/O+YRRbOLab/AbLJeuVhDFyToWayIRNxhab40DJ6JqNvOPAxqzz77NLDB8i7nze3MO4L0HNwK39VbDY5EnDJUlYk4DhbaNotVKntIJah1b7jFr2pEFSk6FjtmbaSEGQt2HCCzkuoUClSNSdqZj6+mhNN+Q9VaHm4iS/MWI+e4EpGRh/Q6H35+ZWKvKQ+8WEVxXZBiTTUZ4kpEwyZjHutgo6V+knZmGjMqzUTUV2S/6NKdeOcbb8Cv5BRk/dhdV+Gfftu1+AevuNLo+W1lIlahRNw11+Kq1lIkYoOIjgpbwmNqZ645E7FZ/bGY4LOPpfa2b71hb8E91Xj5VbtxcKmDJAEePDz5cfOvHjmODz9wFO/4o68NXAOkgt+30B6wwc22GvycJjvz8novLRY7dLaW794yBD1tzAHA33tJWoDw7OlUvWWahwjYiYNJEvbebYFMxHk/PRfGnh+yYpVuUm6eWhl2X4vQa2HO66K9On4ZXBjHCLzJKfYIfT/dNIsqykQM43iyNm1mJ77UO4Ezq/bnv+e7IfZ5y+k/FvYDc2x8XzuVZk7JlIgaaM/tAADMexs4XQMhmgdyWCyOmYdI4PNmy0pEL0nPQc8veQ4usibmU4+nP/0mMLtL/+/JzhykGw6uoXkUjkScMmyFRSYtyM5t9hHH1ci3ay9W4XZm2yTiZD8vvpNece5IOMHstknamYlENCWc8sBzEbeQEjFJEv56yDp64xYlOzMlovwc3DmbLYAB4Lkz6/iVj30TJ85lbZJUrHLRDrPPdN7CZsqkxwsAmCOSvhdWZs8xKerwfQ8/8eqr8ZIrRieD+xY6eMe3X2dsPV/i31lVtzOPv0nkeR6uYuTHXKnc2+qLVaoojCmDjoWSGF1EcYLPPZ6SiK+5Xj8LUYYXsPiRrzM77SRBi9vVzRD/43NP8duHrcx54ErEtT7+4oGj+J7fvAe/9vFHLb7aFDTHMCmYunLPHF5y+U7ccdUu/Ks33jjwu5lSUQHpeFXp/ImCsreAnZmUiGNfb4xEnLgSMWhgeeFaAMCuc+Ofo2ImYunctgrQD9LPK+lVE32TFsZMqJ0ZAOb3Y8ObQeAl6J58qvj+Y2KARJw/AMwyJWISpSpEnoloTiJSI/g8NnBqQg3NqxuazcxP3Q188b8DBfO6ujIRs2KVkucg2ZlBrTIH8m3nMrAipkXfZSLK4EjEKcMkyRsCDTBxApyvSOEW1Vys0qqpWGXSpMBM006xCuXRjWPjKwtaaNdNInbDiOdC7Te0U+ZBLFfZKji6sonVzRAN38M1LF+KXuehM+uVNB5WhUwNKz8HKdtlZSPd8Pjtv34Sv3X3k/hf9z4LICVNyc58kUE7M2DJzszHwcl9ddPGQ5wA3YqU2lUo9saBLTsz/z4eU5H9jm+/Dt/74otx13XmCjib7cx1f15WSBtNPHB4GcvrfSx0GnjRpTvGfrzb2GM8sAVIRDHO4X1/8zROnU8XTaREvGTnbO7fARmJeHath498/XkAWcGYTZQpLWoEPv70J1+BD/74ndg11xr4np5tmhP0szwftsK5BtmZt4ASccYjJWI17cybaNYmBJDh/I4bAAD71h8f+7HETMTSlssKELJMRPSrK1ZpTPK4PA+nWqlSGKefGPzdk59O23YrxLkBO/MBoNEC2sy6vHZKXqyiAyIRvY1arNl50LIzH38I+IO/C3z0XwDPfUH5eEsaMUBVILPUl1QidnYATeG7y6RUBeBKxHlHIkrhSMQpQ1RBUce46DQDtJmSryq5c1zzoiXLRLQbhFs3OTqMGWvFKtUsnstgUnbmE6vpF0274VcSYEx24WdPr2+ZLI5HjqaE5jX75tFmNklxMfbosa2jRqRczpaKRBQ2PM5thtza9sSJ8wBSUokIi4tKtjNXOfGgY5pkwaXYYFrVuDHp1ulFQb0cVrhxxDOKxyR9X33dXvz633thqXGFyiLCOKksnoPG97ozbyepRLz70VSF+Kpr92gpZotA+Z0PPDd5O7OoKFnvRXjvZ58EoKdE3DWXZSLe80SaIfb0qTXrJQJVENlidnEpJaKN+RMnESc4yDMScc5Lz4vq2pknrEQE0N1zEwDg4OYTBfcshqhEnKRyNAzSuUnSq6hYJcramSdFjq7MXgYAaC4LSsRDXwD+1/cA/+cnK32u890Qe6lYhYo3eLnKybHszGinm+zz2MDpiSkRyc4smT/0N4H//WNAxF7fs59XPt6OWTubrsMYW4noeWnGJcGkVAUYaHMHXCZiHhyJOGXIFpmT/aKuWtlRu52ZLRL61jMRJ2xn5hlT1Q6OkyxWyUjEeheax8jKvNQxDmjPw+75NvYtEDm3NdSIjwyVqhC2ovW6r2ElbTcCrqxb3uhx5c1TrL2TWjz3Lw5mgukgUyJWN9GKeebt5L66A9/jm0RVEfWTViKKO/SVkr5bwBkwL1igq/o+ntQ8w4aqUheUh/ia68a3MgPALZcswfOAI8sbXPk3KZCC/jXXp0rXP/jCs+iGkZYSkRaUvTDmJSznuyFOWj6mKq4tUtMD5TIRZy00n4PKBLzJKduoWGXGY63KFbUzTzwTEUC092YAwBXhk2M/ViiSiBNUIkbs86ICm7Efb0CJOBlydH0hzTWePfdMduNz96U/l8fPsxRxfliJCAi5iOOSiGRn3sQpISanTuS2M0d94Au/Dfz1fwA+9BPAiYey3z33ReXj8UJC2yQiPwfHEGVwSzNKKxFniUR0SsQROBJxykD2MlIJTQpV71TENSv2OIk47XZmS0pEHQLHFsjOfK7mXSNeqrIwfh4i4UZeWrI1FH5PnUpJtev2D7ZPb7XXCWRER5FqiCZEZ9Z6OMIWzU+fXkMcJ3jiRHo8w8erAxt25iwHrLKHLAW6xqpTIk5uvADS8X6OjYVV7q5PenwH0vOfvo/PrFVjp5rU55UpEestVjmz1uPZhWUs5XlY7DRxFWvennQuIjlGvuu2g9g918JmP8aDh1cEElGuRJxvN9DMcRw8fbIaa6UMUYlMxGFcu39MEtHG/ImXCUxeidhBVXbmLdLODMC/KC3k2pecBtbPjPVYkVBAMskv5aCdjiO9jWrmX2EUI/CYknhC5Gi44yoAwNKGQBgeezD9uVntZvVmdxNXeMfSf+y8Iv0pNjTzduYd5g/OSETfS3D+3GRU56tsDro4I3yW978f+Ni/BD79S8DD/ye97dX/Kv353BeybNYc8Bgg23bmpIKGcGpoBgZViTpgmYgdRiJupbimrQJHIk4ZaMeQLEyTQtWZCXwxVpMSsdWwn4mYJAmod6au4xqGlUkw9AkcG5hvp8dUt5352Ep1zcwEIue2ihKRmooPDpWMiErE+589i1//5GPWJxhFIFtq3gJXxBLL9Hr8xHn02N/0whhHVzbw+PHU1iwqVnRhRYlYopHUBmYrbnWn8WIcQmBckBpxtcLPaysoEYHMclqVnWpScRW8nblmJeJ9T51GkgA3HFgwLu1R4TZWrjJpS/NZViy1c66Fl7LCoi88fUbLzux5Hm/rBLL4iKdP2SURwwqI7EE7s/lCtWOjmC7eOu3MHaTjxdjHR+3MaE5srkuYW9yFZ2OmJiZSqiR64dZQIrY6KYnYXa8oEzESvtcndB56e9ICnL29w9mNnERcKSz/0EUcJ7i49wzaXoi4vQTsTBWQmZ351HhKxEYHMVMVr6/az4rNw4idOUmA+383/f8r7wJueQvwpl8H7voXqQp54yxwWp4ZumMmHe9rK1YZ5xxcFO3M5ZSInThd99QtTNkOcCTilKG3RZSIlduZaVOsdiWivVwfIkaByZECvJ3ZUrFKEYFjA/Pt9Nyrm0TMlIjjl6oQDrCswbNrW2MHjIjS4fbpm1h+49cPL+Mt770H//lTj+MvHjhS++sTQXmmRdcWKREfOjK4kH/61BoeZ9mI4mJTFzYyEftbJK5irlW1EnHyZJuNchWeUTyBcVDEbsqtq0iJGGpeW1WDNkfrtjOfYGP71XvNNxNUoHKVSSsRabN3x0wTL7syJRE/8fBxbk8+uENdKrWLkYi+B7zx1tQOaJtErMKdcq1oZzaMqxD/plo78+Qz9ohEbINlIlbVzpy0Jq6iX+g08EhyOQAgen48EnEzjLJilQl+Xq3ZdH4Sd6tRIkah8D0xIXK0vT8lEXfHp4Hu+VTNeuqx9Jdxn59T42KtF+IW/2kAQHLRbVl7b1V2Zs9Dv5GSUZtrk9ksGrEzP/814PiDQNAC/u7vAn/nfwIvfRsQNIGLb0/voyhXWaopEzErVqlIiThfLhOxxUhEZ2cehSMRpwxbR4mYTiorszNzW1glD1eIrFjFnhIxFEjESU2saPd9veJFWX+Ciqk5pkSsu0nrOCtWqVKpQkRUleqoskiShOc+DjcVX7F7Du2GD+GUtt7cVgROZDfUi8ydc+l7/I2jg2rPp0+t8YIV0famCxt25m6YXqeUSTgpzFas9s0yESd3XIsWSMStpkSsjEScWDvzZJSIdA3TNV0VXkDlKodXrBeRqLBMSsTZFicRH3huGQCwb6E4D5bs8rddugMvvixdaD9lW4lYwYbKzrkW9syn10aZYpVZvplS4VyjCvXNuGAZe+2EKREra2duTVxFP9du4OE4JRHDow+M9Vib/UhQIk7u8+ospoRXs7dcyePFohJxQuTo0q69OJ2wzdszTwInHs4IdiBVI1aA890Qt3ppeYt/8IXZL2aZEvHxT2aEZZl2ZgBxKz2O3oRIxJF25q/8r/TnDd8BzO4avPOlL0t/HpKTiDvqbmduVJSJaKxETOf9zShV5LtilVE4EnHKsFUyEbmduSK588SKVSySiFtLiVh1scoklYisWKXiYyoCEWz7F6skEasnospiZaPPs8j2LQ6qLRuBjx955RW49eIl3HnVbgDgKpZJgTfIFlxbtOHx8BCJ+ODhFRxZTncgrymhQCICuMqJB43vpiUvVaPqGIStoEQkq8/qRnWfV7QFyFEA2DWXXq+nz1ebiVj3+E7nfbfmTMSRRVhFuPGiRTR8D2fWenwTqm6IhSg7Zpu48aLFgTIelZWZQBtnr7luH65kOY+2lYhVjRkUVVEmE9FGprQXbwESkSkRm1WRiLyduVmbEECGZuDjSf8KAIB3/BtjPVa3H008OxAA5nem5MhCvFpJfMqgnXkyx7V7roUnk5QA6h/64qj1vKJcxPObIW5lSkTv4IuyX1x5V9qsvPxs+m8v4E3LxmC5iOHGhEhENqdZ7DRTReeDf5r+4sU/OHrny+5If6qUiDOZyya0tEZOkqwh3BvnHBRzEEsqERsRszNvgXXYVoMjEacMW0eJSAuyquzM9SofSOnTs9jOHCWTVyJOZSZih9qZw1rVHcetkIj0ZT15JeLzzMq8a66VS2K984034sM//S14yRWpEqVqYtoUtAFQlNtGKhpSN5FV+9PfPAEA2DPfxs65Vv4fK0AEcJUqUhrfJ65EbFVL1E+6nRmwY2feKkrEqu3Mk7LVT0qJmC3Cql1Qd5oBzxOs6rMxBW30el66yAx8D7dfntn2VM3MhH/ybdfin7z2GvzYXVdyEvHZ02sDG6VVo6o54YuYcvJiDbJ0GLMWMhETTiJOsJ25mX7mzf+/vfOOc6M61/8zoy6ttL269wa2sWmmh04glOQmEJIACYE0kksIye9yL6Gkh5ubQMoNSbgJkEZJIySEZmIgYAwY27hj3MsWby/qM/P748wZjdZbJM1Ic7R+v5+PP7vWaqXRTjvnOc/7Piq751uuVNHTmePwlswIMBYHfCy0w929w1JvvWTSdM5Kzt2TfRHmmquRBoxxqBXUtPMiYsTvwQsqK61V1z+Kw+++mf0Eu5yI0SjmS3p4i9mJ2LgQ+PxaYPl1bN82LMiUOueJ7Gfioxbvd8RxnlXOvOWvQKIPqJwKzDjryCdPPoF97doBDHWN+HrmlOf+Iglriin53FI5c/V0JgB7KzJ9LnNFFxFlNQUP0uREHAESEScYcUGcKranM5c4WKUkTkTFeScin5RNxHTmlKIZzq1io2maMXgb3i/QCiI5EXN1WnJ3huNOxDzTmTmnz2EDjS59Uj+ngFAVAAjrx+FgIm1cv6yScZo7XBLGFx8SEyOdGShST0RBeljaXc6sONSuwudQT8SBBDsm+KKOndg9VsoXXpJWGfAYPad5STOQmxNxVn0Fbjl/HoJeN1qqAvC6ZaQUzUi7LwZ2HYNfOHsOfvvJk/Ch46fk/buBIvRE5CKiy+2kiMju8R5VF//sSmfWnE9nBoCUnx3fspoCUtGCXyeRNJ2zToq+QVb9UY0BWxzNqmL+XM6MNWRZwou+s6BqEnytbyCx5R/ZT0jYIyKq7Vvhk9IYkEKZUBVORQPwvvuAL24GPv5Uwe/hDjARMaBFiya6jUYyrRqLHJGAG9j1T/aDxR8ced8Ga4C6eez7UdyIbpdsjG+Ldd9KKRrcejmz253/Ir5BsAa46rfAhx/Jv+WALiICQAgxIeZhokEi4gQirajGwMrpSabt6cxaadM7eZlWStGKtnKU5UR0aFzFHUX2B6s4k94JZEIfgNKFq/TH0qOW+lohUoRwjkLhoSrN4/R8DBXpmMoXvgDgGefk4pN4zulz67P+X0g/RCAjOGiafY69hOE0d3aRiLt97UqrE8GJGAnY7xwVxolYMTHSmR13IgbsFwqKIV7nQ89Qph8iJ1tEHN+JaMYlS5hey35nV+egDVs4MnYluge8Lpw6u85YOM73dwF7F2FVlU+cnRQRmXDMy/isnm+aqZzZ6XsXAHh8FVA1/bhJFH6MJlPmABIHPxcXEaVBY5xmBd4TMQ1n95Va0YxX1EUAgMlSJ3uQ9yq0yYno6XgbALDHM2d0p2GkBfBXFvweLl1ErEAMXYOlbVthrmIK+z1Aj16e3bBw9F/i4SpjlPvzcBXeT9duYqZ+ox6vxcW7eRcBM07P//dcHsDF5nMhxDGYcL4iTDRIRJxAxE2uK6dv1HanN5XciWgSYYuV0KyYJs6SQyUegWKU42iaafJc+kuMS5aMMqMhm5xS48FdelVBewfJ3IkYSylFdcXmgpHMPI6ImJlYOVzOzHsijjM55D0ROcdNqcrqCVaoE9HvkQ3xyC4RWBQnYqZ/oE2LRA6JUmaKms484ZyIznwufm2NO9UTsRhOROO4c6qcWU9mNi2mLJ5cCa9+jcnFiTicmXXsmlnMvogi9FE1FmHtHD/pAo7LSgmfVfTeb24lBhmqZZFUS2XKmZ2emwBAOODFEPRxTNKKiOh82S8AQ0SskobQ0WdduFcUtr8Vh0XEhS0R/Fk5zfh/Et6MwGVTT8RgJ+u1uN8/z5bXGxG9J2IFYiVvW8HHM2Gfm92ve/XS7appo/9Stf6zvgOjPiWTe1AcYS2eUgwnostl/303Z3Q3YlBKIJ5SHZ+HiUZBM5GXXnoJ73vf+9DS0gJJkvCXv/wl6+eapuGOO+5Ac3MzAoEAzj33XOzYsSPrOd3d3fjIRz6CSCSCqqoqXH/99RgcLN6q5dFAwjSQ8TrcvbhYTsRSTVrMf79iXTQUAdw3xeiJaO6D5ESwCpApaR4o0cpRMUqZgYzjC2ANoJ3EEBHH+Ywho1+e0+XMufVErDZNnl2yhOZKv9HXCwBmN4QLen9JkgwR2K5eKnFBnIg8YMIu154IASR29/EFBHIi6sEqdvdELPXn4gsU8ZSCZFrFnU9swvNb2ov+vnwRwO5gFcB5JyJ3kpjbOvjcLnz6jJk4cUaN0eM2H2bUFz9cpdRjwpEIFmHBTOOhRR7nRUQACCNquX2AkmAlw3F4CwqwsZuw341B6OJ4onAxKrsnooOfy19lfDvQfdjyy6kK+1yqwz6j735gMW75wq1QXGxf7XZNzaQJ2+RErOxlbrv20HxbXm9EuIgoxdBpU7hZrvSb713pJDBwiP2gaurov1Q5mX0dQ0Q02nAUKaGZJZ/r1x0nBXofWxALgc1/nJ6HiUZBV4ihoSEsWbIEP/nJT0b8+T333IMf/vCHuP/++7FmzRqEQiFccMEFiMczNuuPfOQj2Lx5M5577jn87W9/w0svvYQbb7yxsE9BAMg4Eb1uuWRlv6Nhe7CKruOVrpy5hCKig42mi5HOnDanTjskZhsJzSV2IjbYLCJ6XLKxj5wuaW7tz82JmDmmnBYRc3MiVpnK+JoifrhdcpaIOLfAcmbA/mAcUZyIdvfqFEFss9tdCZjDfZzdX7ycuSeasqU/Z8Y5WtrP5XdnRMRXdnbiodV78d/PbC/6+/JjImxzsAqQESbtWnDNF/6+5nJmALjl/Hl47FMrDLddPpQioVkEJ2JG1FZt63urqeya6mg5s9trhKtEpCHLi8yq3hMxLXkLKhu3mwqfG0OaPo6xVM5sdiI6KCK63Ei4mfAb7bO+qMKDVVQnhVGwuezkpnoMzrwIALBJmZYRuC2IvwbpJKoHmMGpq3KM8l6reJmIGHbAicjvXZGAB+g/AGgq4Pazfo+jMZKIONiRJdzWV7CFyf3dhfcUHYt4SoUbfOLv4HHoZeP/ajfbbxSukk1BV/OLLroI3/jGN3DFFVcc8TNN03Dvvffi9ttvx2WXXYbFixfj4YcfxqFDhwzH4tatW/H000/jgQcewEknnYTTTjsNP/rRj/DII4/g0KFDlj7Q0YzhUnF4gglkVrUHEvZEwJe6nNklS0afwmSRRUQRBsHRlGJb70ez6OrUZ8uIiKW54LcbLj37+iFyipHyWwjtOToRM8EqTpcz5xbWYS7j46V7fBJcE/KitqLwfZrZdxPMiVikcmZH05mLEHDBJ98Bh/cXF4gUVbPl8zkl+ga8bGwTSynG5OVwkXtMaZpW3HJmh4NVeniwStC+z8avn7sOF09EtKsnohXM57UdJc2aphnBKh63gyV8gNEDLoKo9Z6IKTZ2UN32LrIWStjvwaAN5cyplMmx53DqdFoPi0n2d1p+LVWQcmaOeu7d+FX6AvwgcSkUry4i2lHOPNAKt5ZCQvMgFR7DmWcVkxOx1D0RM/cut6mUeerYx2ulHjLVd0Bv6t0F/Gg58Kv3Avq4mqfav7G3pyjbHU8rcEncieikiKjPBbzs7+j0PEw0bFebdu/ejba2Npx77rnGY5WVlTjppJOwevVqAMDq1atRVVWF448/3njOueeeC1mWsWbNyGlAiUQC/f39Wf+IbBJ6nyCfAD1HIjZHwGeCVSy/VM5kEpqL0xORT8ScHARzl4GmwbYk47RiLmd2RtAO+XT3XqlExIHckosLISxIuEprH3MTlEuwSq5OxMqAWURk7ot5TWzQt6C5sFJmDhezJ1xPRKOceeI5Ee0Uc/g54HQJn9ctG4J2lw1OCL4wWGrR12dyIh7sZdejnmgyq4WG3bA+SOz1J2I5M+/FONyJaIXGMLtHFNN1w8eEji7C2iwiJhUVsu6+cdSJCGRERClqOZ2Zi4iaSwwRscLvxqDGy5kHCn4d7kTUJOeNG7zMVx3qsvxSPFhFFeFzAaisn4JvqtfhgFaPIVkPerKjnDnJFjkG4UdFERaIDEw9Ee24/+ZDn9mJmEs/RIAFyQBAagiI9QCt65jzs30TcHAtAOCE6ex4W7un2xaj0HDiScXkRHTwWshFRI/uRKRy5ixsv0K0tbUBABobG7Meb2xsNH7W1taGhoZsK63b7UZNTY3xnOF8+9vfRmVlpfFvypQpdm962RNPc5eK8xd+j0s2JtB2DI6dKP3lfRFTNolrw1EF6OljHgTb1ReRO8AkybnPVmonYlsfW10sjojovBMxmkwbglFjjsEqpfrbj4aRzjyOiOj3uIxrJncinrewEXe9byHuvvQYS9tgdzkzdyI6vVCUKWe2yYnokChlptIkjNrlyubCQkCAPmC1NoarOOWiN5ePHuhhIqKmMSGxWPBjXJaAUBH2o9MiYs8QL2e2bxLNP1MspSCRLs5iEg/3kR10gMmyZNw77Fg0iyczIqLH47QTsQoAUIkh65Uqejqz7LG/UqMQIn43hmBdREyldSeiw2W/AOCqYKnFcrzb+qKKyq4JqiBORFmWUB9mx06fqouIdpQzp5ibPar5s/qP247ZiVjycmY2Dq8MmJKZx+qHCLB09lC9/gIHgc53Mz/b/GcAbKE97HdjKKlga2vh59BoxNOKcS10VkTUy5ld7JygcuZsnFebcuS2225DX1+f8W///v1Ob5JwGE5EtxgXfjsHx06Uu/F0wmL1REzyHpYO9ohxyZLharKrObjhAHMwJKHUImLHQHGCVQAxnIg8VCXkdSHsG/uGzl2gdiZWFkIqx2AVAKjSE5q5iOhxybju1BmYXWAyMydic+9AYZyINpczi+BE5PcrRdVsCwXKOBEddhXBnNBsvZwq7VRPRJN4bi6V7Spis3q+eBP2eyAVQbByvpyZ/e0qbXQihv1uo1quWJ8rU3nj7LUwaASJWb/Gx1IKXPrE2eVyeBxvOBGHoKiapYocKc2uOZo7/6TvYlDhMwerFC6ApA0novNzLm+YiYhV2gC6LF7jeTqzCOIoh4uIPYq+32xxIrJS9ih8xpyhKPgyPRGdK2f2ZJczj4e5L2LnO5nHtzwBqCpcsmS4Edfstu5+HQ7riShAsIruRKx0s/1GImI2tt99m5qaAADt7dnNXdvb242fNTU1oaOjI+vn6XQa3d3dxnOG4/P5EIlEsv4R2YjkRATMDcPtcz6UsvSXO5jsKvMdTqa/mdODYHuDMLiImIt4UyxCNpeRjsdB3RUzXuhIIdjt+sqH7qEkeqPJTDJzpX/ciXRQT5VMKZohlDsBFzpySQifWstWt+c32XtfMdKZJ1pPxABPP0/bEijAJ6hOpjP7PbJxrNglfEQFKWcGgBo9odnOcuZSi77mfs+7Dmd6mVmdNI9FX4ynWxZnIlPpcLAKP9btdCLKspRpD1Ckz2W4fB2+FmZCBO0VESUn+4ABWT0RAWuLglKajR9krxgiYtjvwaBmQ0/ENDu2RRAR5VAtAKBaGkB7n7XroWp8LjHmkkAmyKMrrbtZ7eiJmNSdiPAXJTTLQA+DqUCsqAteI5EpZzb1RKwep5wZyBYRu3ZkHu8/cERJ8xt7um3bXk7cdC0UoSdiRGb7za4WPhMF268QM2bMQFNTE1auXGk81t/fjzVr1mDFihUAgBUrVqC3txdr1641nvPCCy9AVVWcdNJJdm/SUUPCCFZx/oYGZMJV7JiQGaW/JSxd8bjZexXLiRgTRBDgK+l2lzM76SoqpROxN5o0JubmVF+7sNvNlivxlILzvv8i3nvfy9ipT9hzEUnNpZtO9kU0xOwchKl7r1yKhz9xIo6dXGnrNkzUdGYuEGiaTQ4c/TWcFNskSTIJAnaJiOxzOX2NB0zlzDZMYtIOBeG4XRmh17y4V8yJGT93w77ilJc6Xs6sL/JyN7ZdFPtzidJvtNLGxfJY0lTC57QwpYuIVbIuIlq4l8sKExEljxgiYkVWOXPhImI6pR/bTu8rAAhyEXEQbf1xSy+V1D+XKjnvoOdwJ2J7Sh+D2uFENMqZfago0vUdQFY587uHB4u2sDISfCxTGfAAvTmWMwOmcJX9QKcuItbOZl/1kuYTZ3ARsce2FjAc0ZyIYZmdU9QTMZuCZiKDg4NYv3491q9fD4CFqaxfvx779u2DJEm4+eab8Y1vfAN//etfsXHjRlxzzTVoaWnB5ZdfDgBYsGABLrzwQtxwww14/fXX8corr+Cmm27CVVddhZaWFrs+21GHMcEUxIlo54TMiXLmYgerxAUJwjESmm13IgpQzlyChOCdelldS6XfcEDaid1CVK6098fRNZTEob44fvgC64nSFBl/EuB1Zyb60ZRzN9x8yplbqgI4Y2697dtQYbMALIoT0eeWjTYMdqzMiuLYi9jsCuMLRU5/LgCoqWAikR1ORMVw+Zb+Gj/SsV/MAA9+fBfPiejV3ydli6s3X/ixXmWjE9H8ekUTEQVxIvLPacc1IyaK+wYwRMQamVVZWHEiyipzxrmEcSLaE6yi6Q5L1WWvAF8QXETEANptEhEdPwZNNOgiYltc/1vb0RPRXM5cgp6IYSkGRVXxwvb2cX7BPvj9q8qjAQOt7MHxglWAjBOxY1vm9067hX3VS5qPnVQJv0dG91DSMBrYBbsWiiAispZGFRIvZ6Z0ZjMFjQDffPNNHHfccTjuuOMAALfccguOO+443HHHHQCAr3zlK/j85z+PG2+8ESeccAIGBwfx9NNPw+/PuFh++9vfYv78+TjnnHPw3ve+F6eddhp+/vOf2/CRjl7igjkR7SzTcSKEhIs3dvSQGonM/hKknNkmwSflUKmbmVKWM+/sYDfPWRb7541GuMSl2Rzz+x0eYOdAU2VujdH5xG4o4ZwTMddglWJi9A6cYD0RJUkyRBU7FomihtjmrPMh4563LkqllEyqrwgiop3BKpny89Jf40cSEYvZZ4of35EipXfycZKmlf4aH0sqxjXFbhGx2GXaolRy8GtGrw3XjKwSPqfdbYEqAEC17kQsuGe2qsClB3UIIyL63BgEL2e2EAqRYgKr6hLgc+kiYo1kXURMp/Rj2elj0AR3Ih6I6depRD+gWhxfmsqZS9ET0Q0FPqTw7ObSiYh8EadBO8we8ISMY2VMuIi49xX2NVQPHPMBwBNkJc1dO+B1y1g6pQoAsGa3vSXN8ZQpndnJ41B3IgZBTsSRKGgmctZZZ0HTtCP+PfjggwDYBONrX/sa2traEI/H8fzzz2Pu3LlZr1FTU4Pf/e53GBgYQF9fH375y1+ioqI4k/CjBdGciHauRDvhRJyji0Lb2+xdYeGI4irigo9tTkQHXSqcUpYz8xW4WfVFEhEdKmceqYFwU2Vug2Uu4opQzuzkcWh3P0vuXnb6mgFkFlnsEBFFKU2s1sMlemxyFXFESGfmwSp29A9UHGxZMZLzrLOITkR+3Q0XSUT0umXjuC91STMvZXbLku2T6GKXM0cFCS2q0q8ZtjgRzeXMTrvAhpUzFzx5TmcELbcvaHmz7ID1RGRjGc1COTNS7LNpbvt7YedNgJWWVmPA6GFdKNyJqDnpABsGFxH3RU3XYQsuUgDQuBNR8xW3J6I3MzcII4YX3zlszP+KzYB+/a1NcRfiVCCXtmBcROQ9Q+vmAh5/psx5oA0AcKLeF3Ht3h7bthkAEskUZEl35gvgRAxq7DroZMCliIihNhG2IJoT0SgNs1FElEvYE3FeI1s9eqfd/vh6AIjroq/T5Th2lzMbE0wBglVK4YTLiIj290METGJNicuZ+aTB7HprzjF9mh9TpSgnHw0RenPya6B9TkR2PDvtRATs7dXJXS5Oi21VhohoT38zgC18eR0UsjmGiGhjT0Qnzi1zEBlfVLSjz+NoGOmWRSpnBpzri5gpZfbanjxd7M/Ex7sBr7PnllHObMdiSkqBRxKghA8wRMRKiU2eC/586cyihccngGMPbHGP90RULQR08F6PQoiIZifigLWFolRav6c7LWSb4CJi65AKuO3pi5iOs1ZERXciyjLgZfPJGWEF0aSCV97tLN77meD3r8qkSUTMBS4Wcng/xIoG9nWIORt5BdYBPVzSLlIp0z3dyeMwyETSkMKuEwOUzpyF8yNbwjZE6bHHsdOJyFsFldKJOLeJXfS3F0lETEzQdOaU4twEk2P0oiuJE5ENRIrlRORCVMnLmfXeH8dPr8Z75tUj5HVhcY7BIyEvORGBjNBmV1BHQiAnYkYgtaGcWRgnon0lmPwzBTwu2wWaQqjV05ntKGfm55bLgXPLvOg2V1/oK2Y6c7HLmQFT6a8NJbH5wMNA7C5lBkrnRAx4BGmBYJMTMQx9Mq4nujoGT2fWRcSC96Ne8pvUXPD7BOgdCHafiUpcRCx8fC9zl6VHHBExIkXR0z9k6aV4YIzktJBtoiHM/saHBxLQ+LlhsS9iSt/3MfiKP/bQS5rPmcmOu1KUNGuaZqTGV0QPsgdzSWYGgGAd4DK1L6qbw76G6thXXURs1quTrLpfh6MkTMewx0EHs+7w9aeZYE3lzNmIc4UgLCOSSwWwdxBplDOXcDI2XxcRd3cOIZFW4LPZ4SlOObO96cwiiDcVPt6Tr7gX/ERawb5uNsguWk9Em0tic4XfLMM+D3589XFIq1rOx6rd7tZ80TQt45Zy0BFrfzqzfo0XoGVF2EaBVBgRUXfr9dggtIniruTwYJWeaBKaplkSNp10IpoXSRdPqsTW1v4ipzPzcuYJ6ETU36+6CCJi0YNVuIgoiHvZDgE4llIQBhtPwC+GiFihsUqLgq/zutAWhxdBARa/ANZyS/NWABoslcRKii74ipA6HaiCBgkSNGgxa6WlvCeiJJATsa6CCVrxlArVF4FrqAOw4CIFgHSMHduKO1j8hT5fGBgATpviAzYAz29th6JqRTXGxFMqknpv8MCQLiLm6kSUZaByEtC9i/2/Tm9JF9IDCA0RkYm7bX1xqKoG2abPo+oioiK54XI7uPigi/O+ZC+Akds8Hc04PxMhbEOkflmAaWBswwpt2oES2YawD5UBDxRVw84Oayt7IyHK/so4EW0KVhGqnLm4F/x9XVEoqoYKn9tIj7Mbp3oichdnhd8Nt0vO6zgNOlzObE5U98gOOhEDGUesHcmrxjVDgJYVEb99DlnDVeR4fzP2mezoiRgXKJkZyASrpBTNcnm9KD0RF09hQocdidOjkSlnLoETsUghJKPBy/Z5QrSdlKyc2eHxUyW/ZgzZU84c1p1/zjsRqwAAQZUJLQUfm7qImIDHccE3Cx9b9JWShfdEdCnMAS056ZTiyC6o+j6TY12WXiqlsPuD5CreNS9fAl6XETKYcjODh/VyZrbvtVLsP92JOL9Ggt8jo2soif26AaFYdOvXd49LgmtgP3swVxERyPRFBDLlzMNExMaIH5IEJBXVeD870FLsb5N2OrRIL2f2pPogQy25mUN0SEScQIjmRKzSB6ZWB5HJdCblMlTCSaYkSUZfxO3t1la8RoIPgp12FdXqDpUOi31UOGmjnNn5YJVilzOb+yEWayUz4nBPxEJ6xThdzhxPZ97XyfOL7ztNAwZtEFRFciLaWc7MFzCcFtxqjJAEO5yIYogcHL/HZfx9rZY0O+nyNbf/WDypCgAbY/A0drspZTmzUz0Ri+FEzAijxRF4hXEvB+0Z5wJALJFGBS9nFsSJ6FXj8CBtoZyZi4heoUREWf/7yqkhdoMugIyIKEA5M2CUXgZSfcZYoRBUAcuZgUxfxLhLr/qxWM6s6qE6sq84/cyz4AnNqUG0VDFh7FCfvX0Eh8NLjBsjfkgDevl0uCX3F+B9EWUPUKWXQRvlzKyno9ctGy7R1l4bS5qTuhPRaRFRP6ckTUUEQxSsMgznZyKEbYjibOPYNTCOmibfpR6EzON9EYuQ0MyFDqddRVNr2Crc3i57VsXS+mTO46ATkQtfTIAuzuQSKH4/RCDjRIynivtZhsNvlpECyvicDlaJ6oE6bllydFHF73EZoRpWBx+KqhmLKU5fMwAYrgDec8cKoghudgarRAUptzTDw1W6LfYQNHoiOrBQxI8RWWL3Z26GtKMEfSSsXAdzhTtg7eqdmitc4ONl/HZSadMi8khommaknzs93q2yUSxVEkNwS/o93mknoun9w4haCFZhQklc8zp+fTfj8rOxvaylsxKkc0XTNLhVdh11eQVwIgKQdYGnShqwVAGWVtjvyi5x9heQERGjsi76WXQiakk25ymliIjEQFYJcDHhr99c6QfivezBQHXuL8CdiLWzAJd+/xvmRASAFv3ztNooikpcRHQ7fG65vUYoTrU0iN5YypaqookCiYgTiLggQR0cXuYRSymWVsWG9MmY1yXDW2JBgIerFCOhWRTRd1otu0jvs8lan1KddyKGTO65YpY07+zQnYhF6ocIZDsBS7kKNmgqZ86XkM1hPfkyZHK2OR1qwUuarQoECUHclRwj8CdhT6AAIICrKGRfWakon8lMre4YODxgTfDgPYo9jqQzs79nU8QPr1s2hNHOIvVFnNjlzKms97eTYror+dgJcP784gLwUFJBMm1tkU9LMFFEhQx4SyBsjIXLbUyeI9JQ4fvRVM7s9L4y4wmYRNpE/iaBRFpFQGIiouwVoCciACmUSWi20pJDSXMnojj7C8iIiP3QhSWLPRGRYkKV7Cve+N3AFAbDw0haiywiclGvOezJuDbzERHr57OvzUsyj40gIjYZIqJ9n0fWFx9UEVoF6CXN1RiAomrkRjTh/EyEsI2EPoCxOwCkUMI+N/j83cpA0ih185X+c803nIj2i4gxQUTfqTVssHqoL2ZJ7OVwJ6KTPRE9LtlwoBXzgm8uZy4WbpdsDL5L2Y9jwChnzn+CyXvbORWswp2IoQJKse3GKEe3OJk2T5xFuMZnxFFr55emaYimxNhf1UZIQgpagSVuHFHSY83U6yJi56A1JyLve1vMpvCjwUVEXhLGU6eLldDMj++iljPbWBKbD5lyZvudiOZgFavn0nD42AlwfhE27PfYMs4FYIgiSXcIECDRHYEqAEAEUcvlzHF4Hd9XZsIBHwY1vQw5mf/4PpFS4QdbuHAJIiKaxQ4rzliXLuBITgvZw+AiYp+q/725u65AZL3vnjdQChHxSCeinc69kWjvZ+fe9JDp3NXbFOTEgkuBq34HXPCtzGOGiNhpPFQMUVROs32juQU4t/TzqtnLtqlYY41yhETECYRoTkRZloyBtxVr/RAXBBxouj+3gV34D/bGbBdwEoKU49RVeBH0uqBpwIEe6ze1TE9EZwfB3MFXrJJaTdNKUs4MOBOuMqg7zApJJQ0Z6czOrNhxF6XTohRg377jAr/HJTki3gzHrl6dSUU1nG1Ol/5y4UNRrYePxAQLVgGAhgh3IlobBCuO9kRkf89J1bqIWMFLtO13IqYU1diPXDQvBoYT0YaE33zgQkNVEXsiphQtS/SzA/56Xrfs+LXQZR7nWt1/elJwyl0CUSMXdMGhUhpCX6GiFE9n1rwIOhycZSbsd2MIuohYQEJzPK1kRMRSlMPmQtAeJ6JXYeNayem+nMPgImJXWheWLPZEdOtiqTdQgs+ZJSLqopudPQRHgIt6kwP6/d4bzpQl54LLDcy/ONMHEch8nxwE9HLwYoiiXODVRBCy9fNqso99Pjva3UwUxFCbCFsQzYkIZK9GF8qQg033K4MeNEXYBdLukuZMObOzp6EkSUZfRDtKmvkA32lBoNgJzYcHExhMpCFLwNTa4lruww6EqxhORAs9ER1zIurXjJAAAo5dASQiJTMDmWPSqjhqLnkPOryg4nNnwkes9jgTJSzGDHciHrboRHQyPGvFrFqE/W6cu6ARAIpazmw+tgsJmMqVTOlvaRddeJ+7YoiIQa/LWEi0u0xbtHOr2qZUd5fuiEt7wpa3yRZ0EdGSE9GcziyQEzHid2NQ42JU/uXM8ZQCv6T/TdxiBatUS4OW7l9+RQ8cEUxEbAizv3Nnmt3HrPZEdKtMFPIFnXIiFldE5E7EST79fp9PKfNo+CKAS//7R5kbsbnKflHUpeiCpEcAEVE/r5o8uhOxSK1TyhESEScQCcGciIC5UX3hgytemhh0yFXEw1U+8eCbOPU7L+DxN/fb8rqGc1QAUcAQEW0IV+EusHABZbB2YiQ0F8m9x908tRW+ogv3zjgR+X7M/7zjjgPuIi41Q0Y/OuedD3aVM4uUzAzY1+sxaup563Y5/9l4aadVZxv/XE47zc1wJ0dHv0URUS9ndsJtfubcerx95/l43xKWMsmTIa2GxYwEP7ZDXldRj00ezuFYsEoRypklSbJlEXkkYkl2/IkiSlUaqe4WRcQUExFVr1hORN4TsaBAgZQerCJYOnMk4MEguIhYQDlzOlPODI8AJZeA4ZiqxkDBQTiqqiGo6oEjwTxKX0tAne46b0vooq3FnoheXagKVpTYiVhVmnJmLlI2ePT3CdiwPyUpU9I8yPoiGqJov32fx63wknoReiKy86rBzRy65ETM4PyInbANUYI6zPDBsZWTznAiOvS5TpvN7Nt9sRQO9sbw2zX7bHldI51ZgP3Fw1XsSGjmjqtCHGx2MqWGDexWbT88zjMLo2eIfc6aIkzAhmOX6ysfBi04EUN6/9JYyply5qEJWM7Mr++iOM3N5cxWep5x16goE0wufFgVBKICBqtwEdGyE1Hl6czOlJKaw5JqdSdiMdwBRjJzEUNVAHOwSukmJ5qmGcd4MZyIQObvZruIyKsdBBg7AfYlNLt1EVHxCuIAMzkRVQ0YLKQ9STrTE1GUazyglzMbPRELdCJCv46K4kTUe7excubCjsVYSkGFxAQcT7DKri2zBb5g1J7Ur1dWnIhKCh6w61LJRcQIm5v0RFNFCx9UVc1YLKx16fM6f5U9L85LmoeyRcS2vrhtycVe3SUqidAqQD+vaiV2negqQuuUcoVExAmE4VQpcYLxWPAyDys9EflkLORAsAoAfPL0GXj+ljPxgytZQpXVflIckUTfqbXsQr2ve8jya3HxqZBeenby0ZOnAQAee3N/URrWd3MXR6j4jsuIIUSVvpw5XECgAJ/cOeZENERE588tu8qZudNcFCciP79Tima00igE0cS2asM9b7WcWazPBQANuojYaVtPROePxZqK4pUz83O22PcyLiIOJRWkFGsJv7kymEgbYnAxnIhA8VKnRV14sDrO8OhpsUaSq9PookO1LkIUMo5X9WCVhOZxvF2FmbDfmhMxnjI7EQVwSwHG/gojWvCcK5ZSEAbb3+5S9ArMA9664lBcL6e10hMxmZnrhCpK4Lg0pTNHAm5jXNDWHwd69gKPXQu0vm3b23VHk0gqKiQJqIQukttRzgxkJzTH+9G87j7MkFuRUjTbBDbuEhWi36j+d6uW2HWih0REA+dHgIRtiCRKcapsmJBxQcCp0kRJkjC7oQInzWCW5o4Be1ZbRArCsbMnYibV11kR8bTZdZjfFEY0qeD3r9vjHjXDXQd8YFNMSu1ETKQVJPXJbCH7kTsAi7XKOh5RocqZ7UkxjqfF6okY8rrBjWhWyjCNFGPBBAGr/c0yvWGdPwY5hhNxIGHJPSpKeBaQSWcuZjlzMZOZgWyno/lc2tbWb2kBdiy4sOdzy0UbMxarTDsurBPR2uf06r3oxBERmbhS72YT+kJE0nSCjSlFcyJGLIqIibSpJ6JHECei3sMwIkULdyImM05E2Y7yVxvhY+0ehaczW3Ai6sEdKc2FSKgEQhXvL9nfCklVMiXAvTHgrYeALX8BnrrVtrdr00uZ6yp8cHGxVU9bt4xZRFz7IFwvfQdf9j0BwJ4SbUXV4NPY9rt8ArR20MuZwyr7O5ITMYPz6gVhGyI6EY3SMBsmmU67ivgELKVotvREEEn0nWYSEa1MLoFML71iT7zGQ5IkXH/aDADAg6/ssd3hwXumFcvFYSYjRJXGiThoMVDACFZxupxZgEkLFwgGEhPLiSjLki2BP6I59qqN/mb29EQU5XMBmXKwpKJaErVjKXFacfA+WcUY2JeqnNklS4bbkY+Vdh4exEX3vYxP/2ZtUd6TC17FvH9VFqmcWbSFB6MnosV0Zh5oIfnFClapceUoIiYGgb2vAmpmrMVFxAS8Qs1NrJczm5yIbkF6IprKzwsVtM1ORKMEVxD8HhdCXhf6Nd35aaEnoqY7EWPwobIERgA0HcvEqKEOYMPvMwnNfXGg7yB7zv41trkRuYjYXOkHYj3sQduciLycuRM4+CYAYJq7C4A9YTHxlIKAxBYFXX4BnIh6OXNIYccbOREziHNFJywjkijFsWNClklndtbR4XHJxoSl3WJjesAchOP8/ppUHYBLlhBPqeiwWOo2IEhPRAC4dGkL6ip8aOuP46mNrba+Nr+RlMaJWNpglUGTCFdI37OQfq5GHQtWEa8n4kRzIgKZz2YlVdYQ2zzO7yvAnLRqsZxZF9BFcUsB7F7DFyQ6Bgob7GuaZlzjnW5ZAWSuv8XoiViqcmbgyJLYTQf7oGnA9vb8XVK5wI/vYvVDZK9tj7g2HNF6ItqVzuxXmbAhjANMF6WqpBxFxOe+CvzqImDbk8ZDSpJdZ1SXN6uXqdNYDVZhPRF5ObMoTkS2v3xSCkNDhbUmiiYVhPX9DZ8gx6GJmgov+qGLiEoCSBZWPRWPMkEoCp/hJC4q3hBw2hfZ9y/eg8lhdu1q7YsBA6a5yZv/Z8vbtenJzI0RPxDvZQ/a1hPR5EQ8tA4AUAfmCm3tte5EjKcUBPV+o26BnIj+VC8A66F7EwkSEScImqYJl94JmErDhqynM4vgKqoPs8FCoRMwM5lgFef3l8clo0VPDLMarjIgSE9EgIVQfPjEKQCA57d22Pra3UZT+hKWMydS2LC/F2t2dRX1/QYshKoAGfdV1Kly5oQY7mUgO4DECqI5EYHMZ7PSq1O8/ma8BYc9wSqifC5OQ4Rd5wvt7ZtIq0jp5cwiXONrdXflYCJtlLnaRanKmQGTa08/7vbrrUV6osmi9EkshYhYtGAVwc6tKht6fwNAQE/FdQkmIkYkJkiN625r38y+dmw1HlJ0kUdxCSK06YT9bgxqTETUCk1nlgRzInrD0MCE2nS0t6CXiCVNTkS/IGX1JmpCPgwigJRH37buXQW9ztAAFxH9pasWOP56oKIJ6NuHcxPPANCde2YR8e3HrJVp64zsRKyy/LoAMiLi4W1AL2sVVaV0AwBa++2YG6uGiCiLICIGmBPRm+wFoBn98AkSEScMKUUDb9MnSnonYF6JLnxwZTgRBXAVNUbYhKXDohMxrWQmYqI4i3hfxL1d1sJVROmJyDl5JltFemtvj62vm3EiFn+CySfrr+/uxvt/+iqufmCN5f00Flb3IZ/cxVKKbWlt+TDocB9VM0Y5s9V0ZgGdiJGA7rK08Nm4q0gEwRfIBCVZLWcWrUybU19hLaGZi+GSlHEcO0nE7zYEuJ2H8y9LHIt+o5y5BE7EABsrcZfD/m7m6NC04jgfuLBXinJmu4NVRDu3+L6z4rhMKypCGrunC5OKq4sOFfp2jSsG9x9iXwfajIe0JDuOVZfP9s2zQtjvxhCYsJmK5V8Wm+1EFERElGVoej9NLd5XUGuiWDKFCnAnoljlzABQG/ICkNBbMYs9cHhbQa8THWT7PCn5S+eQ9QaBM1jfw1MOPgg30kxE7NdFRH8V69W44RHLb8XLihsjfiDWyx60O1ilLVN67VOjCCKO1l57y5nhFSC0SC9nljQFEUTRXYSqh3KFRMQJAne1AWI42ziZhtOFn3QiOREbdSdiu8XVlrgpzVSEcmYAmFrDek/stxiuwgWcQlJ9i8GSKVWQJeBgbwwdNqyScUrZE5H/LTsHk1BUDYqq4bE39xft/fg+rChwH5rFhZjN7qBc4C4wEYTssE39LEV0Iho9Ee0IVhGknNlwIlpwzwOZ414UoYNjDlcpBPMCgyxAsIokSZjfxCa7W1vtLf3t1IVWLhIVkyn6It4efXFof0/mPlzovhoLfnwXtZy5WE5EgVrBAEBl0LpYGktlykg9oSo7Nss6uhMxpDJxfkyRVFUyIuJge+bhtD7mEsyJ6HO7EJfZOadaFhEF+my6ezCgDBY09krGBuGSuCNFRCciuxZ3+Fm/c7PrNR94OXPSVWIBeNk1gC+CYKIDM6Q29PZ2A0n9vnXazezrht9bfhs+R22uLEI5c0X9iA/XS72GA9IKsaSCEPTX8QjQE9ETMBLYq6RBDCUV26seyhVxZiOEJRJ6P0RJArwucXZrpieiDU5EAVwP3InYbrGc2XwBEqXZ9LRa3YloQUTUNM0kIjq/vwA22Z3byCaZb+2zz43YU8J05kpTz5alU6oAAI+/eQDpIpS5AcBggpfxFbYP/R4ZfHGXn7+lJHPNcH6SGTEla1sJLUqI6ES0ITVctAASu4NVRBFHOXaJiE4HZ5lZ0Mwmu1tbC2+0PxJc0JteV/yJzKx69h7cTbnPdB8u1DU6FlwQKmY7jsoipTOLds2oClgvZzYHWniCgog3uojIA1/G3I+DHYCmj2tNTkSk2FhZcwsktOmoHlYqqcbzdzAn0gIGqwCQTCXohcy7UkO9AAAFsjgOSxO1+nj7gHsqe6BAJ2J8iO3zdKlFRLcPqGEC6DSpHUqfLrx7w8C897LvO99lFnQL8JTkpqIEq4wiIqI3a/GrUBJpBQEI5EQEjL6I9TI7buwIV50IiKFeEJbhopTPLQvVvLhKLw2LpQpX7kVJZwaA+gh3Ilob1PO/hdctC+HmADLlzFaciLGUAkUVp18WZ9k0dvN8a1+vLa+naVpJnYhLp1ThksXN+MqF8/Dop05GbciLjoEEVm0/XJT3s1rOLEkSgrpLJGbqi6jqLspik+mJ6PwxyMuZk4pqCIGFIKITMVPObKXnrTiCL2BfSIJofds4DbqIWGiAlkihKpyFRRARNU3D7sNMRJxRChGxgQkaOzuGkFbUrJTLziI4EbnAUMxQAcOhZ7OIGBcuWIWNAQYS6YL7V8aTquFE5EKQ4+jb4daS8CE5tqOUuxCBLCciUnpZvkhuPR3NyxaXtWT+DuZUIgG3pO9rgT6bpLvNwogVJHRoej++mFwBCDSX5PBF+10S63VeqIiYjrF9rrgdEKmqmYg4VepAMMHG8AeVKrzRq5ePJwcywl+B8DlqU1Y5c5Wl1zQI1mX/X18gaJT70doXxwGLQmI8pSLIy5k9goiIugA72c+uZxSuwhBnNkJYwnCpCDKo4oR9biPdtVA3YmbV2flJS6PFCRjHSNIWxIUI6CtWsCaQcvHJJUvCDPABYNlUXUS0qS9iLKUY51wpnIhet4wfX70Mnz1rNnxuFz6wfDIA4JE3ilPSbEdfywBPaDaJiJ946A2c8p2Vtpe3DcdIlxZARAx5XeDrBFYcOXEBr/G2lDOnxBLbuDPLysKXpmlGYIwo4ijHLieiSCKi2Yloxe1rpmMggaGkAlnKLLAVk9n1TETc3TmEAz2xrMWWojgRo8VfBDtaypkjJiG20M9qdiIKU0bqDQN6UEcE0bHH8P0HM98PtgOqLrAp7NiVBHQiws9EGzmZvxNRMacCC+REhEUnoqKXdidcApSRjgAfb29RJrEHuncZbtd8SOnuU80JEVF3Is5yd6AJLJBkdzKC36/rACoa2XN69hT88gPxlDEGbgoBSOs9Lu0qZ3Z7jeMMkIDppwEAjqtm++HVndaCH83pzPAKchzqTsTJPhIRzYijYBCWMDsRRUKSpExfxAKbTvPJmAhOxEbdiWi1t15csEEwYHaoxAueiJnFJ5EcscumVgEA3j7Yh6QFNxiH30C8btkRkeBDx7NV2H9u77Dcn3Mk7OhrmUloThuvuWr7YbT3J7DOxrLykTCuGQIIOJIkZcQ2C2W/CQGv8bzc3Uo5s2ghCRG/9YWvRFo1gs5EEUc51kVE7kQUp5x5TmMFXLKEnmjKcpUAZ5fuQpxSE4S3BOdcS1UAPreMpKIeMQnrHLB/wsKdtsXsiWguZ7YzYEukhWWALZrya2Gh14x4PAa/pP+uKKm4smyIBXPl/bk7EdU0EGPiiKT3RJRFKUs04fIz4V5OFS4iapBYiaoocBFxPNF3NBJii4i1FUxE3B0Lsc+qqUDXu3m/jpLQ97kTIlX1dADAbE8XGiU2Fm5HDQvsrNLLtHv3FvzyfE4Q8bsR1FsRQJLtXZzgJc3184CamQCAYyp1EfHdTksvHU+pmXJmUZyIerhKo5ud9yQiMsSZjRCWSKTFE6U4vKSl0Eb1QwlxBoyGiDiQsDQo5vtLpAkmn1ymFK3gUj4+wRQh0MLMjLoQqoMeJNMqNh/qs/x6/FiuCXodEUtnN1Rg6ZQqKKqGf+2wdsMeiUEuBltwG2VERHas72jPlAy9055/+VA+DPFJpiDHoR1lv4Z7WaBrPHcFWBGyueAbEOD6DmQvfBXa98bsYAwKtL8Ak4hYaDpzTDwnot/jwky95NiukubdnaUrZQaYEMXfa9X2jqyfFdOJWMyeiNyhp2qs1NcujHJmrzhTGP537CtwsTyp96IDoDsABaGWpeA+6LkHV/X/0ihPPgKzExEABljirKywe4MkYH89Fy/XLkBEVPXU6bTsE6vsVxegI9JQYfevBBubpdwVdm6VbdSE2P2reygF1C9gDxZQ0qzqIqLkc0JEzPRENERErRpt/XGgahp7Tk/hIiJvhdFcGciUMvsr2aKAXXARseU4wz05w8/+pq/u7LJUERBPxOCR9DGUYE7EejcbF5CIyBDnDkxYggeriORS4VRbHFxlXEXOT1rqKryQJEBRNXRZuIhkypnFmWD63K5M8lmBwTGihapwJEnCcVPt64vYzUvBSlDKPBoLW9hgkTf/txPDbWRBhOMiIl8EMAuH29vyH7TnSkpRDbepCE5EAAj7rJf98oUHka7xPLBoW9tAwYNGw1UkkNhWFbQmIvLP5HXJcAsUdAYA9RV8EpYsqH8bvzaIFKwCZEqat9gmIrJrVKlERCDTF/EV3cnBj8Oi9ETUr0XVRXQi+j0u4z7w/We3Y8gmITEmYGhRlcWE5nRU70UHP+AS53Phqt9hcNZ74ZEUfCz9J+B/VwA7/3nk844QEVlfRJdezuz2ilfO7Amy+5dLSwPp/M4xVRdTFcFSp81OxEJK6yXdiZj2iCki8mCVrqEktPr57MFCEpp1J6nsc+Bz6k7ERrUdl0xn9+A2rRrtfXGgWhcRe/cV/PK8yqAh4sv0VrSrlJnDHZOTTzBExDr0wueW0TGQMALCCkGJm+Y0ooiIAeZErJXYPKaHREQAJCJOGOICOxGtNKpXVS0zyRSgnNntklGrr4RZcd9kypnFOgV5SXOhJWEiJndyeEmzHQnN/AZSE3Luc86oZTfXPV3W09CGw8VgK05E3o8wlmKvta3NJCK225uiaoaHqgBiuJeBjBPRStkvX3jwCXSNn93Aykj7YqmsIIh8EK2cGTAnNFvr4yva9R1gn82tl2t3FuBw6xewJyJQDBGRTWRm1pdukjlLfy/upD5uShUA+52IiqoZAkNlEUVEALjhdFbq9tDqvTj/By/h3Q7rC0hRAUOLqixeM5QYD7QQZNLMCTchccWDuCF5C1q1GqBnN/Dry4E3f5X9PHM5MwAMsoRml8qOXZdPkLJEE15zCnYiv+NSM0REgUqZAUNEDEvRgoQOlx4yo3gEcsOa4EaHRFpFsmYue7CQcJUUGze7/Q6IiJWTAdkNSUmivm8zAKBdq8FAIo1EhR4YY6GcuWuQ7ffakBeI97IH7Upm5pxzB3Dx/wBLP2KIiK6hdhw/nb3PK+8W3hcxrferTMMNuASZS+pOxCqwbbNiIppIiDfCJQoiIaCzjVMZKHxwFTOXhQkyYGyMWOspBYgpCABAg5E+XZggYJQzCzbBBIBjJ1cBAN5ttz6JKWUy82hMq2WD8j2dxXAiWhcKeLAOn/CZnYg72geLltI8pDuXvS65JL3MciFi9EScWE5Ev8eFWfXWykijgpWeAxlBoFAnYkywnm1mZFlCXUXh97DMtUGQwb3OgmY26bWrnHkXFxFL6USsz34v7p4vROwdi4F4Ctw4XBUo7j3si+fNxUOfOBGTqgI42BvDp379prFIVSiipTMDQA13jfJ91XcAWPUd4J/fzun3hRURwcrSn1OPx3mJe5BY+CH24PrfZj+JOxFrZ7OvA0xEdHMR0SteOXM44EdU00XARH7XDUkXEVWBnYiFpKK79NJu1SumEzHodRljoL4KVmpfiIjoTjMR0RtwQCyVXRkn3wAT3/vcTKTq8jSxxy2UM3OBq7bC5ES0K5mZUzkZOOGTLJk8rIfBDHbglFksufnVnYW3WdL0oKOkLNC5pfdEDKvsOl3o2HCiIc5shLAEdyL6hHQ+8DKP/E86LghIkjgCaaNFoQ0QM1gFMKVPFywiiulSAYAmo5+l9SASfgMpRTLzaEyv407EIdsSSTmGE9GCsMOdiNwZuN3kREykVewtQhk2AKNkTgTnMieTYmzdiSjaNcOcjFsIfKFIlEUiIOMw7h601oJDpM9kxkq4SiZYRaxr/EL9ONzTOWSIuIWSVlTs0x3eJS1nHuZ6XKaLiL3RlC2BYBxeFRLyukqy0HLm3Ho8cdOpaIr4sfPwEL7yhw2W7llRAd3LTZVMJDMc2b37gVXfBl77KZAe/zqixXmghXjijcclo8LnxiCCOLzkM+zBjq0wlGhVBfpZD0RMWs6+DrYDmga3yj67xyegiOh3Ywi6UJFvQjMXEUVKZgZM6czRguZcHi4iipIQPgxJkoyS5o4A6y1YSEKzSxcRfUGHzje9LyJHDTcDAFplXZDr3ZdJOM+TLn0hoybkzfREtNuJaIYnSg8dxikzqgAAq3d2FWwUUBNsbpByCXRu6SJiSGHX6a4Cx4YTDfEUJ6IgDGebIEKbGSv9pbgAEfS4IMtiNC/mTkQrKZBG+blAriLALJBaK2cWLVgFyJRq99gwIRPBiTi1JghJYn/zQoNwRsMWJ6IpWKVzMIHOwSQkiZXAAsULV+GlgCL0UOVkypknlhMRMIuIhe1PI1hFIHG0pYoNXg/0jBIiMA6xlHjllmasiYhiLhTVh32oDXmhasB2i9eWAz0xpFUNfo9sLD6VgpnDnIjHTqqEx8XGPV1D9rkRe0oQqjKcugoffvKRZfC4JDy1sQ2PvLG/4NeKCbgI21LFjpPWPv2aMeUkNrlO9AF7Xhr/BXQRMekWz4kIZJK2u3yTAdnDRLc+fR9GOwE1xRJgm5ewxwbaACUFGWys5fGLV84c9nvQr+nbFc8zcE9PndbcArmlACOBN4KhgsaFnrQupvoq7dwqW6nRE5o71EpTQvOOvF7Dq7Lz1B90qGxb74vIkOCJMBHxgFLDziMlwYT4AuBOxLoKUzmz3T0RzYTq2TZrKo6tTqHC50Z/PF3wGF9LMhExLZKIqPdE9CeZs5OciAyxZiNEwSRS4joRrfSK4U5EkUrd6sO60GbB0Saqq4gLpNaDVcQqdQOYmO3VQw6s9pgSwYno97jQrE9wd9tc0mzHfgwa5cxpvKO7EKfWBLFU7/NVrHCVqL7tIYGciHaUM4t6zVho0YloLBQJJLhNrWETy73dhZ1XIvZ5NMMXVDoKERETYgarSJKE+XpJ8/Y2ayXN/Ho6vTZU0sXLoNeNSbqAXRnwoDLoMXowW2mfMpw+fSxWXeKevsunVeOzZ7Fy139u6xjn2aMjokjPxeY27kSUZWD+xez7LX8d/wUSXEQUz4kIZETEngSAujnsQR5owUuZKxqByCT2/WA7kM4swnicSMEdh0jAg8OoYv/Ry69zhadOQzQR0aIT0avoqcV+MXsiApmE5q6hVKZ8vmdPzr+vqhq8Ktt/gQqHxNIakxOxogENVez8aB1UgMhk9niBfRG7jJ7tRSxnNiO7gCArY3ZHO7BID33cfKiw+7CQIqLuFPXEO+FBmtKZdcRTnIiCiKfF7YloJbUuariKxPlchtA2AYNVDIG0YCeimKVuAJtk1lss1+YYTkQHRUQAmKaHq9hdGjxog6OUC//RpGI4g+Y2hjFPT/QdHq6SVlRbyrK5ACpSPzp+PlgpZxbdibi7a8hwFeaKpmmIpsTrH8j7je7vLsyJmAlWEee+ZaZWd3IUMhAW1YkIAPMa2bFodYHC6IdYX3rhg7/nlBo2geL3LDv7IhpOxCL3QxyJYyaxSXtbgfdgRdWMSgKREt25e/mQOWBqwaXs67a/A+rYJfayHmiRFjTQgouIfbEU0LCQPdixhX3t00XESAsQ1nu6DbQaiceqJsHvF0xsA7uGtWl6mefwdOlxkNNcRBRI6AAywSqIFjTnCijs2ucKiOtE5OXM3UNJQ7xCtDvn3x9IpBEAOzaDFQI4EcPN2W2yeEJzgX0ReTlzbUWJypmBrL6I/Bq/6WCe7l4dKamHFol0blU0AO4AJGhokTrRE01BLVJf93JCrNkIUTAJI6hDvF1qJF3GCuiJKKAg0GhRaAME7oloUSDNONjE2V9m6i04cMz0DLHBWY2D5cyAuS+ifQnN8ZSCpMKuJ1YCcrgLayiZKWuY1xjG3CbuFmKPqaqGh1fvwXFffw6f/e1bVjYdQEbAEamkPqJPwKyUM4vqRKwP+1BX4YOmZfe9zIWkohp9c0RyFU2tYefVob6YId7mQ1TAPo9mMk4OKyKiWE5EAJjXxFxcVtPfdx1mImQp+yFyeF/EKdVMyK7TBV87nYhcXKgqcjLzSDRXsvHTod4C09xNYXsiXTOa9M/VOZjItEuZfhqbvEc7gb2vjvn7Lr0nn+IR24nIRMQF7EHDiagnM2eJiO1G38AEPAgINIbnMBGRlSkaPR1zxHAiegQTR3URMSQlMBiL5S10+DQmIsp+MXsiApkKICYiskASxHIXEftjKYQktv98TgSrANk9ESMtpjZZ8UzoSqFORL1fX13IV5pyZiDTF3GgDcdM4k7EAkXEFDsGVZFaO0iSsV+mSIehqJqlyqKJgniKE1EQmR574gyqOJmeiBaciAKVJjbaENAhrojIP1uioFUWkXsiAtbK+Mx0R7kT0dmJ9PQiJDSbkzOt9BXkQvLru7vx+m42wJvXFMZ8XUTc0xXFpoN9+ODPVuOOJzZjIJ7Gym0dllObhwQMtciUM1txIvK+t+Ldtnky7rY8RURzAIZI+6uuwoug1wVNK6wvYiwp3uKXGe7k6MrT3aZpmtBu83lN9jgReTnzjLrSCzoXHtOEugovLjqWlU9lnIj2lU/1Gj0RnRMRs8S2PIiawvZEuhbWhrzwumRomil0z+UB5uklzVvHLml2p/VFNa+YTsS6sEnM5k7Edt2JyF18kUlAhS4imnq6xeEVSvDlRPwetHMnop6SmytuXUSUvYL1ejQFogTVobwdvyGVLUi7g+I6EbmI2DWUNAIvEO3K+fd7oynDiQivQ0IVdxsCQLg5ux1CVeFOxGgybSy01FR4TeXMRXYi8vN+sB3HtLBjZ/Oh/oLmkVKahxYJdm7p+2yOlx1rVNJMIuKEQdTyWMDcEzGZd7miiE5EvuJ8eCBRcAqk4SoSaBAMZCYsaVUrqHGsyC4VAGjQV/sOWyhn1jQNPUPO90QEilPObC5ldlnoB3bhoiZMqgrgQE8MOw+z7ZvXFEZD2IfKgAeKquF9P/4X1u7tQcjrgsclIZlWcaDHmquS99gLCSRkR4xyZgvBKoIuPACF90Xki0QelwSPS5xroSRJRl/EfQW4fGNJdn0XceIMDHNy5EEirSKlsHu4iCLiHD20qXMwkbdAamavkcxc+knMyTNr8cZ/nYtLl7QAYIEkgL1ORL6g60QwWE3IayRCtxdwH47zc8vjgiSJEbYHsGsGHxtmCTcL9ZLmrU+OmbbqSXERUUwHGC/XPtgbyzgRO7cDSjrbiejxG2447F8DABiCX6jgLE7E7zGciGp/fiKiS2Hno+QRqOQSAFxuwMuugxEpip2H81tQCWrs2ucJVdm9ZbaRXc7MRcSenH+/L5pEkIuIHodERF+YBZIArJy50lThxgXGApyI3IXoc8usDZhRzlxlcYPHoaKBfR3swMz6Cvg9MqJJBbsLmJu49eRsTbRzSxd3T64ewLkLGiALdP9xCnFG7YQlMi4V8W7U1fpqd0rRjEljrvAVFZGciHUVXtRV+KBqwLYCG7gboq9gk0yPSzbKpwop1+YuFXGdiBmnZaEMJNJI66trTqYzA5lyOzvLmbkT0eo+rK3w4bFPrzDckh6XhBl1IUiShHm6G1HTgHPmNzHe/WAAAFOOSURBVOC5W840yvjyHfQOJ9MTUZxzi5czW0nRFtuJaE1EFHGCaYiI3fmfW9GUfgwK+LmAYU6OPOAiuCSJlX7OCfncxn4rNKE5raiGCDS52hknhFkcM5K0beyJ2Bvj5cylv39JkmS4EVv78hcRjVAVAc+tTKm2yb088yzAE2Q9Art3jvq7Xp6KK2gZKQ/8OdQbY5NpTwhQkkD3LpOIqIeqcFfSy98HADynLBfKCMCpMJUza335lTO7NHY+yl7BhA4gE66CKHZ25D6e0jQNFWD3O1+oXJyIejlzHk7E/sF+yJJuaHHKiQhkSpojLVkVbmpl4eXMfGGwNuRl9xHuRCx2OTNvYzDYBpcsGQvLhfRFlHUR0dF9MxK6uHtBSwIPXHuC0U7qaEa82QhRECI7EQMel5GKm6+7bchI7hRnACJJkpE+tanA9CmRg3CspE+L3hPRjnJm7kIMel2Ou8L4hLkvljK2yyq8z4eVfoicSVUBPPapFThzbj0+feYsw2127YrpWDK5Ej/68HF44Nrj0VIVwCzdRbSzw5qrkpe7iSRkz6gLweOS0DmYKKj0XFE1o0+l08fcSHARcVvbQF5u80yKsTj7isPDVfYW5EQULz3WDA9W6RnKrzqg3+RSLmVqcT7M1YOb3smztJ7T1h+HomrwuCTU6y5AJymGE9EoZw44UzGQERHzbxXAr+8inlsjiqNuXyZEYQxRwKsHWkiCiojNlUwsa+2Ls+TphvnsB+0bM8m4XETkIQuxbiiahP9TLhJqUY/jkiUMeJkbTB5qG9MpaialqPBp7BxyiVbODGTCVaSoUQWSC4lEAgGJfS5vRZHLXy2QCQZLAIH8y5m7e0yuRY+D++/0LwELLwPmXWTMTVKKhl4fc6Gj7yCg5Lfw3DXEQ1V8bJWe90QsejlzxokIZAK0CklodivsviCJdm7xMvPefc5uh0CIpzgRBSGyS0WSpIITmvmAUaR0ZgCZxrEFpk+J2hMRsBauInJyJ5ApZ7bSz9JIZnbYhQiwiRTvpbLHppJm7jayax82RPx46BMn4kvnzzMeu3hxM5646TS8b0mL4bqxy4k4JKAwFfK5ccJ0Nthdtb0j79839w4T8RrPBbeBeDovtyW/vgcFcppzpuqtAvZ1539eRQUXEbmTI61qeSWGc6d5RNB2FYA5XKWw68hBvQdmc2VACKG0mOnMTvX05YJUIeEqQjsRdbde23CHZeUU9rXvwKi/69dFRFnQVNyWKl0g7Y2zPme8pPnZO4D+A0yM4cIidyIC+Id6Eg5oDUKOdQEg7quDqkmQ1DQLwMmBRFo1euq5fII7EfMYTyWGeo3vA0I7Edk1sXuwsGCVzm4mIqZkHxPEnWLehcCHHgaCNVlVYK1qJduHmgK0bsjrJXnv3NoKL5AcAlT9/l70cmZTKjtg9EUsxInoMUREwZx+FsrMJyrizUaIghC5XxaAgkVEw4kokKsIMF0gC0yfEtk5Wmj6tKJmytWF7YnIy5ktJGvzCZjT/RA50/W+XXaJiP/cdhgAMLu+tKECs+rZgMGyiKi7YUVqgQAAZ85ljodV7xzO+3fjpkRSEUVEvycjZudT/ityivG0GutORFHLmX1ul+HU5c6FXBB9kQgwh6sUViVwSHfH8fJNp7HbiahpGvZ2smOai3mlhjv22gpwIsYFvmaMWM4MAJWT2dfe/aP+bkBPxXUJ6kRsjPghSUBSUVkZKQ9X6deF0UvuzbidwhkR8WfpSwCIub8AoCocRCd0wSzHvojxlAKf7thz+wQTOoCMiCgN5TWeig/2AgCimg8er/Mu7NHgTsShpIKEt4o9mI8TsZeJiIpgwR28pLl9IAnMOJM9+O7zeb1Gt7lfO/+buHzFd1zWzmZfe/YAsV4smpQpZ843C8GjskUYSbRzi6dmDx1mAi1BIuJEwQjqEHTSwnvv5FvOLK4Tkd2kt7cNFJQwmBB4fzUW6NYbNCXPilRKaqbB5OooNAWYT+aEERF1x9TuTut9EQcTaTz5NhtIf+iEKZZfLx8yTkRrN+chAYNVAOCseazc47VdXVmiYC5wp7lbluAWKIDEzFSj/Df3/ZcR28TaV0B2T8R8B8GxlHhu2OEUEq7CRUShnYi8nLl9MO/9BmSciJOqxRARuRNxIJ7O+7oxEgd6YhhIpOFxScY1t9QYYlsBPRH5QqWIYycuyh6RiFs1vhMxqOoioqCpuB6XbCwwHzKHqwDAsmuBJVdm/l8zEwCQmnIKNmrsexH3F8D2WZuR0JxbX8R4SoEf7LopXLAKYCQ0RxBFe3/CcJCPR1J3Ig5KYolrwwn73IYo3ZbStzXWC6i5XR/7+pj5Q3MqVGUUMgnNCWD2uezBd1fm9Ro8UKyuwpdx1obqWCPjYlJRb5z3OPAG5jSE4XXJ6I+ncaAnv8Uir8qe7/I5c38alUA14NOvz1TSDIBExAlDIs0uniK6VIBMuEpvvj0RBSxNBIDJ1QFE/G6kFA3vFNDAPZ4W14lYHynMiTiQYAMVn1s20hdFo7bCB1kCVC0/B46ZXbrINUOQprpz9Enz2wd6Lb/W3zYcQjSpYGZ9CMdPK21PnJm6E7F7KJl3aqwZo0RWsIWHuY0VaIr4EU+pWLM799IbQOz2B5xpBaQZc9eoiGW/k6oDcMkSEmk17x6qIvdt4xQSrsInoyI7EXn/0cFEmiXJ5gn/nRZBnIgRv9soc/vXjtzKLcdiix5+NLsh7Nh92hDbCglWEbhVQMaJOFo58yhOxFQMIbDjzhOuL9bmWaa5yiQiTjoeiEwGpq4ALvpu9hOXfBi46L9x+IKfAmBjQpcArQFGYlJVAO16uEquTsREWoUfujDn9hdpyyygOxGbfey+tSvHhdlUlIlrUYhx7RsNSZIMp/iBOP/7a5kk4jHQNA2DA+xzyoKJVA3G3CsOzD6HPXjwzUw4Sg7wdOaakBcY0p2IPMG62Ew5mX3dvwZet2wEKG7Ms6TZqzsRXaI5EQGgWncj9lBJM0Ai4oRBdCcit2nnu/IcFbQ0UZIkU+PY/EuaDVFAwGCVxnBhPREzpW7iulRcssQaDqPwkuZ39bQ7HgTiNCfPZAOE13d3I6Xk74o188gbbJJz1QlTshJCS0HQ6zYGhrsslDQb5cyCLTxIkoSz5uklzXn2RRS55y2HO/f25lHOHBO4NNHjko0+YPmWNMcETp3m1FpwIoosInrdMmbWsWtzIQt8B3UBaLIgIqIkSXj/MlYOy6/PVuAJ6guaw5Zfq1C4GFVIsIrI1wwuInYOJrIrVMYTEfUy534tAG9I3EALLqwf6ouzFOmb3wauewoY7sbz+IGTbkTUy/rViSj4clqq/JaciEd8dhHQRcSWANvGXEua0zE2l4nJAoo3w+BO8QP9KePz5lLS3BdLQUqx+7nHL9bnbDKLiJWTgfr5gKYCu1axJ+QQstJlSmc2nIjBumJs7pFMOZF93fcaABgJzfyekyt+jd2DPQEx5lhZVFFfRDPizkiIvBDdiTilmk0w9+cxwQSAIcPRId6kxUr6FB8I+wScZDYW6EQUPZmZw0uaC+0xxQdkvIef0yxoiqAm5EU0qWDD/t6CX2dbWz/W7++FW85MWkvNTBv6InL3smjlzECmL+KLefZFLAcnIi9nzseJKHoAybQadjzmU6INZD6XiEIHp7ByZu5EFHehCIDhgNjaWoCI2MOOX1HKmQHgQ8czEeqf2zvYBNMCfELHJ3hOwJ2InYNJY+yaKzGBy5lrQl7D3Zm1n3hPxP5DI5dc6qVxB7V6+AUc63L4Ip/R81F2jRlM0aeHtIna3gbg5czciZiriKgiIOnjR4GdiE1eto25jqeUaBmJiPqxeLAnlldC84GejOtXNCdiUyWbmxjXjlm6G/Hd54FV3wW+1QJseHTM1+AVVqycWf97hEokIk7VnYgH1wJKylioykdETCsqAmCf3+0Xa/8AAKqns6/kRARAIuKEgTsRRRSlAGCK7lLZn2dvBD4ZE60nIgAsask0js2XjHNUvFOQi4iHBxNI5+FsK4dSNyAjIhaS0JxIK0ZwRKmDR0ZDliWsmMlW/F/dmXtz6eH8dT0r5Tl3QaPRzL/U2NEXUVT3MgCcOqcOLlnCrsNDeZValoMTcZqRZpy/iCiq2Gbct/Jc/BJdHAWAGr1Elpc/5UJ/GTgRAWDxZDaJzrdtgKZpxnkpSrAKAMxuqMAJ06uhqBr+sHb0vnq5wIXVBQ6KiNVBj3Eta+/LbzFP5HRmSZJGDlcJNwGymyWlDrQd8XvxwzsBAAe0elQL0mt5JPhny9VBysu6WxwK8MmFlqoA2sGdiLmWMyuZcmaBnYg1Lvb339mR23hKizGxJ+ESX0TkrtgDvbG8EpoP9MRQK+mLS/z3BIGXMxvtEHhJ84ZHgFXfApQk8PYjY75Gd1Y5c4mdiHXz2LGXigLtm4x7TD6LeXFT8rnX75xbflTIiZiFuDMSIi9ETvsFgCk17IKftxMxwfubiTdp4U7ELa39eYd0iOwsagj7EPK6oKgadnXmLubwUjeRV52BTKP6QsqZ93ZFoWqssTN/HRFYMYsNhl55t/CeWTzd+aSZJeqfMgK8RHxnR+FOxEGBrxkRvwdz9M+4JQ8Hc1xg5zKH90Rs64/nHAARS4q7rwBgmu6u3NLan1dIBz8GRb4WZsqZC0lnFtuJyB2/r+3qMpxrudA9lDQW+JoqxXIYXXkC68X02Jv7oRYYCjYQTxkiv5Miollsy7ekOSb4woORPG12IsouINLCvh+hpHmoYzcA4LCrQehrBhduDg7v+TgKXEjl5esiwsqZ2ZhHy7UnYkqFT+hyZnZuV0psTJerE1FLcBFRjAXysZhcbXIiBvNxIkZRJ+nGj4qGYm1eQczV+5u/e3iQjaGmncKcrmomtBL7XgPSIy/8aZqGTl7OXGEqZw6VSCyVZWAyL2leg/n6PeZgbwx90dzCfeIpBUHd5esJCChmV5OIaEZMxYnIm4xTRcyBFXd0dA8lDWEwFwwnooCuohm1IYS8LsRTat7llyKnM8uyZFz887Ghl0O/LABo0BMG8w1LADLi1syGipL3DByLU2ezlcZ1+3rzmjSb4aufzQ66BmZZLGfWNE3oawYAzG/i6bG5r86WgxOxKugxzv1cF4uigvcO5OFCz2/twHf+sS0nIXEokTbK+EQToszUhNgiyEQLVgGYc29SVQDJtIrXduXuzubXwPqwT7h783uPbULY58berije3Jt7o30z29vYNacx4jPK2Z2C32da8+yTLbITEch8riPDVfSG/L1Hioiprj0AgMGgM21EcqWlclg58zjwfStKSNFINIT9OIz8RMR4SoFf0q+bApczBzV2H97TNZRbVZEuIqbc4ouIRml9n8mJGM3NiVgHXUQMiRVi1FLpR12FF4qqsTZZngAw/xIAEvDe77Gy7VQUOLRuxN8fTKSNXqy1IZ8pWKVETkQAmHoS+7r/NVQGPMZ+2pLjXDKWVBDUnYiilZsDyDgReyidGSARccIguhMx4vegSk9o3t+Tf3qniE6VQsU2RdWQ1G/ofkFFAd7LItcLP2B234jtUmmIFF7OLFo/RM702iBaKv1IKire3JtfCR+HJ2U2Oyh88HLmfd3RvHtlAUBSUZHWXToi9kQEgLm6iLitLY8SD8Gv7wBzFxnhKjn2RYwJLvgeP70Gd1yyEADws5d24TtPbxv3d3g5bMTvFtqxN1GDVQB2LJ4xN/8Qo4O9ej9EAUWPoNeNs+Yz58wbewq7xmdCVZxzIXKMpN88nYiZVgFiHoOGE3H45+J9EUdwIrr0x5TIlKJum1V40NThgURO92fRks5HwiVLUMPMJSon+oHk+NU3LJ1ZZCciExE9qX743DJSijZ2K6m9rwJPfA5TDz0FAEh7BBRvhsGPqdbeONThPRG7dgKJkReiD/TEhHUiSpKEJZOrAABvH+hlD15xP/Cl7cCJNwDTT2OP7XlpxN/n9/Kg18VaqRhOxBKKiEZC8+sAYCppzm0umUgrRjkzvGLNswAAVfo1OtGXV2r2REXcGQmRMwPxlOFUCQss4PBwlVwb75eDq6gQsc08+BK1Z1YhvSzKxaWS6YlYgBNR79U3S5B+iBxJkrBiFhso/GtH/iXNaUU1RFUnS48awj5UBjxQNWDTwfwDi6KJzLkVFNSpYjgR8xARRXeac3j5b64JzaILAgDwidNm4JtXHAMAeODl3eNOno2eevr9TlQKCVbp16/xEYHFUQ5PQs8nxOhAD993AgoDAJbovR4LDdDaIkA/RE5GbCvUiSjm9IVX3RwxduKTzxFExED0IADAXTOtqNtmlZqQN69eltyx2CKwIxsAqqprMKjp25hDuEo8pSAAkZ2IVQAAKd6P2Xr7lFFFHFUFHr8OWPcbhOOsX2dnaE4JNtIajRE/3LKEtKph0KVfz6LdwIG1wI+WA09+YcTfyypnDoklIgLAYl1ENK7xLg8QbmTfTz+dfd398oi/22nuhwiUviciAExaBkguoP8g0H8IC/MMV0nE4/BI+hjLI+AYyhvKOFgpXIVExIkA763VUulHZVDcwb3RFzHHcBWzq0hEJyJQmNjGey4BgF9QUSDf1SOgfFwq9bycuYCeiBknolgiIgCcOpuVdPzspV249Mf/wmNvHDlZGY2OgQRUDfC4JNSFnOv1KEmS8TnyTTAGMm5Yn1uG2yXm7Y33vdl5eNAoPRmPcnAiAsBUPc0413Jm7kqvrxA3TAAArj5xKsJ+NxRVw+5x+sQe5EKUwH3AAL1nElg5c679HsvlGg+wFg8el4Q9XVHsybG3r4ihKmaWTKkCAGzgLpU8EcqJOFrZ7zjEBQ8tOkXvT7x2X48hugMwORGHBeMkh1CRZo6WUMOMUmxiwUiSZDjAcnGQlkM5M6CHq2i5h6u098VNTkQBhQ7diYhEP5ZNZmPVt0ZrgXBwLTDYDvgi+Ovsr+PcxD3YVX92iTa0cFyyZLQL6Vb18XisG9j1AgAN2PEcE0hNaJqGg1lORLHKmQFg8RS2794+MEJg5wxdRNz/OpA+cv7SNcgeq+XBiKVOZwaYyFY5iX3fuz8zl2zLbS6ZjJnm0iI6EQHg/G8AH3wQqJrq9JY4jtgzEiInNuki4iI96ENU8k26NPd2E7WJdiFiGxcEvC4ZsixOXz0z85vCkCRWttI5mJvYxntrVQbEFbKBjBPx8EAir7AETdOMnoizG8S7uZ2/qAmnz6mDJLEByP/709s57zve3L4x4nf8mDxrLlsdfjGPMkQOd7aJ3Jx+UlUAFT430jkIUpyycyJ2jf+5UoqKHe3sfBJB1BgLSZKMQBy+zaMhuhDFqdUXC5JpFUM59lHNuM3FvsYD7Bpw/DRW5pZrSfMhwffdopYIZAlo70+gvT8/8U1RNaMnIneHOAl3IuaTUg+I716eVhvCjLoQFFXDq+ags0rdiTi8J6L+/z4tiIaGphJtZeHwkubx+iLGkorhchY5nRlggjYPV8nFibi3oweypI8dPQIuFgVrAR+7p55RzVofvLVvFBFxOythxpzzsK7yHLyrTRa23+hw+HW6Q9FFxGgX0Po2+z7RD3TtyHp+fyyNgUQKtdDnawI6EXk5867OIaO3skH9fOYqTMeY+DsMfr7VhbxMZNR7XJY8hVpvD4CBQ1jYwo7Dd9oHc+rLOTig9+WEm7kwRWTJVcCiKzKBPkcxJCIKSudgAo+/uR+/f3385p2bD7IVi2NaBBcRq/MTEfnExuuW4RHUVVSI2JZJWhXzMwHM+Tm9lglluQqkfILCyydEpTHiR8DjQlJRR17tG4X2/gSGkgpcsmQ4rkSiwufGr68/Ca//57mYXB2ApuW+77gbRITB/pl6GeLbB/uMldVcGeJpv4K2PwCYIDW3kZ0j23MMVzGuGYL2UOUYPRFzuMbvOjyEpKIi5HUZ9waRmdPAhJcd4ySHHxS8JJYT8LqMyWL34PglzZqmGU7ESEBMAWc4/FryUo4tHkQXgINet+FkzrekeW/XEGIpBT63bNzbnWRuIxs7bW3tzxbbxoG33agSeLHyTKMfp8lNX2kqZzYvXvayMf4BrR6Ta8Q87szkGojDFyZDXpfw14tJVX60gYuIB8Z+MoADh009Sd0C7jNJApqOBQAsdbPja9PB/pFbcXARcd57jXYw5SYiHkrq+yDaBbRuyDxhmNC2vyeKCKLwSXq4p2DBKgArReZVexuHz08kydQX8V9H/C43ctSEvBkXouQyyttLRqSZfe1vxZTqIEJeF5JpFbtyWDTfeZAt+KVkAcV54gjEnpEcxezsGMSX//A2frhyx7jP3aw7EY+ZJLabw3Ai5hisEtVLE0OCuhCBwsS2uMDJzGYW5NHLIp5SsEsv9V0ouKvI65ZxzgK2AvnkhtzS+IBMKfPUmiC8Aos59WEfFuu9s3I9JnlfKhHSZBsjfixojkDTgJfz7O/YF2UrtyFBXSqceU3sHNmeY4kHH0xyp5+ocBHxQHcMijq2y5cfm/ObI467X3Nhji78vtsxtvCbcbOJva+ATO+krqHxxfp4KtNepByciABw4gwmDGw+lNtiUTkIwNypkm9JM2+5Mq8pLESrhyk1QXz0JNYD8L/+sslYKBmLeErBHr2nNhdTRYT341y1/XCm2oGXMycHgXiv8dx4524ATEQUVbw2w0uTx3OQGguTVQFIktjX9+bKAPaqet+5rl1jPldVNbR19QIANEkW1y3VtBgAUDe4HTUhL5KKaswVDbp2Aoe3AbIbmH0O3tXHuCJf/8zw7dwb08etfQeBXlOfumEiYlaoii8iposUpr6II13jeUnzCCIi781fF/aZ+iHWAHKJr/cmJ2K+AaS7W5mIqInYJoA4AudHEsSIHDOpErLEVvs6xihbiSUV7NAnNccIXs481ShnjuVURnpIFzaqgmL3yzLCVYbfoEchni6P/mYLmnLv97i9bQCqxhI/68PO9dTLlUuXsJvc395uhTqO2MERNZl5JPLZd0Cmv5GToSpmziwgWRXIJB6L2LPSzDzuRGwb29UGsEnL6l1sVfmU2SXsbVMALVUBeFwSkoo6brlbpj+buGKAmdl5ljO3CHIujQXvi5hLuAovZZYlsRf2zPB91t6fOLI0bBhDiTR69EUIkXu4jdkzawyM861JnEW+L184Dw1hH3Z3DuF/V+0c9/m7Dg9BUTVE/G40RsQdZ5w8sxY+t4y2/jje4dcLbzBTVmjqizjUzj73YVdjWYjzU3ThZuc4jmx+/W8W+FzitFQFsFPThY/Od8Z8bmt/nJWTAqwfoqgCqe5ElNo24ji9l+oRfRHfeZp9nXYK0t5KY7GF914VHS667xzS77XpYWOOI0TEKOrAQ1XEcyFylg5PaDZTv4B9HRbQtL1tAE9tZKX47z2mOZPMXMpQFY7JiQjkHkCqaRoOdrCxrssv9hieYIitYhzFhHxuYwC8YYzB4ra2fqgaUFfhM3q9iUpLlR+SxNL1OnMon3pzDysZOE7wG9rCPPsi8sbgooaqcPLp92hu2C76qjPAytwifjfa+uN4fU/3+L+AjHgwU3CBCsi/Vyd3IjZHxBA+zjKVIeYq8gLAJn0QvEhwV/ZcntCcQznzltZ+9MVSqPC5sVjwhSKXLGGh3lbjr+O4fLcIFPKQC3N059PuziGkRuntk1JUo1ddObg5Mk7E8e/HW3WBvrlSfGcRJ+L3oEm/pr07jujx+m52H2iM+ITu67vEmGD25dXTV0TRPuL34K5LFwEA7l+1E73RsY/D7e26e7lJ7HGG3+PCyTOZYJi1EDZCX8R0F3NORYMtJds+K3CB6e0DfWP2OOMLk6IHTAFMjNqlMeFD63wnu9x8GDs7Bo1QFUnEZGZOM3Miom0jlk2tAgCs29eb/Zzt/2Bf570X77QPIp5SEfa5MUOAdge5wO+xO/qHVZ408c++CUhlTDg72gdNoSri9UPk8EqiDftHmPtz8XOoK+vh7z+3HZoGXHRME46dXJn5eSlDVThhXUQcYCLisfq49YjjbxgHemKoS7Dye2/1lKJtHmEfJCIKzJKxViN0NplKmUUeVAEsFIAP6HMpaV6jD+pPmCF289J8E5ozTkTBRUS9Ie67HYMj91IxIeIEZSx8bhcuPIY1MR9P7OCs1Vdx+Q1eZPLZd0DG9SuKa2D5tGqEfW50DyXx9sHcHTfcDSx6f9h5uiC1rzuKIb1tw2i8ovcLO2lGjRBliONx3SmsRPHBV/eMeezx62W5iIgtlX6EvC6kVW3U4Ji2vjhUjbVMcDLlPFcMETGHRT3et46nz5YLuZahP7ulDQBw3sLGom+TFeY1heF1y+iLpbC3K7fWMIBYycxmLjqmCTPrQkgqKtaN0+eRO7fnNom/kMcXwl58x9QXsX4e+7pzpfGQq59NmtOR8kj6nF1fgbDfjVhKMZz/I2E4EQXoszwekYAb7R6WKCvFezP95EZg5+FBUzKzwJ+tbh7g8gKJPqyoYferrHCVvoPA3lfZ93MvNOaZx06uLIv2IkDGMb6vNwXNZ7quLXgfc+CpKaBtIwDW6ubJtw9lRESBnYjHTKqES5bQ1h/HnuF9BLkomOgD0uw43LC/F89sbockAbecN5f9nB/DpQ5VAYCIviDSz+ZWJ0xnc/j1+3vHHBO+faAPiyXmzJYnLyvuNhK2IP6M5Chmsb7iN5YTsVxCVTi5JjQn0grW6wPKE8tERNx5ODfBJtMTUezTr6XSj4ifpchuG0cgLTdBAADep5c0/2Nj66jOIk5/PIWtev+6E6eLfTwC2ftuPAcOALTxcmYBeiICgMcl4wx9EvboG/vHeTZjIJ4y0o4XtYh9HNZW+FBXwUSm8YI6Xt1ZHqXMnIuPbUFjxIfDAwn8bcPISZc8iEqSWDhVOSBJEmbr4u9oJc3mYI5ymIjVhng58/g9ETPHYXmJiLmUoSuqhue2tAMAzl8odkKuxyUb17dc+yL2RpPGQtF8we7RkiRl3G0jOW9McOf2PIFKskfjrHnM6fTGnm4M8oWiJR9mXzc8CiTY8RiIsom2u2Z6qTexIGRZwlJeHjta4i+yeyKKjiRJqKmqwgFNv8d2jt6LPktEFNmJ6PayNF8Ai1x74JIltPbFjcAbvPB1QFOAaacCNTOMawnvx1cO8HLmoaSCpLc684PmJcCk5ex7vaT592/sQzSpYH6F7kwU2IkY8rmNxboj+rb7q1hYCgBEu5BWVHzj71sAAFcsnWRUTBjlzE47ETUNM+pCqKvwIpkeO8xyw4FeLJH1nqQtJCKWA2KrGEc5Sybz3je9o5at8PI90UNVOLkmNL99oA/JtIq6Ci9m1oltrW+u9KMy4EFa1cbtlwVkklZFdyJKkoRTdeHikTdGTwnXNM0Q2MpJRFwxsxZ1FT70RFP487qDYz537Z4eaBowvTaIBkFKfsdCkqScHbIpRUXHABMRRHINXHMyc7T96a0DOaU0cxdiS6UftRXiu8C4a/fPb42eBplMq0aZ5allIt543TKuO2UGAOAXL+8a8d7FXVHTa0MICh6CY2YOF6RGEX55MEc59EMEgBrdLTleOXNvNGmMNU6ZVR5iNieXVO31+3vQOZhE2Oc2ylBFhlepvLYrt1Yc/B4wqSogZKm2Ub43jii6XXe+zRM4VIUzvTaIqTVBpBQtkz4940ygZhaQHAA2Pg4kBhFK9wIAQg3THdvWfFk2lQk2R/TYM8HLmcvlWthc6ccuVRc/usYQETuG4Je4E1Hwz6aXNPsObzYW697a2wscWg9seIQ95/yvA8iUzi6dUh6GFIDNoer0vr5b+0zjiKbFWSJiSlHx4Ct7AAAnN+pGj5C4IiKQMTn8dcOh7DGULLOwFAAYOoz/fnY73tjTg6DXhS9yFyJgClZxUERMx4FYDyRJMsxAfDw7Elv2tWOepJsGJpGIWA6QiCgw85si8Lpk9EZT2DeC6JZMq8agalHZOBGZSPHSjk4jSXUk+IXmhOk1wpdpM8GG3aBzSYFs03tm+QTviQgAnziNiQF/fOvgqELOgZ4YBuJpeFyS8IEWZtwuGTeczj7ft5/aOma4AO+beEIZuBA5XEQcL/CnvT8OTQM8LslwJonAiTNqsHhyJRJpFb9dM7qIzeGtHRYJ3jeQc90p0wEAD63eO2pJ/fr9vYilFNSGvJjbIP7EmXP1iVMR8LiwrW0Aq3ceWRrGRUTRk9yHM66IaHIilgP8fP/LuoOY/9V/4JMPvTGi6Pvari5oGnP1NZbBIoqZTDnz6CLis5uZC/E98xvgdYs/LOYl13/bcAix5PjVD6KWMnMyffZGXzAfiKeM86scRERJko4saZZl4PhPsO/f/D9gy18AAL1aCI0NYpfRm1k2TRcR97H9devjG3DZj/9ljKE0TTPKmVsEWpgcixl1oZzCVXYeHkTAKGcWPEHW6A34tjF2XbmlDXj2dgAacOwHgUnLEU8p2K67fMvJiQhkrmndGrsmdKEKaqgxS0R8amMr2vrjqA/7MM2vlwdXiFvODAAXLGqC1yVjR8egsW8M9FLsN7a8g5+9yJx7//1vS4xKPwDOOhE9fiCgz5X0voj8+BtNRFRUDeqht+GWVKQD9UBkUkk2lbCG+KOloxivWzZ6m41U0vz67m6kFA2VAQ8ml0ETd4ClrkoS2/Zzf/DiqOmrZhGxHDhxBnMvPPTq3jGDIJ7ccAjff5YNUMqht97x06qxZEoVkmkVv3ltZCGHT1BmN4TLYgJm5hOnzcD8pjB6oil866mtoz6PH4+il9abyTXwh4eqNFX6hSrBlCQJ1+si9sOr9xgO3tEot9YO5yxoxGfPmgUA+H9/eHvEkJVXd7KB4IpZtULtm/GoDHpwxTI2CHzy7SMF0nLrocrhgtSO9gHsaB/Ar17ZbaQWAxkn4qQqwSeXOsdNrYLXJUPVWJuN57d2ZPdw0+GlzKeWWT9EICP8HuyNZcpKTWiahmd5KfOi8hByVsysxZSaAAYSaSORcywyor2Y59vC5gjcsoTOwUzZ9XD49bEp4kdlUDw35UhwEXHV9sMZcXTp1YDLx3q1PfE5AMA/lBMxuaY8xvAAjHLmfd1RPLH+EP6w9gA2HOjDt/UxVE80ZbTtaRKkRcp4/NvyyYaImGjbPuJz+uMpdAwk4CuHcmbAJCJuxKVL2WdLbf4rsOdldgyecwcAZn5QVA11FT5hWtrkyvc+uAS/uOZ4rDhmDgBgozINb+3vZSXNANC9E794gR2X166YBpchrontRKwMeIzrx1/XDxtD6X0O//DyBgDADafPwMWLm7OfM+RgT0TA1BcxW0Rcu7cHyghz5F2HBzFXYQ5g1+Rl4qaeE1mU14z/KMQoaR7WcDqZVnHXk5sBAO9b0iy8W49z3NRqPHrjCsysD+HwQAKf+c1bODyQ7XBTVM0IsSgX0ebjp0xHhc+NLa39+MemthGf8/SmVvz7I+uQVjVccdwkQ0AQGUmS8EmTkPPjF3bgIw+8hifWZ8p/M/0QxZygjIXHJeObVxwLSQL+sPYAXtjWfsRz4inFaDpdLscjYAr8aeuHpmmj9us0QlUEdAy899hmNFf60TmYHDcAp9xaOwDAl86fh9Nm1yGWUowJGEfTNDytX0tOLZN+iGYu0oOLntvSnjVo7I0m8Zae0ieqM2o0zKWx7/3hy7j7yS34/O/XGQtHRiJpmSzqzWkMY+1Xz8W//t978NGTWbDD//1r9xHP4+E+K8qslBkAqoJe1IdZ2fbOEdyI73YMYnfnELwuGWfOFdudwpFlCVcez9Irc+kZK3q7Eb/HhXl6ueWGUcJVMqEq5TPOOHlmLbwuGQd7Y9h5WD/2gjXAMe83nvO/6Utxe/oTZeNeBpjAwcX5//rzRuPxx9cewOqdXYYLsa7CJ3zbHs7iyVXwNrBy0KFDIy8o7zrMXGwNAb2HtsjBKgDQyJLP0X8Qx9UqWF6v4Q75/9hjp3weqGLXfF7KvGRyZdnMJTmNET/OW9iIQAObT72pzmVjxWCt0Tuws+MQqoMefPTkacCgblwROFiFw4XfJ98eVtKsuwuDqR7MrA/hKxfOP/KXuVjqlIho9EVk4/YFzRGEfW4MJtJ4+0Avvv2PrbjziU1Gwvv6/b1YrPdDlLiLlBAeEhEFZ7GR0JztRPz5Szvxbscg6iq8+PL5I1xABObEGTV46gunY8nkSsRSCv531btZP9/a2o/BRBphn1vYQe9wqkNefFIvjf2f57YbF0aOomr45lNboWrAlcdPwf98cElZJK0CTAyYVBVA11AS33v2Hbzybhf+3x/fNvpalmtpImf5tGp89CTWf+/Tv3nrCCFx3b5epBQNjREfptaUh8MIYK4plyyhN5rCud9/EfNufxq/fm3vEc8TLVTFjMclG2W///fy7lFL3WJJxShXPKZMypkBwCVL+PrlxwBgJW988gUAr7zbhW1tAwh4XHjvMc2jvYSwnDSjFmG/G52DSazfzxaFntrYinO//yL2dUcR8rqMMsZyYVJVAH6PDEXVkFI0SBJzGf3wBbaCXm49EQEg7PdgcnUQnzpjFmQJeHlHJ7a1ZdzL+7qi2Hl4CLLEHHDlyFhl6L9/nYlwp82pQ9hfHg43APi35VMgS6zVhiFQjUBaUfGO3qtZ5PHUEiNIsHfEn3MnYrkEMQFA0OvGSTPZwuOq7SaH7zl3AEs/im1n/wL3pK9COOgvq2MPyPRFHEoqCHpduPhYdo/6r79sNHpMl9N1EADOOPVUAEAkfhDR2JEtpPgixBlevdxZ9JJLfwSoZQ496bFr8L3Ar1Av9WGfawpwxpexdm8PHn9zP57ZzBYry+1+nMUpN2HzqT/E/ykX4amNrUhrgKKX1NZKA/ivixeiKugFhvTzUPByZgA4Z34jgl4X9nfHsMpcIaD3OayV+vHhE6bCM9JccsjBcmYAiOhjVt2J6JIlLJ/Orhk3PPwmfvbiLjy0ei/+sp4JpL97fR+W6MnM1A+xfCgPFeMohje53XiwD6++24muwQQef3M/fvgCE96+esnCsintMOP3uPDlC5j4+dvX9hm9bl7f3Y0vPLIOALB8ejVcZVTCd/1pM1Ad9GDX4SE8tDpbrHl2cxv2d8dQFfTgrksXlVVpotsl4ysXzkNNyIv3zKvHMZMiiKdU3P6XTVi7txsv72A3t3IVEQHg9ksW4LyFjUimVXzq12vxD1OJ2Bt7eClzbVmt0vo9LszWe1Tu1FfQv/X3rTjQkz045imKIjoRAeCqE6ci6HVhe/sAXt7BBka/f30ffrRyB4b08sStbf1QNeZ8aAiLH6piZkZdCCfNqIGqMTcs54F/sVXZDx0/uSyv8V63jLPns5KhZze34187OvHZ376FzsEk5jRU4OHrTzISqssFWZbwgWWTMbk6gJ9cvQzf+zdWMnXfyh34yT/fNe5jk8uknNnMlJogLtLF6h+tfBe/fm0vPvrAGrznf1YBYH2Xy/E4BMwi4gCe39KO7z+7HbGkgv54Co/qoWHX6osV5UJTpR/v0ROAv/fM9iOExD2dQ7jtTxvx6d+sRTKtIuR1Cb0Ilqm6GbmvNBe255ZBP0Qz3N2a1SYg0oK+8+/FDa+xCf7pc8QXNIazfFomDffjp07Ht644FnUVPuw6PGS4mcvJXQkApx93LKLwww0Vt//fk7jpd29l/fvFy7sQwRBWxF9mv8DTtkXmou8A3jCw9xXM6Hgeqibh36OfxMce3oAP/PRVfPkPb2ON3q6nHFosjYovjLlnfwyBUASdg0n88pXdOJhkoZynNmv4wLJJLBE9pY9/BS9nBoCA14UPLp8MALj1sQ1GsnaHyuZadfKA0TYmC1UBYnrokRPBKgAQ1suZBzIVRLySq3Mw03/+3uffwTOb2/DuvkOYJevzrpbjSraZhDUcj0X8yU9+gv/+7/9GW1sblixZgh/96Ec48cQTnd4sYZhZV4HqoAc90RSufmBN1s9On1OHS/UEp3Lk1Nm1OHlmDV7b1Y0vP74BbpeMl/SBVl2FD7eYk6bKgLDfg8+cNQvfemobvv63LXhjdzfuvmwRGiN+PKAPqj560jQEvOVR3mHmsqWTcNlSdrN6t2MQ773vZbz4zmG8urMTKUXDiTNqyqrUdzg+twv/+5FluOWxDXhywyF87ndv4X8+tAQrZtbh72+zG9uJ06vHeRXxuP2SBXhywyGcNKMWj76xH6/v6cYdT2zGF8+di2//Yyt6oymjT5iITkSAlU596PgpePDVPXjgX7txeCCB2/7ESqgeeWM/rj9tBp7Te5odMylSVkIv56oTp2DN7m489uZ+3PSe2dh5eBCrth+GJGXCjcqR8xc24Yn1h/CPTW2G2+H9yybh2+8/tiyCpUbim1ccm/X/dft78JvX9uG/n2F9tCSpfPqADef602fg7xtbjX+c+U1hfOXCeQ5umTVm68LTH9ceNBrRH+iJYX5zGENJBXMaKnDGnPIr1f7IyVOxclsH/rGpDf/Y1IYTp9fgpx9dBrcs45pfvp4VyLd0apXQi5e86mbjwT6oqgZZlvDs5jbc88x2tPfHjftUOTkRAeCseQ34xt+34rVdXfjWU1tx5tx6BLwu/GjlDuzvjmFKTQBfv2yR05uZNyfNrIEssXHvjafPQmXQgx99+Dj83792Ia1q8LpkfKYMWvaYcblkxCIzEOzfisFDW/HsgSODAj/megUeLQk0LAQmH+/AVubJ7HOBG1cBj18LtG/CCzUfwrrWOcCOTsgScMqsOrhdEqbWBHFaGbZNMeNxybjomCb8ds0+fOupbTjGE8RUF3D9Mn1cOKSXMnuCgK88QiBve+8CvLGnB1ta+/GZ37yFR248Ga93SLgEwPxwYuSF2Gg3AL1qJ+jQvGyYExEATptdh3uwHUGvCz+5ehm+8se3caAnhpsfXY/jZL2NSuVU59yTRN44KiI++uijuOWWW3D//ffjpJNOwr333osLLrgA27dvR0OD+KsEpUCWJTz0iRPx29f24bmt7egeSmJ+UxgXLGrC9afPKMsJM0eSJHz5gnn4wE9XG43bAVbu+5/vXVCWrofrT5uJ/lga97+4E09vbsMrOztx9YlTsXZvD7wuGdecMs3pTbTM7IYKfOasWbhv5Q6kFA2nz6nDzz62vGzKs0fD45Jx75VL4XfLeHztAdzy2AYEPS4MJRUEPC68Z375XZNOn1NvuByWTKnERfe9jBe2deCf2zswvDJYVBERAD5x6gw8vHoPXnrnMF7bxa4VYZ8bB3tj+NrfthjP486ccuOiY5pxxxObcaAnhqc2teIfG5ngdv7CRkyrDTm8dYVz5rx6eF2yIWY0Rny4+9JFZSsgjsRd71uEeY1h/O3tVryxpxsnzagtu4ApzrKp1ThrXj1WbT+M46ZW4cJFTbhgUROm15XvMQhknIidg5n+y39adxC+jWw/fbJMx1Jnz2/Ej68+Do+/eQCv7uzE63u6cdXPX0NzVQD7uqOYXB3AJ0+bAZcs4ZwFYofGzGmoQMDjwmAijW/8fSv290SNxSFOS6UfsxvKY/LPmVUfwjGTIth0sB8/f2kXfv7SLuNnPreMn35kOSuzLDOm1YbwyI0rUBPyGmP1FbNqsaIMw5fM1Ew9Bti0FTcuVHHKjIXZP9Q0XP763UA/gGXXlk/4Q91s4IYXgPbNqFNmwPfz1zCrvgLf+cCxZZfGPB5XnzQVj795AAGvC4FwI9C/BY0u3aU9qLuBy6AfIsfvceH+jy7HJT96Gev39+KEbz6PsxUFl7iAGcGRQ6iMfoj+KsDl0Dx6BCfi4slV+NXHT8C0miBm1lfg82fPxh1PbEY8peJEny4iTiIXYjnhqIj4/e9/HzfccAM+/vGPAwDuv/9+/P3vf8cvf/lL/Md//IeTmyYUiydXYfG/VeGbior+eBo1ofIbcIzG8mk1+PSZs/DW3h6cNb8eFy5qwsz68hokmnHJEm69YB4uXtyM//jj29hwoA8/0weNly5tQUNYXKEmHz5z1izs6RpC2O/G7RcvLJvm2ePhkiV89wOL4fe48OvX9mIoqeC4qVX4zvsXY3K1uKVguTC7IYzPnDkLP3zhXWga8L4lLThzbj2e29KGlKLhNIGdOFNrg7hgURP+sakNybSKs+bV48dXL8OPXtiBtXt6cOKMGlx4TFPZDoj9HheuOG4SHl69Fzf9bp3x+CdPn+ngVlmnwufGqbNr8U+9H9jdly4qu95f4+F2yfjYiun42IrpiCbT8Je5QPrANccjmlIQmUD7aW5jGLIEqBrw2bNmoSrowbee2oZEWkVdhddw2ZcjlyxuwSWLW/BuxyA++sAa7OgYxI6OQXjdMu7/6PKy6RHrdslYPLkSa3Z345evsAmlS5Zw4xkz8cHlkyFJEpor/WU31pAkCX/49Cn457YOPL25DRv290LVgKDXhVvOm1s2+2ckyrn6ZDSkOtZD8PjQYRx/6rAqgINrgee3s2TjxR9yYOss4PYBk5ZhKYD1d5wPv0cuy4WT8VjUUol1d5wHr1uG55nngNf/mRHVuBOxorwWm6fWBvHTjy7HFx9dj46BBNqkCsAFRNSRWz843g8RMDkRDzFn5BsPAIuvxHvmZYw0V50wFT97cRcO9sZwSV0b0AWghfohlhOOiYjJZBJr167FbbfdZjwmyzLOPfdcrF69+ojnJxIJJBKZVeT+/v4jnjPRcbvkCSUgcv7jovIKhsmFBc0R/Omzp+JXr+zG/zz7DhRNM4JXJgJ+jwv3XTUxV4xkWcLXLluERS0RuGQJ7182uax6c47FTWfPQSTgwdzGMM7QezX9m95zRXRuPGMmntnchknVAdx75VJU+Ny47aIFTm+WbVx90lT8bs0+pFUN85vCuPaU6ThhevlP0i4/bhL+uf0wzl/YiAsWNTm9OUUl6HW8Q4xl3C4ZkTJ3lQ+nJuTFD65cCoC15tA0DRv29+HvG1vxidNmlJ0wNRKzGyrw2KdW4OoHXsOBnhi+cfkxZSdQ3XXpIjz6xn4oqgavW8b7l03Copby+gwj4fe4cNGxzbjo2PILyDrqaNLbVex+EVBVQNavhfF+4Jn/Yt8vvMy5MlEbKMeWSvkQ8un3Yd4PkItqRjJzeYmIAHDq7Dqsvu0crNvXgy1ve4C3AGno8MhPHmCVLI5+Tu5EjHYBf/wksHMl0L0buOKnxlO8bhm/uOZ4vPjOYcx+Sw8rolCVssKxEW9nZycURUFjY3aJRWNjI7Zt23bE87/97W/j7rvvLtXmEYRlXLKET54+E1ccNwmDiXRZlyUebUiShKtOnOr0ZtiO1y2XrbvtuKnVePrmM9AQ9pVl+dd4zG+K4JkvngG3LE2oa8WlS1owoy6E+U3l2a+SmBiY3YaSJOG+q5bi+tNnYGmZupdHYmptEM/cfAZa+2KY3VBevQMBtvh616Xl1x+QmEDMOhvwVQL9B4G9rwAzTmdlsL/9ANC6gYWUnHaz01tJ5EJIL62P6u2yuJhYBsnMI+GSJRw/vQbH1y8H3gIQ7wOU1JEly63r2dfGhcNfonQEa5hjV0kwARFg588wFrZEsDCSAFYdACABzUtLupmENcpmufm2225DX1+f8W///v1ObxJB5ERthW9CiQIE4RRzG8MTUkDkzKqvmHDXCkmSsHhyVdn2CSQmJm6XjGVTq4UOGymEkM9dlgIiQQiBxw8suox9//ajQDoJ/PoKJoAE64Dr/gY0ktBdFhzhRNR7rJZRT8QRCVQDkj6einYd+fND69lXJ1OOJQkID6s86XyHiZ7DOfQW+1o3B/BHir9thG04Nqqvq6uDy+VCe3t24+T29nY0NR1Z8uTz+RCJRLL+EQRBEARBEARBEIRlFl/Fvm55AnjpHqB9IxCoAT7xDNCy1NFNI/KA9wTkPRH7DrCvleXRwmdUZJkdj0BGIOWoasbx56SICAARvaS5fgFz8KopoHPHkc87qIuI1A+x7HBMRPR6vVi+fDlWrlxpPKaqKlauXIkVK1Y4tVkEQRAEQRAEQRDE0cbUFUDlFCDRD7z03+yxC7/DUo6J8mG4E7F3H/taNQFaFXGBdHhfxO6dQHIAcAeAunml3y4zi68EamezPoi8tLp985HP405E6odYdjhaX3TLLbfgF7/4BR566CFs3boVn/nMZzA0NGSkNRMEQRAEQRAEQRBE0ZFl4NgPZv4/6+zyS2MmMkJbvJeV0fbuZf+vmjbqr5QNvCR7eDnzoXXsa9OxgMvhoLfjPw58fi1zRDboImLHMBFR08iJWMY4eoRdeeWVOHz4MO644w60tbVh6dKlePrpp48IWyEIgiAIgiAIgiCIorLkKuCVe1k4xMXfZz3eiPIiUA1AAqCxfnypKHu83MuZASCoh8YML2fmIqLTpczD4X1E27dkP963n5Wby+5MMjpRNjgsUwM33XQTbrrpJqc3gyAIgiAIgiAIgjiaqZ8HXPME4K8EamY4vTVEIcgulhIc7cq43cLNgNvn7HbZwfB+jxzRRcQOXUQ88CZLQOdBKw0LWagRUVY4LiISBEEQBEEQBEEQhBDMOMPpLSCsEqxjIiLvuzcR+iECpn6Ppp6IqgK0vs2+Fy0AqGEB+9q3H+jYBjx4MZCOA5KLPU79EMsSR3siEgRBEARBEARBEARB2AZ37B2cYCJiaFhoDMCSj1NDgCcI1M11ZrtGI1ANRPQy8ic+ywREANAU9nXS8c5sF2EJciISBEEQBEEQBEEQBDEx4GIbTwWeaCKiOViFlzI3L2Gl3KLRuBDoPwAcXMv+/+FHgYFWoGcPBReVKSQiEgRBEARBEARBEAQxMeBlv6ree2+iiIjBYU7EXauA5+9k34uactywENjxLPt+8onA3AsosKjMIRGRIAiCIAiCIAiCIIiJAXfscaqmObMddsM/V/8h4A+fADb9CYAG1M0DThE0rLbxmMz3Z/4/EhAnACQiEgRBEARBEARBEAQxMQgOFxEniBMxVM++poaATX9k3y+7FrjwO4A36Nx2jcX0UwFvBTD5BGD2OU5vDWEDJCISBEEQBEEQBEEQBDExCNWa/iMBlZMd2xRbCdYCx30M6NgCzDgTmHcRMOVEp7dqbCItwK3vALKHXIgTBBIRCYIgCIIgCIIgCIKYGJidiOFmwO1zblvsRJKAy37s9Fbkjzfk9BYQNiI7vQEEQRAEQRAEQRAEQRC2YO6JOFFKmQlCEEhEJAiCIAiCIAiCIAhiYhAkEZEgigWJiARBEARBEARBEARBTAyCNZnvSUQkCFshEZEgCIIgCIIgCIIgiImBywP4q9j3JCIShK2QiEgQBEEQBEEQBEEQxMQh0sK+1sxwdjsIYoJB6cwEQRAEQRAEQRAEQUwcLvousO81YNppTm8JQUwoSEQkCIIgCIIgCIIgCGLiMOMM9o8gCFuhcmaCIAiCIAiCIAiCIAiCIMaERESCIAiCIAiCIAiCIAiCIMaERESCIAiCIAiCIAiCIAiCIMaERESCIAiCIAiCIAiCIAiCIMaERESCIAiCIAiCIAiCIAiCIMaERESCIAiCIAiCIAiCIAiCIMaERESCIAiCIAiCIAiCIAiCIMaERESCIAiCIAiCIAiCIAiCIMaERESCIAiCIAiCIAiCIAiCIMaERESCIAiCIAiCIAiCIAiCIMaERESCIAiCIAiCIAiCIAiCIMaERESCIAiCIAiCIAiCIAiCIMaERESCIAiCIAiCIAiCIAiCIMaERESCIAiCIAiCIAiCIAiCIMaERESCIAiCIAiCIAiCIAiCIMaERESCIAiCIAiCIAiCIAiCIMaERESCIAiCIAiCIAiCIAiCIMaERESCIAiCIAiCIAiCIAiCIMaERESCIAiCIAiCIAiCIAiCIMaERESCIAiCIAiCIAiCIAiCIMaERESCIAiCIAiCIAiCIAiCIMaERESCIAiCIAiCIAiCIAiCIMaERESCIAiCIAiCIAiCIAiCIMaERESCIAiCIAiCIAiCIAiCIMaERESCIAiCIAiCIAiCIAiCIMbE7fQGFIqmaQCA/v5+h7eEIAiCIAiCIAiCIAiCIMoPrqtxnW0sylZEHBgYAABMmTLF4S0hCIIgCIIgCIIgCIIgiPJlYGAAlZWVYz5H0nKRGgVEVVUcOnQI4XAYAwMDmDJlCvbv349IJOL0phHEhKG/v5/OLYIoAnRuEURxoHOLIIoHnV8EURzo3CofJuq+0jQNAwMDaGlpgSyP3fWwbJ2Isixj8uTJAABJkgAAkUhkQu1IghAFOrcIojjQuUUQxYHOLYIoHnR+EURxoHOrfJiI+2o8ByKHglUIgiAIgiAIgiAIgiAIghgTEhEJgiAIgiAIgiAIgiAIghiTCSEi+nw+3HnnnfD5fE5vCkFMKOjcIojiQOcWQRQHOrcIonjQ+UUQxYHOrfKB9lUZB6sQBEEQBEEQBEEQBEEQBFEaJoQTkSAIgiAIgiAIgiAIgiCI4kEiIkEQBEEQBEEQBEEQBEEQY0IiIkEQBEEQBEEQBEEQBEEQY0IiIkEQBEEQBEEQBEEQBEEQY5KXiPjtb38bJ5xwAsLhMBoaGnD55Zdj+/btWc+Jx+P43Oc+h9raWlRUVOADH/gA2tvbs57zhS98AcuXL4fP58PSpUtHfK9nnnkGJ598MsLhMOrr6/GBD3wAe/bsGXcbH3/8ccyfPx9+vx/HHnssnnrqqVGf++lPfxqSJOHee+8d93X37duHiy++GMFgEA0NDfjyl7+MdDqd9Zyf/OQnWLBgAQKBAObNm4eHH3543NclCODoPrfG2+bt27fjPe95DxobG+H3+zFz5kzcfvvtSKVS4742QdC5Nfo233XXXZAk6Yh/oVBo3NcmiKP13NqwYQM+/OEPY8qUKQgEAliwYAHuu+++rOe0trbi6quvxty5cyHLMm6++eZxt5UgzND5Nfr5tWrVqhHvXW1tbeNuM0HQuTX6uQWIpWdMhH113XXXHXGtuvDCC8d93fG0J6fHGXmJiC+++CI+97nP4bXXXsNzzz2HVCqF888/H0NDQ8ZzvvjFL+LJJ5/E448/jhdffBGHDh3C+9///iNe6xOf+ASuvPLKEd9n9+7duOyyy3D22Wdj/fr1eOaZZ9DZ2Tni65h59dVX8eEPfxjXX3891q1bh8svvxyXX345Nm3adMRz//znP+O1115DS0vLuJ9bURRcfPHFSCaTePXVV/HQQw/hwQcfxB133GE856c//Sluu+023HXXXdi8eTPuvvtufO5zn8OTTz457usTxNF6buWyzR6PB9dccw2effZZbN++Hffeey9+8Ytf4M4778z59YmjFzq3Rt/mW2+9Fa2trVn/Fi5ciA9+8IM5vz5x9HK0nltr165FQ0MDfvOb32Dz5s34r//6L9x222348Y9/bDwnkUigvr4et99+O5YsWTLuaxLEcOj8Gv384mzfvj3r/tXQ0DDu6xMEnVujn1ui6RkTZV9deOGFWdeq3//+92O+bi7ak+PjDM0CHR0dGgDtxRdf1DRN03p7ezWPx6M9/vjjxnO2bt2qAdBWr159xO/feeed2pIlS454/PHHH9fcbremKIrx2F//+ldNkiQtmUyOuj0f+tCHtIsvvjjrsZNOOkn71Kc+lfXYgQMHtEmTJmmbNm3Spk2bpv3gBz8Y83M+9dRTmizLWltbm/HYT3/6Uy0SiWiJRELTNE1bsWKFduutt2b93i233KKdeuqpY742QYzE0XJu5bLNI/HFL35RO+2003J+bYLg0Lk1OuvXr9cAaC+99FLOr00QnKPx3OJ89rOf1d7znveM+LMzzzxT+/d///e8X5MgzND5lTm//vnPf2oAtJ6enrxfiyCGQ+dW5twSXc8ox3117bXXapdddlmuH1HTtNy0JzNOjDMs9UTs6+sDANTU1ABgCncqlcK5555rPGf+/PmYOnUqVq9enfPrLl++HLIs41e/+hUURUFfXx9+/etf49xzz4XH4xn191avXp313gBwwQUXZL23qqr42Mc+hi9/+ctYtGhRTtuzevVqHHvssWhsbMx63f7+fmzevBkAU4P9fn/W7wUCAbz++utUdknkzdFybhXCu+++i6effhpnnnlm0d6DmLjQuTU6DzzwAObOnYvTTz+9aO9BTFyO5nOrr6/P+NwEUQzo/Dry/Fq6dCmam5tx3nnn4ZVXXin49YmjGzq3MueW6HpGOe4rgLVgaGhowLx58/CZz3wGXV1dY25PLtqT0xQsIqqqiptvvhmnnnoqjjnmGABAW1sbvF4vqqqqsp7b2NiYV5+KGTNm4Nlnn8V//ud/wufzoaqqCgcOHMBjjz025u+1tbVl/bFHeu/vfve7cLvd+MIXvpDz9oz2uvxnANuxDzzwANauXQtN0/Dmm2/igQceQCqVQmdnZ87vRRBH07mVD6eccgr8fj/mzJmD008/HV/72teK8j7ExIXOrdGJx+P47W9/i+uvv75o70FMXI7mc+vVV1/Fo48+ihtvvLHg1yCIsaDzK/v8am5uxv33348//vGP+OMf/4gpU6bgrLPOwltvvVXw+xBHJ3RuZZ9bIusZ5bqvLrzwQjz88MNYuXIlvvvd7+LFF1/ERRddBEVR8n5d/jMRKFhE/NznPodNmzbhkUcesXN7ALA/zg033IBrr70Wb7zxBl588UV4vV7827/9GzRNw759+1BRUWH8+9a3vpXT665duxb33XcfHnzwQUiSNOJzLrroIuN181H2v/rVr+Kiiy7CySefDI/Hg8suuwzXXnstAECWKQSbyB06t0bm0UcfxVtvvYXf/e53+Pvf/47vfe97eb8GcXRD59bo/PnPf8bAwIBx3yKIfDhaz61Nmzbhsssuw5133onzzz/f0uckiNGg8yv7/Jo3bx4+9alPYfny5TjllFPwy1/+Eqeccgp+8IMfFPZHII5a6NzKPrdE1jPKcV8BwFVXXYVLL70Uxx57LC6//HL87W9/wxtvvIFVq1YBsGcM7wTuQn7ppptuwt/+9je89NJLmDx5svF4U1MTkskkent7sxTh9vZ2NDU15fz6P/nJT1BZWYl77rnHeOw3v/kNpkyZgjVr1uD444/H+vXrjZ9xS2tTU9MRaTzm93755ZfR0dGBqVOnGj9XFAVf+tKXcO+992LPnj144IEHEIvFAMCwrzY1NeH1118/4nX5zwBm9f3lL3+Jn/3sZ2hvb0dzczN+/vOfGwk/BJELR9u5lQ9TpkwBACxcuBCKouDGG2/El770Jbhcrrxfizj6oHNrbB544AFccsklR6x8EsR4HK3n1pYtW3DOOefgxhtvxO23357z5yGIfKDzK7fz68QTT8S//vWvnD83QdC5deS5JaqeUa77aiRmzpyJuro6vPvuuzjnnHMK1p6cJi8RUdM0fP7zn8ef//xnrFq1CjNmzMj6+fLly+HxeLBy5Up84AMfAMCSs/bt24cVK1bk/D7RaPQItZsLBaqqwu12Y/bs2Uf83ooVK7By5cqsiOvnnnvOeO+PfexjI9atf+xjH8PHP/5xAMCkSZNGfN1vfvOb6OjoMJK/nnvuOUQiESxcuDDruR6Pxzi4H3nkEVxyySWOK/eE+Byt51ahqKqKVCoFVVVJRCTGhM6t8dm9ezf++c9/4q9//aul1yGOLo7mc2vz5s04++yzce211+Kb3/xmzp+FIHKFzq/8zq/169ejubk5p+cSRzd0bo1/bomiZ5T7vhqJAwcOoKury7heWdWeHCOfFJbPfOYzWmVlpbZq1SqttbXV+BeNRo3nfPrTn9amTp2qvfDCC9qbb76prVixQluxYkXW6+zYsUNbt26d9qlPfUqbO3eutm7dOm3dunVG2szKlSs1SZK0u+++W3vnnXe0tWvXahdccIE2bdq0rPcaziuvvKK53W7te9/7nrZ161btzjvv1Dwej7Zx48ZRfyeXNKN0Oq0dc8wx2vnnn6+tX79ee/rpp7X6+nrttttuM56zfft27de//rX2zjvvaGvWrNGuvPJKraamRtu9e/eYr00Qmnb0nlu5bPNvfvMb7dFHH9W2bNmi7dy5U3v00Ue1lpYW7SMf+ci4r00QdG6Nvs2c22+/XWtpadHS6fS4r0kQnKP13Nq4caNWX1+vffSjH8363B0dHVnP459j+fLl2tVXX62tW7dO27x585ivTRAcOr9GP79+8IMfaH/5y1+0HTt2aBs3btT+/d//XZNlWXv++efHfG2C0DQ6t8Y6t0TTM8p9Xw0MDGi33nqrtnr1am337t3a888/ry1btkybM2eOFo/HR33dXLQnTXN2nJGXiAhgxH+/+tWvjOfEYjHts5/9rFZdXa0Fg0Htiiuu0FpbW7Ne58wzzxzxdcwH6O9//3vtuOOO00KhkFZfX69deuml2tatW8fdxscee0ybO3eu5vV6tUWLFml///vfx3x+rpOxPXv2aBdddJEWCAS0uro67Utf+pKWSqWMn2/ZskVbunSpFggEtEgkol122WXatm3bxn1dgtC0o/vcGm+bH3nkEW3ZsmVaRUWFFgqFtIULF2rf+ta3tFgsNu5rEwSdW2Nvs6Io2uTJk7X//M//HPf1CMLM0Xpu3XnnnSNu77Rp08b9+wx/DkGMBp1fo5873/3ud7VZs2Zpfr9fq6mp0c466yzthRdeGHd7CULT6Nwa69wSTc8o930VjUa1888/X6uvr9c8Ho82bdo07YYbbtDa2trGfd3xtKfR/j6lGmdI+gYQBEEQBEEQBEEQBEEQBEGMCDXrIwiCIAiCIAiCIAiCIAhiTEhEJAiCIAiCIAiCIAiCIAhiTEhEJAiCIAiCIAiCIAiCIAhiTEhEJAiCIAiCIAiCIAiCIAhiTEhEJAiCIAiCIAiCIAiCIAhiTEhEJAiCIAiCIAiCIAiCIAhiTEhEJAiCIAiCIAiCIAiCIAhiTEhEJAiCIAiCIAzuuusuLF261LbXO+uss3DzzTfb9noEQRAEQRCEM5CISBAEQRAEcRSQq5h36623YuXKlcXfIIIgCIIgCKKscDu9AQRBEARBEITzaJoGRVFQUVGBiooKpzfHMslkEl6v1+nNIAiCIAiCmDCQE5EgCIIgCGKCc9111+HFF1/EfffdB0mSIEkSHnzwQUiShH/84x9Yvnw5fD4f/vWvfx1Rznzdddfh8ssvx9133436+npEIhF8+tOfRjKZzPn9VVXFV77yFdTU1KCpqQl33XVX1s/37duHyy67DBUVFYhEIvjQhz6E9vb2I7bBzM0334yzzjrL+P9ZZ52Fm266CTfffDPq6upwwQUX5PMnIgiCIAiCIMaBRESCIAiCIIgJzn333YcVK1bghhtuQGtrK1pbWzFlyhQAwH/8x3/gO9/5DrZu3YrFixeP+PsrV67E1q1bsWrVKvz+97/Hn/70J9x99905v/9DDz2EUCiENWvW4J577sHXvvY1PPfccwCYwHjZZZehu7sbL774Ip577jns2rULV155Zd6f86GHHoLX68Urr7yC+++/P+/fJwiCIAiCIEaHypkJgiAIgiAmOJWVlfB6vQgGg2hqagIAbNu2DQDwta99Deedd96Yv+/1evHLX/4SwWAQixYtwte+9jV8+ctfxte//nXI8vhr0osXL8add94JAJgzZw5+/OMfY+XKlTjvvPOwcuVKbNy4Ebt37zaEzYcffhiLFi3CG2+8gRNOOCHnzzlnzhzcc889OT+fIAiCIAiCyB1yIhIEQRAEQRzFHH/88eM+Z8mSJQgGg8b/V6xYgcHBQezfvz+n9xjucGxubkZHRwcAYOvWrZgyZYohIALAwoULUVVVha1bt+b0+pzly5fn9XyCIAiCIAgid0hEJAiCIAiCOIoJhUJFfw+Px5P1f0mSoKpqzr8vyzI0Tct6LJVKHfG8UnwWgiAIgiCIoxUSEQmCIAiCII4CvF4vFEUp6Hc3bNiAWCxm/P+1115DRUVFlnuwUBYsWID9+/dnuRq3bNmC3t5eLFy4EABQX1+P1tbWrN9bv3695fcmCIIgCIIgcodERIIgCIIgiKOA6dOnY82aNdizZw86OzvzcgImk0lcf/312LJlC5566inceeeduOmmm3Lqhzge5557Lo499lh85CMfwVtvvYXXX38d11xzDc4880yj1Prss8/Gm2++iYcffhg7duzAnXfeiU2bNll+b4IgCIIgCCJ3SEQkCIIgCII4Crj11lvhcrmwcOFC1NfXY9++fTn/7jnnnIM5c+bgjDPOwJVXXolLL70Ud911ly3bJUkSnnjiCVRXV+OMM87Aueeei5kzZ+LRRx81nnPBBRfgq1/9Kr7yla/ghBNOwMDAAK655hpb3p8gCIIgCILIDUkb3mCGIAiCIAiCIHSuu+469Pb24i9/+YvTm0IQBEEQBEE4CDkRCYIgCIIgCIIgCIIgCIIYExIRCYIgCIIgiILYt28fKioqRv2XT8k0QRAEQRAEITZUzkwQBEEQBEEURDqdxp49e0b9+fTp0+F2u0u3QQRBEARBEETRIBGRIAiCIAiCIAiCIAiCIIgxoXJmgiAIgiAIgiAIgiAIgiDGhEREgiAIgiAIgiAIgiAIgiDGhEREgiAIgiAIgiAIgiAIgiDGhEREgiAIgiAIgiAIgiAIgiDGhEREgiAIgiAIgiAIgiAIgiDGhEREgiAIgiAIgiAIgiAIgiDGhEREgiAIgiAIgiAIgiAIgiDGhEREgiAIgiAIgiAIgiAIgiDG5P8DRHIhX9/Vj+0AAAAASUVORK5CYII=", "text/plain": [ "
" ] From be9a89f18319fa1cb1be676fc2033961dc800d16 Mon Sep 17 00:00:00 2001 From: Chelsea Lin Date: Tue, 24 Jun 2025 11:32:06 -0700 Subject: [PATCH 04/28] chore: add more types for read_gbq_table in conftest (#1843) * chore: add more types for read_gbq_table in conftest * add bug id * undo dtype conversions for repeated_pandas_df * undo unrelated changes * fix json --- bigframes/core/compile/sqlglot/sqlglot_ir.py | 2 +- tests/unit/core/compile/sqlglot/conftest.py | 120 +++++++++++++++++- .../test_compile_concat/out.sql | 24 ++-- .../test_compile_readlocal_w_json_df/out.sql | 7 +- .../out.sql | 10 ++ .../out.sql | 11 ++ .../out.sql | 23 ++++ .../compile/sqlglot/test_compile_readlocal.py | 19 ++- .../compile/sqlglot/test_compile_readtable.py | 14 ++ .../google_cloud_bigquery/_pandas_helpers.py | 2 + 10 files changed, 202 insertions(+), 30 deletions(-) create mode 100644 tests/unit/core/compile/sqlglot/snapshots/test_compile_readtable/test_compile_readtable_w_json_types/out.sql create mode 100644 tests/unit/core/compile/sqlglot/snapshots/test_compile_readtable/test_compile_readtable_w_nested_structs_types/out.sql create mode 100644 tests/unit/core/compile/sqlglot/snapshots/test_compile_readtable/test_compile_readtable_w_repeated_types/out.sql diff --git a/bigframes/core/compile/sqlglot/sqlglot_ir.py b/bigframes/core/compile/sqlglot/sqlglot_ir.py index 3766ab3266..6b805802b0 100644 --- a/bigframes/core/compile/sqlglot/sqlglot_ir.py +++ b/bigframes/core/compile/sqlglot/sqlglot_ir.py @@ -182,7 +182,7 @@ def from_union( selections = [ sge.Alias( - this=expr.alias_or_name, + this=sge.to_identifier(expr.alias_or_name, quoted=cls.quoted), alias=sge.to_identifier(output_id, quoted=cls.quoted), ) for expr, output_id in zip(select_expr.expressions, output_ids) diff --git a/tests/unit/core/compile/sqlglot/conftest.py b/tests/unit/core/compile/sqlglot/conftest.py index 20f8159f01..6d5fac8184 100644 --- a/tests/unit/core/compile/sqlglot/conftest.py +++ b/tests/unit/core/compile/sqlglot/conftest.py @@ -29,24 +29,48 @@ DATA_DIR = CURRENT_DIR.parent.parent.parent.parent / "data" -@pytest.fixture(scope="session") -def compiler_session(scalars_types_table_schema): +def _create_compiler_session(table_name, table_schema): + """Helper function to create a compiler session.""" from bigframes.testing import compiler_session - # TODO: Check if ordering mode is needed for the tests. - table_name = "scalar_types" anonymous_dataset = bigquery.DatasetReference.from_string( "bigframes-dev.sqlglot_test" ) session = mocks.create_bigquery_session( table_name=table_name, - table_schema=scalars_types_table_schema, + table_schema=table_schema, anonymous_dataset=anonymous_dataset, ) session._executor = compiler_session.SQLCompilerExecutor() return session +@pytest.fixture(scope="session") +def compiler_session(scalars_types_table_schema): + """Compiler session for scalar types.""" + return _create_compiler_session("scalar_types", scalars_types_table_schema) + + +@pytest.fixture(scope="session") +def compiler_session_w_repeated_types(repeated_types_table_schema): + """Compiler session for repeated data types.""" + return _create_compiler_session("repeated_types", repeated_types_table_schema) + + +@pytest.fixture(scope="session") +def compiler_session_w_nested_structs_types(nested_structs_types_table_schema): + """Compiler session for nested STRUCT data types.""" + return _create_compiler_session( + "nested_structs_types", nested_structs_types_table_schema + ) + + +@pytest.fixture(scope="session") +def compiler_session_w_json_types(json_types_table_schema): + """Compiler session for JSON data types.""" + return _create_compiler_session("json_types", json_types_table_schema) + + @pytest.fixture(scope="session") def scalars_types_table_schema() -> typing.Sequence[bigquery.SchemaField]: return [ @@ -91,6 +115,40 @@ def scalars_types_pandas_df() -> pd.DataFrame: return df +@pytest.fixture(scope="session") +def nested_structs_types_table_schema() -> typing.Sequence[bigquery.SchemaField]: + return [ + bigquery.SchemaField("id", "INTEGER"), + bigquery.SchemaField( + "people", + "RECORD", + fields=[ + bigquery.SchemaField("name", "STRING"), + bigquery.SchemaField("age", "INTEGER"), + bigquery.SchemaField( + "address", + "RECORD", + fields=[ + bigquery.SchemaField("city", "STRING"), + bigquery.SchemaField("country", "STRING"), + ], + ), + ], + ), + ] + + +@pytest.fixture(scope="session") +def nested_structs_types_df(compiler_session_w_nested_structs_types) -> bpd.DataFrame: + """Returns a BigFrames DataFrame containing all scalar types and using the `rowindex` + column as the index.""" + bf_df = compiler_session_w_nested_structs_types.read_gbq_table( + "bigframes-dev.sqlglot_test.nested_structs_types" + ) + bf_df = bf_df.set_index("id", drop=False) + return bf_df + + @pytest.fixture(scope="session") def nested_structs_pandas_df() -> pd.DataFrame: """Returns a pandas DataFrame containing STRUCT types and using the `id` @@ -117,7 +175,32 @@ def nested_structs_pandas_df() -> pd.DataFrame: @pytest.fixture(scope="session") -def repeated_pandas_df() -> pd.DataFrame: +def repeated_types_table_schema() -> typing.Sequence[bigquery.SchemaField]: + return [ + bigquery.SchemaField("rowindex", "INTEGER"), + bigquery.SchemaField("int_list_col", "INTEGER", "REPEATED"), + bigquery.SchemaField("bool_list_col", "BOOLEAN", "REPEATED"), + bigquery.SchemaField("float_list_col", "FLOAT", "REPEATED"), + bigquery.SchemaField("date_list_col", "DATE", "REPEATED"), + bigquery.SchemaField("date_time_list_col", "DATETIME", "REPEATED"), + bigquery.SchemaField("numeric_list_col", "NUMERIC", "REPEATED"), + bigquery.SchemaField("string_list_col", "STRING", "REPEATED"), + ] + + +@pytest.fixture(scope="session") +def repeated_types_df(compiler_session_w_repeated_types) -> bpd.DataFrame: + """Returns a BigFrames DataFrame containing all scalar types and using the `rowindex` + column as the index.""" + bf_df = compiler_session_w_repeated_types.read_gbq_table( + "bigframes-dev.sqlglot_test.repeated_types" + ) + bf_df = bf_df.set_index("rowindex", drop=False) + return bf_df + + +@pytest.fixture(scope="session") +def repeated_types_pandas_df() -> pd.DataFrame: """Returns a pandas DataFrame containing LIST types and using the `rowindex` column as the index.""" @@ -125,10 +208,31 @@ def repeated_pandas_df() -> pd.DataFrame: DATA_DIR / "repeated.jsonl", lines=True, ) + # TODO: add dtype conversion here if needed. df = df.set_index("rowindex") return df +@pytest.fixture(scope="session") +def json_types_table_schema() -> typing.Sequence[bigquery.SchemaField]: + return [ + bigquery.SchemaField("rowindex", "INTEGER"), + bigquery.SchemaField("json_col", "JSON"), + ] + + +@pytest.fixture(scope="session") +def json_types_df(compiler_session_w_json_types) -> bpd.DataFrame: + """Returns a BigFrames DataFrame containing JSON types and using the `rowindex` + column as the index.""" + bf_df = compiler_session_w_json_types.read_gbq_table( + "bigframes-dev.sqlglot_test.json_types" + ) + # TODO(b/427305807): Why `drop=False` will produce two "rowindex" columns? + bf_df = bf_df.set_index("rowindex", drop=True) + return bf_df + + @pytest.fixture(scope="session") def json_pandas_df() -> pd.DataFrame: """Returns a pandas DataFrame containing JSON types and using the `rowindex` @@ -149,8 +253,10 @@ def json_pandas_df() -> pd.DataFrame: ] df = pd.DataFrame( { + "rowindex": pd.Series(range(len(json_data)), dtype=dtypes.INT_DTYPE), "json_col": pd.Series(json_data, dtype=dtypes.JSON_DTYPE), }, - index=pd.Series(range(len(json_data)), dtype=dtypes.INT_DTYPE), ) + # TODO(b/427305807): Why `drop=False` will produce two "rowindex" columns? + df = df.set_index("rowindex", drop=True) return df diff --git a/tests/unit/core/compile/sqlglot/snapshots/test_compile_concat/test_compile_concat/out.sql b/tests/unit/core/compile/sqlglot/snapshots/test_compile_concat/test_compile_concat/out.sql index 8da545b8fa..62e22a6a19 100644 --- a/tests/unit/core/compile/sqlglot/snapshots/test_compile_concat/test_compile_concat/out.sql +++ b/tests/unit/core/compile/sqlglot/snapshots/test_compile_concat/test_compile_concat/out.sql @@ -49,21 +49,21 @@ WITH `bfcte_1` AS ( * FROM ( SELECT - bfcol_17 AS `bfcol_46`, - bfcol_18 AS `bfcol_47`, - bfcol_19 AS `bfcol_48`, - bfcol_20 AS `bfcol_49`, - bfcol_21 AS `bfcol_50`, - bfcol_22 AS `bfcol_51` + `bfcol_17` AS `bfcol_46`, + `bfcol_18` AS `bfcol_47`, + `bfcol_19` AS `bfcol_48`, + `bfcol_20` AS `bfcol_49`, + `bfcol_21` AS `bfcol_50`, + `bfcol_22` AS `bfcol_51` FROM `bfcte_6` UNION ALL SELECT - bfcol_40 AS `bfcol_46`, - bfcol_41 AS `bfcol_47`, - bfcol_42 AS `bfcol_48`, - bfcol_43 AS `bfcol_49`, - bfcol_44 AS `bfcol_50`, - bfcol_45 AS `bfcol_51` + `bfcol_40` AS `bfcol_46`, + `bfcol_41` AS `bfcol_47`, + `bfcol_42` AS `bfcol_48`, + `bfcol_43` AS `bfcol_49`, + `bfcol_44` AS `bfcol_50`, + `bfcol_45` AS `bfcol_51` FROM `bfcte_7` ) ) diff --git a/tests/unit/core/compile/sqlglot/snapshots/test_compile_readlocal/test_compile_readlocal_w_json_df/out.sql b/tests/unit/core/compile/sqlglot/snapshots/test_compile_readlocal/test_compile_readlocal_w_json_df/out.sql index 100036d75f..4e21266b87 100644 --- a/tests/unit/core/compile/sqlglot/snapshots/test_compile_readlocal/test_compile_readlocal_w_json_df/out.sql +++ b/tests/unit/core/compile/sqlglot/snapshots/test_compile_readlocal/test_compile_readlocal_w_json_df/out.sql @@ -1,10 +1,11 @@ WITH `bfcte_0` AS ( SELECT * - FROM UNNEST(ARRAY>[STRUCT(PARSE_JSON('null'), 0), STRUCT(PARSE_JSON('true'), 1), STRUCT(PARSE_JSON('100'), 2), STRUCT(PARSE_JSON('0.98'), 3), STRUCT(PARSE_JSON('"a string"'), 4), STRUCT(PARSE_JSON('[]'), 5), STRUCT(PARSE_JSON('[1,2,3]'), 6), STRUCT(PARSE_JSON('[{"a":1},{"a":2},{"a":null},{}]'), 7), STRUCT(PARSE_JSON('"100"'), 8), STRUCT(PARSE_JSON('{"date":"2024-07-16"}'), 9), STRUCT(PARSE_JSON('{"int_value":2,"null_filed":null}'), 10), STRUCT(PARSE_JSON('{"list_data":[10,20,30]}'), 11)]) + FROM UNNEST(ARRAY>[STRUCT(0, PARSE_JSON('null'), 0), STRUCT(1, PARSE_JSON('true'), 1), STRUCT(2, PARSE_JSON('100'), 2), STRUCT(3, PARSE_JSON('0.98'), 3), STRUCT(4, PARSE_JSON('"a string"'), 4), STRUCT(5, PARSE_JSON('[]'), 5), STRUCT(6, PARSE_JSON('[1,2,3]'), 6), STRUCT(7, PARSE_JSON('[{"a":1},{"a":2},{"a":null},{}]'), 7), STRUCT(8, PARSE_JSON('"100"'), 8), STRUCT(9, PARSE_JSON('{"date":"2024-07-16"}'), 9), STRUCT(10, PARSE_JSON('{"int_value":2,"null_filed":null}'), 10), STRUCT(11, PARSE_JSON('{"list_data":[10,20,30]}'), 11)]) ) SELECT - `bfcol_0` AS `json_col` + `bfcol_0` AS `rowindex`, + `bfcol_1` AS `json_col` FROM `bfcte_0` ORDER BY - `bfcol_1` ASC NULLS LAST \ No newline at end of file + `bfcol_2` ASC NULLS LAST \ No newline at end of file diff --git a/tests/unit/core/compile/sqlglot/snapshots/test_compile_readtable/test_compile_readtable_w_json_types/out.sql b/tests/unit/core/compile/sqlglot/snapshots/test_compile_readtable/test_compile_readtable_w_json_types/out.sql new file mode 100644 index 0000000000..4e8f61d75d --- /dev/null +++ b/tests/unit/core/compile/sqlglot/snapshots/test_compile_readtable/test_compile_readtable_w_json_types/out.sql @@ -0,0 +1,10 @@ +WITH `bfcte_0` AS ( + SELECT + `rowindex` AS `bfcol_0`, + `json_col` AS `bfcol_1` + FROM `bigframes-dev`.`sqlglot_test`.`json_types` +) +SELECT + `bfcol_0` AS `rowindex`, + `bfcol_1` AS `json_col` +FROM `bfcte_0` \ No newline at end of file diff --git a/tests/unit/core/compile/sqlglot/snapshots/test_compile_readtable/test_compile_readtable_w_nested_structs_types/out.sql b/tests/unit/core/compile/sqlglot/snapshots/test_compile_readtable/test_compile_readtable_w_nested_structs_types/out.sql new file mode 100644 index 0000000000..75c4a86e18 --- /dev/null +++ b/tests/unit/core/compile/sqlglot/snapshots/test_compile_readtable/test_compile_readtable_w_nested_structs_types/out.sql @@ -0,0 +1,11 @@ +WITH `bfcte_0` AS ( + SELECT + `id` AS `bfcol_0`, + `people` AS `bfcol_1` + FROM `bigframes-dev`.`sqlglot_test`.`nested_structs_types` +) +SELECT + `bfcol_0` AS `id`, + `bfcol_0` AS `id_1`, + `bfcol_1` AS `people` +FROM `bfcte_0` \ No newline at end of file diff --git a/tests/unit/core/compile/sqlglot/snapshots/test_compile_readtable/test_compile_readtable_w_repeated_types/out.sql b/tests/unit/core/compile/sqlglot/snapshots/test_compile_readtable/test_compile_readtable_w_repeated_types/out.sql new file mode 100644 index 0000000000..2436c01a44 --- /dev/null +++ b/tests/unit/core/compile/sqlglot/snapshots/test_compile_readtable/test_compile_readtable_w_repeated_types/out.sql @@ -0,0 +1,23 @@ +WITH `bfcte_0` AS ( + SELECT + `rowindex` AS `bfcol_0`, + `int_list_col` AS `bfcol_1`, + `bool_list_col` AS `bfcol_2`, + `float_list_col` AS `bfcol_3`, + `date_list_col` AS `bfcol_4`, + `date_time_list_col` AS `bfcol_5`, + `numeric_list_col` AS `bfcol_6`, + `string_list_col` AS `bfcol_7` + FROM `bigframes-dev`.`sqlglot_test`.`repeated_types` +) +SELECT + `bfcol_0` AS `rowindex`, + `bfcol_0` AS `rowindex_1`, + `bfcol_1` AS `int_list_col`, + `bfcol_2` AS `bool_list_col`, + `bfcol_3` AS `float_list_col`, + `bfcol_4` AS `date_list_col`, + `bfcol_5` AS `date_time_list_col`, + `bfcol_6` AS `numeric_list_col`, + `bfcol_7` AS `string_list_col` +FROM `bfcte_0` \ No newline at end of file diff --git a/tests/unit/core/compile/sqlglot/test_compile_readlocal.py b/tests/unit/core/compile/sqlglot/test_compile_readlocal.py index 58587da129..bd27ad450e 100644 --- a/tests/unit/core/compile/sqlglot/test_compile_readlocal.py +++ b/tests/unit/core/compile/sqlglot/test_compile_readlocal.py @@ -30,26 +30,31 @@ def test_compile_readlocal( def test_compile_readlocal_w_structs_df( nested_structs_pandas_df: pd.DataFrame, - compiler_session: bigframes.Session, + compiler_session_w_nested_structs_types: bigframes.Session, snapshot, ): - bf_df = bpd.DataFrame(nested_structs_pandas_df, session=compiler_session) + # TODO(b/427306734): Check why the output is different from the expected output. + bf_df = bpd.DataFrame( + nested_structs_pandas_df, session=compiler_session_w_nested_structs_types + ) snapshot.assert_match(bf_df.sql, "out.sql") def test_compile_readlocal_w_lists_df( - repeated_pandas_df: pd.DataFrame, - compiler_session: bigframes.Session, + repeated_types_pandas_df: pd.DataFrame, + compiler_session_w_repeated_types: bigframes.Session, snapshot, ): - bf_df = bpd.DataFrame(repeated_pandas_df, session=compiler_session) + bf_df = bpd.DataFrame( + repeated_types_pandas_df, session=compiler_session_w_repeated_types + ) snapshot.assert_match(bf_df.sql, "out.sql") def test_compile_readlocal_w_json_df( json_pandas_df: pd.DataFrame, - compiler_session: bigframes.Session, + compiler_session_w_json_types: bigframes.Session, snapshot, ): - bf_df = bpd.DataFrame(json_pandas_df, session=compiler_session) + bf_df = bpd.DataFrame(json_pandas_df, session=compiler_session_w_json_types) snapshot.assert_match(bf_df.sql, "out.sql") diff --git a/tests/unit/core/compile/sqlglot/test_compile_readtable.py b/tests/unit/core/compile/sqlglot/test_compile_readtable.py index 63849f093c..d3b5140471 100644 --- a/tests/unit/core/compile/sqlglot/test_compile_readtable.py +++ b/tests/unit/core/compile/sqlglot/test_compile_readtable.py @@ -23,6 +23,20 @@ def test_compile_readtable(scalars_types_df: bpd.DataFrame, snapshot): snapshot.assert_match(scalars_types_df.sql, "out.sql") +def test_compile_readtable_w_repeated_types(repeated_types_df: bpd.DataFrame, snapshot): + snapshot.assert_match(repeated_types_df.sql, "out.sql") + + +def test_compile_readtable_w_nested_structs_types( + nested_structs_types_df: bpd.DataFrame, snapshot +): + snapshot.assert_match(nested_structs_types_df.sql, "out.sql") + + +def test_compile_readtable_w_json_types(json_types_df: bpd.DataFrame, snapshot): + snapshot.assert_match(json_types_df.sql, "out.sql") + + def test_compile_readtable_w_ordering(scalars_types_df: bpd.DataFrame, snapshot): bf_df = scalars_types_df[["int64_col"]] bf_df = bf_df.sort_values("int64_col") diff --git a/third_party/bigframes_vendored/google_cloud_bigquery/_pandas_helpers.py b/third_party/bigframes_vendored/google_cloud_bigquery/_pandas_helpers.py index 5e2a7a7ef0..3e35b1382e 100644 --- a/third_party/bigframes_vendored/google_cloud_bigquery/_pandas_helpers.py +++ b/third_party/bigframes_vendored/google_cloud_bigquery/_pandas_helpers.py @@ -17,6 +17,7 @@ import warnings +import db_dtypes import google.cloud.bigquery.schema as schema import pyarrow @@ -61,6 +62,7 @@ def pyarrow_timestamp(): "TIME": pyarrow_time, "TIMESTAMP": pyarrow_timestamp, "BIGNUMERIC": pyarrow_bignumeric, + "JSON": db_dtypes.JSONArrowType, } ARROW_SCALAR_IDS_TO_BQ = { # https://arrow.apache.org/docs/python/api/datatypes.html#type-classes From 15e1277b1413de18a5e36f72959a99701d6df08b Mon Sep 17 00:00:00 2001 From: Shenyang Cai Date: Tue, 24 Jun 2025 12:00:17 -0700 Subject: [PATCH 05/28] docs: add data visualization samples for public doc (#1847) --- samples/snippets/data_visualization_test.py | 149 ++++++++++++++++++++ 1 file changed, 149 insertions(+) create mode 100644 samples/snippets/data_visualization_test.py diff --git a/samples/snippets/data_visualization_test.py b/samples/snippets/data_visualization_test.py new file mode 100644 index 0000000000..64cbbe0511 --- /dev/null +++ b/samples/snippets/data_visualization_test.py @@ -0,0 +1,149 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (t +# you may not use this file except in compliance wi +# 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 +# distributed under the License is distributed on a +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, eit +# See the License for the specific language governi +# limitations under the License. + + +def test_data_visualization() -> None: + # [START bigquery_dataframes_data_visualization_penguin_histogram] + import bigframes.pandas as bpd + + penguins = bpd.read_gbq("bigquery-public-data.ml_datasets.penguins") + penguins["culmen_depth_mm"].plot.hist(bins=40) + # [END bigquery_dataframes_data_visualization_penguin_histogram] + + # [START bigquery_dataframes_data_visualization_noaa_line_chart] + import bigframes.pandas as bpd + + noaa_surface = bpd.read_gbq("bigquery-public-data.noaa_gsod.gsod2021") + + # Calculate median temperature for each day + noaa_surface_median_temps = noaa_surface[["date", "temp"]].groupby("date").median() + + noaa_surface_median_temps.plot.line() + # [END bigquery_dataframes_data_visualization_noaa_line_chart] + + # [START bigquery_dataframes_data_visualization_usa_names_area_chart] + import bigframes.pandas as bpd + + usa_names = bpd.read_gbq("bigquery-public-data.usa_names.usa_1910_2013") + + # Count the occurences of the target names each year. The result is a dataframe with a multi-index. + name_counts = ( + usa_names[usa_names["name"].isin(("Mary", "Emily", "Lisa"))] + .groupby(("year", "name"))["number"] + .sum() + ) + + # Flatten the index of the dataframe so that the counts for each name has their own columns. + name_counts = name_counts.unstack(level=1).fillna(0) + + name_counts.plot.area(stacked=False, alpha=0.5) + # [END bigquery_dataframes_data_visualization_usa_names_area_chart] + + # [START bigquery_dataframes_data_visualization_penguin_bar_chart] + import bigframes.pandas as bpd + + penguins = bpd.read_gbq("bigquery-public-data.ml_datasets.penguins") + + penguin_count_by_sex = ( + penguins[penguins["sex"].isin(("MALE", "FEMALE"))] + .groupby("sex")["species"] + .count() + ) + penguin_count_by_sex.plot.bar() + # [END bigquery_dataframes_data_visualization_penguin_bar_chart] + + # [START bigquery_dataframes_data_visualization_taxi_scatter_plot] + import bigframes.pandas as bpd + + taxi_trips = bpd.read_gbq( + "bigquery-public-data.new_york_taxi_trips.tlc_yellow_trips_2021" + ).dropna() + + # Data Cleaning + taxi_trips = taxi_trips[ + taxi_trips["trip_distance"].between(0, 10, inclusive="right") + ] + taxi_trips = taxi_trips[taxi_trips["fare_amount"].between(0, 50, inclusive="right")] + + # If you are using partial ordering mode, you will also need to assign an order to your dataset. + # Otherwise, the next line can be skipped. + taxi_trips = taxi_trips.sort_values("pickup_datetime") + + taxi_trips.plot.scatter(x="trip_distance", y="fare_amount", alpha=0.5) + # [END bigquery_dataframes_data_visualization_taxi_scatter_plot] + + # [START bigquery_dataframes_data_visualization_noaa_sampling_n] + import bigframes.pandas as bpd + + noaa_surface = bpd.read_gbq("bigquery-public-data.noaa_gsod.gsod2021") + + # Calculate median temperature for each day + noaa_surface_median_temps = noaa_surface[["date", "temp"]].groupby("date").median() + + noaa_surface_median_temps.plot.line(sampling_n=40) + # [END bigquery_dataframes_data_visualization_noaa_sampling_n] + + # [START bigquery_dataframes_data_visualization_usa_names_subplots] + import bigframes.pandas as bpd + + usa_names = bpd.read_gbq("bigquery-public-data.usa_names.usa_1910_2013") + + # Count the occurences of the target names each year. The result is a dataframe with a multi-index. + name_counts = ( + usa_names[usa_names["name"].isin(("Mary", "Emily", "Lisa"))] + .groupby(("year", "name"))["number"] + .sum() + ) + + # Flatten the index of the dataframe so that the counts for each name has their own columns. + name_counts = name_counts.unstack(level=1).fillna(0) + + name_counts.plot.area(subplots=True, alpha=0.5) + # [END bigquery_dataframes_data_visualization_usa_names_subplots] + + # [START bigquery_dataframes_data_visualization_taxi_scatter_multidimension] + import bigframes.pandas as bpd + + taxi_trips = bpd.read_gbq( + "bigquery-public-data.new_york_taxi_trips.tlc_yellow_trips_2021" + ).dropna() + + # Data Cleaning + taxi_trips = taxi_trips[ + taxi_trips["trip_distance"].between(0, 10, inclusive="right") + ] + taxi_trips = taxi_trips[taxi_trips["fare_amount"].between(0, 50, inclusive="right")] + + # If you are using partial ordering mode, you also need to assign an order to your dataset. + # Otherwise, the next line can be skipped. + taxi_trips = taxi_trips.sort_values("pickup_datetime") + + taxi_trips["passenger_count_scaled"] = taxi_trips["passenger_count"] * 30 + + taxi_trips.plot.scatter( + x="trip_distance", + xlabel="trip distance (miles)", + y="fare_amount", + ylabel="fare amount (usd)", + alpha=0.5, + s="passenger_count_scaled", + label="passenger_count", + c="tip_amount", + cmap="jet", + colorbar=True, + legend=True, + figsize=(15, 7), + sampling_n=1000, + ) + # [END bigquery_dataframes_data_visualization_taxi_scatter_multidimension] From c06d8db9b5e8372c0c5e932181e1a09f7158ac44 Mon Sep 17 00:00:00 2001 From: Shenyang Cai Date: Tue, 24 Jun 2025 14:19:09 -0700 Subject: [PATCH 06/28] chore!: remove ai.top_k(). (#1842) * chore: remove ai.top_k(). * remove redundant import --- bigframes/operations/ai.py | 203 ---------------------- notebooks/experimental/ai_operators.ipynb | 123 ------------- tests/system/large/operations/test_ai.py | 59 ------- tests/system/small/operations/test_ai.py | 24 --- 4 files changed, 409 deletions(-) diff --git a/bigframes/operations/ai.py b/bigframes/operations/ai.py index 10c842c64c..8c7628059a 100644 --- a/bigframes/operations/ai.py +++ b/bigframes/operations/ai.py @@ -19,8 +19,6 @@ from typing import Dict, Iterable, List, Optional, Sequence, Union import warnings -import numpy as np - from bigframes import dtypes, exceptions, options from bigframes.core import guid, log_adapter @@ -586,207 +584,6 @@ def search( return typing.cast(bigframes.dataframe.DataFrame, search_result) - def top_k( - self, - instruction: str, - model, - k: int = 10, - ground_with_google_search: bool = False, - ): - """ - Ranks each tuple and returns the k best according to the instruction. - - This method employs a quick select algorithm to efficiently compare the pivot - with all other items. By leveraging an LLM (Large Language Model), it then - identifies the top 'k' best answers from these comparisons. - - **Examples:** - - >>> import bigframes.pandas as bpd - >>> bpd.options.display.progress_bar = None - >>> bpd.options.experiments.ai_operators = True - >>> bpd.options.compute.ai_ops_confirmation_threshold = 25 - - >>> import bigframes.ml.llm as llm - >>> model = llm.GeminiTextGenerator(model_name="gemini-2.0-flash-001") - - >>> df = bpd.DataFrame( - ... { - ... "Animals": ["Dog", "Bird", "Cat", "Horse"], - ... "Sounds": ["Woof", "Chirp", "Meow", "Neigh"], - ... }) - >>> df.ai.top_k("{Animals} are more popular as pets", model=model, k=2) - Animals Sounds - 0 Dog Woof - 2 Cat Meow - - [2 rows x 2 columns] - - Args: - instruction (str): - An instruction on how to map the data. This value must contain - column references by name enclosed in braces. - For example, to reference a column named "Animals", use "{Animals}" in the - instruction, like: "{Animals} are more popular as pets" - - model (bigframes.ml.llm.GeminiTextGenerator): - A GeminiTextGenerator provided by the Bigframes ML package. - - k (int, default 10): - The number of rows to return. - - ground_with_google_search (bool, default False): - Enables Grounding with Google Search for the GeminiTextGenerator model. - When set to True, the model incorporates relevant information from Google - Search results into its responses, enhancing their accuracy and factualness. - Note: Using this feature may impact billing costs. Refer to the pricing - page for details: https://cloud.google.com/vertex-ai/generative-ai/pricing#google_models - The default is `False`. - - Returns: - bigframes.dataframe.DataFrame: A new DataFrame with the top k rows. - - Raises: - NotImplementedError: when the AI operator experiment is off. - ValueError: when the instruction refers to a non-existing column, or when no - columns are referred to. - """ - if not options.experiments.ai_operators: - raise NotImplementedError() - - import bigframes.dataframe - import bigframes.series - - self._validate_model(model) - columns = self._parse_columns(instruction) - for column in columns: - if column not in self._df.columns: - raise ValueError(f"Column {column} not found.") - if len(columns) > 1: - raise NotImplementedError("AI top K are limited to a single column.") - - if ground_with_google_search: - msg = exceptions.format_message( - "Enables Grounding with Google Search may impact billing cost. See pricing " - "details: https://cloud.google.com/vertex-ai/generative-ai/pricing#google_models" - ) - warnings.warn(msg, category=UserWarning) - - work_estimate = int(len(self._df) * (len(self._df) - 1) / 2) - self._confirm_operation(work_estimate) - - df: bigframes.dataframe.DataFrame = self._df[columns].copy() - column = columns[0] - if df[column].dtype != dtypes.STRING_DTYPE: - df[column] = df[column].astype(dtypes.STRING_DTYPE) - - # `index` is reserved for the `reset_index` below. - if column == "index": - raise ValueError( - "Column name 'index' is reserved. Please choose a different name." - ) - - if k < 1: - raise ValueError("k must be an integer greater than or equal to 1.") - - user_instruction = self._format_instruction(instruction, columns) - - n = df.shape[0] - if k >= n: - return df - - # Create a unique index and duplicate it as the "index" column. This workaround - # is needed for the select search algorithm due to unimplemented bigFrame methods. - df = df.reset_index().rename(columns={"index": "old_index"}).reset_index() - - # Initialize a status column to track the selection status of each item. - # - None: Unknown/not yet processed - # - 1.0: Selected as part of the top-k items - # - -1.0: Excluded from the top-k items - status_column = guid.generate_guid("status") - df[status_column] = bigframes.series.Series( - None, dtype=dtypes.FLOAT_DTYPE, session=df._session - ) - - num_selected = 0 - while num_selected < k: - df, num_new_selected = self._topk_partition( - df, - column, - status_column, - user_instruction, - model, - k - num_selected, - ground_with_google_search, - ) - num_selected += num_new_selected - - result_df: bigframes.dataframe.DataFrame = self._df.copy() - return result_df[df.set_index("old_index")[status_column] > 0.0] - - @staticmethod - def _topk_partition( - df, - column: str, - status_column: str, - user_instruction: str, - model, - k: int, - ground_with_google_search: bool, - ): - output_instruction = ( - "Given a question and two documents, choose the document that best answers " - "the question. Respond with 'Document 1' or 'Document 2'. You must choose " - "one, even if neither is ideal. " - ) - - # Random pivot selection for improved average quickselect performance. - pending_df = df[df[status_column].isna()] - pivot_iloc = np.random.randint(0, pending_df.shape[0]) - pivot_index = pending_df.iloc[pivot_iloc]["index"] - pivot_df = pending_df[pending_df["index"] == pivot_index] - - # Build a prompt to compare the pivot item's relevance to other pending items. - prompt_s = pending_df[pending_df["index"] != pivot_index][column] - prompt_s = ( - f"{output_instruction}\n\nQuestion: {user_instruction}\n" - + f"\nDocument 1: {column} " - + pivot_df.iloc[0][column] - + f"\nDocument 2: {column} " - + prompt_s # type:ignore - ) - - import bigframes.dataframe - - predict_df = typing.cast( - bigframes.dataframe.DataFrame, - model.predict( - prompt_s, - temperature=0.0, - ground_with_google_search=ground_with_google_search, - ), - ) - - marks = predict_df["ml_generate_text_llm_result"].str.contains("2") - more_relavant: bigframes.dataframe.DataFrame = df[marks] - less_relavent: bigframes.dataframe.DataFrame = df[~marks] - - num_more_relavant = more_relavant.shape[0] - if k < num_more_relavant: - less_relavent[status_column] = -1.0 - pivot_df[status_column] = -1.0 - df = df.combine_first(less_relavent).combine_first(pivot_df) - return df, 0 - else: # k >= num_more_relavant - more_relavant[status_column] = 1.0 - df = df.combine_first(more_relavant) - if k >= num_more_relavant + 1: - pivot_df[status_column] = 1.0 - df = df.combine_first(pivot_df) - return df, num_more_relavant + 1 - else: - return df, num_more_relavant - def sim_join( self, other, diff --git a/notebooks/experimental/ai_operators.ipynb b/notebooks/experimental/ai_operators.ipynb index f830787801..07e20f6bbd 100644 --- a/notebooks/experimental/ai_operators.ipynb +++ b/notebooks/experimental/ai_operators.ipynb @@ -1064,129 +1064,6 @@ "animals.ai.join(animals, \"{left.animal} generally weighs heavier than {right.animal}\", model=gemini_model)" ] }, - { - "cell_type": "markdown", - "metadata": { - "id": "kU7BsyTyiouX" - }, - "source": [ - "## AI Top K" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "s9QePXEoiouX" - }, - "source": [ - "AI Top K selects the top K values based on your instruction. Here is an example:" - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": { - "id": "bMQqtyZ2iouX" - }, - "outputs": [], - "source": [ - "df = bpd.DataFrame({\"Animals\": [\"Corgi\", \"Orange Cat\", \"Parrot\", \"Tarantula\"]})" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "KiljGBSCiouX" - }, - "source": [ - "You want to find the top two most popular pets:" - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/", - "height": 159 - }, - "id": "OZv5WUGIiouX", - "outputId": "ae1cee27-cc31-455e-c4ac-c0a9a5cf4ca5" - }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/usr/local/google/home/sycai/src/python-bigquery-dataframes/bigframes/core/array_value.py:114: PreviewWarning: JSON column interpretation as a custom PyArrow extention in\n", - "`db_dtypes` is a preview feature and subject to change.\n", - " warnings.warn(msg, bfe.PreviewWarning)\n" - ] - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
Animals
0Corgi
1Orange Cat
\n", - "

2 rows × 1 columns

\n", - "
[2 rows x 1 columns in total]" - ], - "text/plain": [ - " Animals\n", - "0 Corgi\n", - "1 Orange Cat\n", - "\n", - "[2 rows x 1 columns]" - ] - }, - "execution_count": 19, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "df.ai.top_k(\"{Animals} are more popular as pets\", model=gemini_model, k=2)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "dC8fyu3aiouX" - }, - "source": [ - "Under the hood, the AI top K operator performs pair-wise comparisons with LLM. The top K results are returned in the order of their indices instead of their ranks." - ] - }, { "cell_type": "markdown", "metadata": { diff --git a/tests/system/large/operations/test_ai.py b/tests/system/large/operations/test_ai.py index ded5e0b588..86b30d9c65 100644 --- a/tests/system/large/operations/test_ai.py +++ b/tests/system/large/operations/test_ai.py @@ -848,65 +848,6 @@ def test_sim_join_data_too_large_raises_error(session, text_embedding_generator) ) -@pytest.mark.parametrize( - "instruction", - [ - pytest.param( - "No column reference", - id="zero_column", - marks=pytest.mark.xfail(raises=ValueError), - ), - pytest.param( - "{Animals}", - id="non_existing_column", - marks=pytest.mark.xfail(raises=ValueError), - ), - pytest.param( - "{Animals} and {Animals}", - id="two_columns", - marks=pytest.mark.xfail(raises=NotImplementedError), - ), - pytest.param( - "{index}", - id="preserved", - marks=pytest.mark.xfail(raises=ValueError), - ), - ], -) -def test_top_k_invalid_instruction_raise_error(instruction, gemini_flash_model): - df = dataframe.DataFrame( - { - "Animals": ["Dog", "Cat", "Bird", "Horse"], - "ID": [1, 2, 3, 4], - "index": ["a", "b", "c", "d"], - } - ) - - with bigframes.option_context( - AI_OP_EXP_OPTION, - True, - THRESHOLD_OPTION, - 10, - ): - df.ai.top_k(instruction, model=gemini_flash_model, k=2) - - -def test_top_k_invalid_k_raise_error(gemini_flash_model): - df = dataframe.DataFrame({"Animals": ["Dog", "Cat", "Bird", "Horse"]}) - - with bigframes.option_context( - AI_OP_EXP_OPTION, - True, - THRESHOLD_OPTION, - 10, - ), pytest.raises(ValueError): - df.ai.top_k( - "{Animals} are more popular as pets", - gemini_flash_model, - k=0, - ) - - @patch("builtins.input", return_value="") def test_confirm_operation__below_threshold_do_not_confirm(mock_input): df = dataframe.DataFrame({}) diff --git a/tests/system/small/operations/test_ai.py b/tests/system/small/operations/test_ai.py index 771b7b47d3..d6ec3cacad 100644 --- a/tests/system/small/operations/test_ai.py +++ b/tests/system/small/operations/test_ai.py @@ -74,11 +74,6 @@ def predict(self, *args, **kwargs): {"search_column": None, "query": None, "top_k": None, "model": None}, id="search", ), - pytest.param( - bigframes.operations.ai.AIAccessor.top_k, - {"instruction": None, "model": None}, - id="top_k", - ), pytest.param( bigframes.operations.ai.AIAccessor.sim_join, {"other": None, "left_on": None, "right_on": None, "model": None}, @@ -247,25 +242,6 @@ def test_join(session): ) -def test_top_k(session): - df = dataframe.DataFrame({"col": ["A", "B"]}, session=session) - model = FakeGeminiTextGenerator( - dataframe.DataFrame( - {"ml_generate_text_llm_result": ["Document 1"]}, session=session - ), - ) - - with bigframes.option_context( - AI_OP_EXP_OPTION, - True, - THRESHOLD_OPTION, - 50, - ): - result = df.ai.top_k("top k of {col}", model, k=1).to_pandas() - - assert len(result) == 1 - - def test_forecast_default(time_series_df_default_index: dataframe.DataFrame): df = time_series_df_default_index[time_series_df_default_index["id"] == "1"] From c706759b85359b6d23ce3449f6ab138ad2d22f9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Swe=C3=B1a=20=28Swast=29?= Date: Tue, 24 Jun 2025 17:01:01 -0500 Subject: [PATCH 07/28] feat: create `deploy_remote_function` and `deploy_udf` functions to immediately deploy functions to BigQuery (#1832) * Refactor function deployment to avoid code duplication This commit refactors the implementation of immediate deployment for remote functions and UDFs to eliminate code duplication introduced in a previous commit. Changes: - The `remote_function` and `udf` methods in `bigframes.functions._function_session.FunctionSession` now accept an optional `deploy_immediately: bool` parameter (defaulting to `False`). The previous `deploy_remote_function` and `deploy_udf` methods in `FunctionSession` have been removed, and their logic is now incorporated into the unified methods. - The public API functions `bigframes.pandas.deploy_remote_function` and `bigframes.pandas.deploy_udf` now call the corresponding `FunctionSession` methods with `deploy_immediately=True`. - The public API functions `bigframes.pandas.remote_function` and `bigframes.pandas.udf` call the `FunctionSession` methods with `deploy_immediately=False` (relying on the default). - Unit tests in `tests/unit/functions/test_remote_function.py` have been updated to patch the unified `FunctionSession` methods and verify the correct `deploy_immediately` boolean is passed based on which public API function is called. Note: The underlying provisioning logic in `FunctionSession` currently deploys functions immediately regardless of the `deploy_immediately` flag. This flag serves as an indicator of intent and allows for future enhancements to support true lazy deployment if desired, without further API changes. * Refactor function deployment to use distinct methods This commit corrects a previous refactoring attempt to eliminate code duplication and properly separates immediate-deployment functions from standard (potentially lazy) functions. Changes: - `bigframes.functions._function_session.FunctionSession` now has distinct methods: `remote_function`, `udf`, `deploy_remote_function`, and `deploy_udf`. The `deploy_immediately` flag has been removed from this class. - `deploy_remote_function` and `deploy_udf` methods in `FunctionSession` are responsible for ensuring immediate deployment by calling the underlying provisioning logic directly. The standard `remote_function` and `udf` methods in `FunctionSession` also currently call this provisioning logic, meaning all functions are deployed immediately as of now, but the structure allows for future lazy evaluation for standard functions without changing the deploy variants' contract. - Public API functions in `bigframes.pandas` (`remote_function`, `udf`, `deploy_remote_function`, `deploy_udf`) now correctly delegate to their corresponding distinct methods in `FunctionSession` (via the `Session` object). - Unit tests in `tests/unit/functions/test_remote_function.py` have been updated to mock and verify calls to the correct distinct methods on `bigframes.session.Session`. This resolves the issue of using a boolean flag to control deployment type and instead relies on calling specific, dedicated methods for immediate deployment, aligning with your request. * Simplify internal deploy_remote_function and deploy_udf calls This commit simplifies the implementation of `deploy_remote_function` and `deploy_udf` within `bigframes.functions._function_session.FunctionSession`. Given that the standard `remote_function` and `udf` methods in `FunctionSession` already perform immediate deployment of resources (as the underlying provisioning logic they call is immediate), the `deploy_remote_function` and `deploy_udf` methods in the same class are simplified to directly call `self.remote_function(...)` and `self.udf(...)` respectively. This change makes the distinction between the `deploy_` variants and the standard variants in `FunctionSession` primarily a matter of semantic clarity and intent at that level; both paths currently result in immediate deployment. The public API in `bigframes.pandas` continues to offer distinct `deploy_` functions that call these `FunctionSession.deploy_` methods, preserving your user-facing API and its documented behavior of immediate deployment. No changes were needed for the public API in `bigframes.pandas` or the unit tests, as they were already aligned with calling distinct methods on the `Session` object, which in turn calls the now-simplified `FunctionSession` methods. * add tests and use kwargs * add missing func argument to bpd --------- Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> --- bigframes/core/global_session.py | 4 +- bigframes/functions/_function_session.py | 50 +++++++++++++ bigframes/pandas/__init__.py | 32 ++++++++ bigframes/session/__init__.py | 78 +++++++++++++++++++- tests/unit/functions/test_remote_function.py | 54 ++++++++++++++ 5 files changed, 214 insertions(+), 4 deletions(-) diff --git a/bigframes/core/global_session.py b/bigframes/core/global_session.py index 8732b55990..4698e4c4c5 100644 --- a/bigframes/core/global_session.py +++ b/bigframes/core/global_session.py @@ -110,8 +110,8 @@ def get_global_session(): _T = TypeVar("_T") -def with_default_session(func: Callable[..., _T], *args, **kwargs) -> _T: - return func(get_global_session(), *args, **kwargs) +def with_default_session(func_: Callable[..., _T], *args, **kwargs) -> _T: + return func_(get_global_session(), *args, **kwargs) class _GlobalSessionContext: diff --git a/bigframes/functions/_function_session.py b/bigframes/functions/_function_session.py index 9e7555431a..a7910127e4 100644 --- a/bigframes/functions/_function_session.py +++ b/bigframes/functions/_function_session.py @@ -668,6 +668,30 @@ def wrapper(func): return wrapper + def deploy_remote_function( + self, + func, + **kwargs, + ): + """Orchestrates the creation of a BigQuery remote function that deploys immediately. + + This method ensures that the remote function is created and available for + use in BigQuery as soon as this call is made. + + Args: + kwargs: + All arguments are passed directly to + :meth:`~bigframes.session.Session.remote_function`. Please see + its docstring for parameter details. + + Returns: + A wrapped remote function, usable in + :meth:`~bigframes.series.Series.apply`. + """ + # TODO(tswast): If we update remote_function to defer deployment, update + # this method to deploy immediately. + return self.remote_function(**kwargs)(func) + def udf( self, input_types: Union[None, type, Sequence[type]] = None, @@ -866,6 +890,32 @@ def wrapper(func): return wrapper + def deploy_udf( + self, + func, + **kwargs, + ): + """Orchestrates the creation of a BigQuery UDF that deploys immediately. + + This method ensures that the UDF is created and available for + use in BigQuery as soon as this call is made. + + Args: + func: + Function to deploy. + kwargs: + All arguments are passed directly to + :meth:`~bigframes.session.Session.udf`. Please see + its docstring for parameter details. + + Returns: + A wrapped Python user defined function, usable in + :meth:`~bigframes.series.Series.apply`. + """ + # TODO(tswast): If we update udf to defer deployment, update this method + # to deploy immediately. + return self.udf(**kwargs)(func) + def _convert_row_processor_sig( signature: inspect.Signature, diff --git a/bigframes/pandas/__init__.py b/bigframes/pandas/__init__.py index a9d1c31865..ed999e62c1 100644 --- a/bigframes/pandas/__init__.py +++ b/bigframes/pandas/__init__.py @@ -117,6 +117,22 @@ def remote_function( remote_function.__doc__ = inspect.getdoc(bigframes.session.Session.remote_function) +def deploy_remote_function( + func, + **kwargs, +): + return global_session.with_default_session( + bigframes.session.Session.deploy_remote_function, + func=func, + **kwargs, + ) + + +deploy_remote_function.__doc__ = inspect.getdoc( + bigframes.session.Session.deploy_remote_function +) + + def udf( *, input_types: Union[None, type, Sequence[type]] = None, @@ -140,6 +156,20 @@ def udf( udf.__doc__ = inspect.getdoc(bigframes.session.Session.udf) +def deploy_udf( + func, + **kwargs, +): + return global_session.with_default_session( + bigframes.session.Session.deploy_udf, + func=func, + **kwargs, + ) + + +deploy_udf.__doc__ = inspect.getdoc(bigframes.session.Session.deploy_udf) + + @typing.overload def to_datetime( arg: Union[ @@ -330,6 +360,8 @@ def reset_session(): clean_up_by_session_id, concat, cut, + deploy_remote_function, + deploy_udf, get_default_session_id, get_dummies, merge, diff --git a/bigframes/session/__init__.py b/bigframes/session/__init__.py index c06233bad3..13db6823c1 100644 --- a/bigframes/session/__init__.py +++ b/bigframes/session/__init__.py @@ -1343,6 +1343,40 @@ def _check_file_size(self, filepath: str): "for large files to avoid loading the file into local memory." ) + def deploy_remote_function( + self, + func, + **kwargs, + ): + """Orchestrates the creation of a BigQuery remote function that deploys immediately. + + This method ensures that the remote function is created and available for + use in BigQuery as soon as this call is made. + + Args: + func: + Function to deploy. + kwargs: + All arguments are passed directly to + :meth:`~bigframes.session.Session.remote_function`. Please see + its docstring for parameter details. + + Returns: + A wrapped remote function, usable in + :meth:`~bigframes.series.Series.apply`. + """ + return self._function_session.deploy_remote_function( + func, + # Session-provided arguments. + session=self, + bigquery_client=self._clients_provider.bqclient, + bigquery_connection_client=self._clients_provider.bqconnectionclient, + cloud_functions_client=self._clients_provider.cloudfunctionsclient, + resource_manager_client=self._clients_provider.resourcemanagerclient, + # User-provided arguments. + **kwargs, + ) + def remote_function( self, # Make sure that the input/output types, and dataset can be used @@ -1565,9 +1599,15 @@ def remote_function( `bigframes_remote_function` - The bigquery remote function capable of calling into `bigframes_cloud_function`. """ return self._function_session.remote_function( + # Session-provided arguments. + session=self, + bigquery_client=self._clients_provider.bqclient, + bigquery_connection_client=self._clients_provider.bqconnectionclient, + cloud_functions_client=self._clients_provider.cloudfunctionsclient, + resource_manager_client=self._clients_provider.resourcemanagerclient, + # User-provided arguments. input_types=input_types, output_type=output_type, - session=self, dataset=dataset, bigquery_connection=bigquery_connection, reuse=reuse, @@ -1585,6 +1625,37 @@ def remote_function( cloud_build_service_account=cloud_build_service_account, ) + def deploy_udf( + self, + func, + **kwargs, + ): + """Orchestrates the creation of a BigQuery UDF that deploys immediately. + + This method ensures that the UDF is created and available for + use in BigQuery as soon as this call is made. + + Args: + func: + Function to deploy. + kwargs: + All arguments are passed directly to + :meth:`~bigframes.session.Session.udf`. Please see + its docstring for parameter details. + + Returns: + A wrapped Python user defined function, usable in + :meth:`~bigframes.series.Series.apply`. + """ + return self._function_session.deploy_udf( + func, + # Session-provided arguments. + session=self, + bigquery_client=self._clients_provider.bqclient, + # User-provided arguments. + **kwargs, + ) + def udf( self, *, @@ -1726,9 +1797,12 @@ def udf( deployed for the user defined code. """ return self._function_session.udf( + # Session-provided arguments. + session=self, + bigquery_client=self._clients_provider.bqclient, + # User-provided arguments. input_types=input_types, output_type=output_type, - session=self, dataset=dataset, bigquery_connection=bigquery_connection, name=name, diff --git a/tests/unit/functions/test_remote_function.py b/tests/unit/functions/test_remote_function.py index 978281e5c9..ea09ac59d3 100644 --- a/tests/unit/functions/test_remote_function.py +++ b/tests/unit/functions/test_remote_function.py @@ -89,3 +89,57 @@ def function_without_return_annotation(myparam: int): match="'output_type' was not set .* missing a return type annotation", ): remote_function_decorator(function_without_return_annotation) + + +def test_deploy_remote_function(): + session = mocks.create_bigquery_session() + + def my_remote_func(x: int) -> int: + return x * 2 + + deployed = session.deploy_remote_function( + my_remote_func, cloud_function_service_account="test_sa@example.com" + ) + + # Test that the function would have been deployed somewhere. + assert deployed.bigframes_bigquery_function + + +def test_deploy_remote_function_with_name(): + session = mocks.create_bigquery_session() + + def my_remote_func(x: int) -> int: + return x * 2 + + deployed = session.deploy_remote_function( + my_remote_func, + name="my_custom_name", + cloud_function_service_account="test_sa@example.com", + ) + + # Test that the function would have been deployed somewhere. + assert "my_custom_name" in deployed.bigframes_bigquery_function + + +def test_deploy_udf(): + session = mocks.create_bigquery_session() + + def my_remote_func(x: int) -> int: + return x * 2 + + deployed = session.deploy_udf(my_remote_func) + + # Test that the function would have been deployed somewhere. + assert deployed.bigframes_bigquery_function + + +def test_deploy_udf_with_name(): + session = mocks.create_bigquery_session() + + def my_remote_func(x: int) -> int: + return x * 2 + + deployed = session.deploy_udf(my_remote_func, name="my_custom_name") + + # Test that the function would have been deployed somewhere. + assert "my_custom_name" in deployed.bigframes_bigquery_function From 0709f17ed01839f34cce272eeed5d745daa9e30f Mon Sep 17 00:00:00 2001 From: Shenyang Cai Date: Tue, 24 Jun 2025 15:34:13 -0700 Subject: [PATCH 08/28] refactor: provide infrastructure for SQLGlot scalar compiler (#1850) * refactor: provide infrastructure for SQLGlot scalar compiler * remove redundant code * remove redundant code * add TODO back --- .../compile/sqlglot/expressions/__init__.py | 13 +++++ .../sqlglot/expressions/binary_compiler.py | 49 +++++++++++++++++ .../sqlglot/expressions/nary_compiler.py | 32 +++++++++++ .../sqlglot/expressions/op_registration.py | 44 +++++++++++++++ .../sqlglot/expressions/ternary_compiler.py | 35 ++++++++++++ .../compile/sqlglot/expressions/typed_expr.py | 27 ++++++++++ .../sqlglot/expressions/unary_compiler.py | 31 +++++++++++ .../core/compile/sqlglot/scalar_compiler.py | 54 +++++-------------- 8 files changed, 244 insertions(+), 41 deletions(-) create mode 100644 bigframes/core/compile/sqlglot/expressions/__init__.py create mode 100644 bigframes/core/compile/sqlglot/expressions/binary_compiler.py create mode 100644 bigframes/core/compile/sqlglot/expressions/nary_compiler.py create mode 100644 bigframes/core/compile/sqlglot/expressions/op_registration.py create mode 100644 bigframes/core/compile/sqlglot/expressions/ternary_compiler.py create mode 100644 bigframes/core/compile/sqlglot/expressions/typed_expr.py create mode 100644 bigframes/core/compile/sqlglot/expressions/unary_compiler.py diff --git a/bigframes/core/compile/sqlglot/expressions/__init__.py b/bigframes/core/compile/sqlglot/expressions/__init__.py new file mode 100644 index 0000000000..0a2669d7a2 --- /dev/null +++ b/bigframes/core/compile/sqlglot/expressions/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/bigframes/core/compile/sqlglot/expressions/binary_compiler.py b/bigframes/core/compile/sqlglot/expressions/binary_compiler.py new file mode 100644 index 0000000000..447b5af860 --- /dev/null +++ b/bigframes/core/compile/sqlglot/expressions/binary_compiler.py @@ -0,0 +1,49 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import annotations + +import typing + +import sqlglot.expressions as sge + +from bigframes import dtypes +from bigframes import operations as ops +from bigframes.core.compile.sqlglot.expressions.op_registration import OpRegistration +from bigframes.core.compile.sqlglot.expressions.typed_expr import TypedExpr + +BinaryOpCompiler = typing.Callable[[ops.BinaryOp, TypedExpr, TypedExpr], sge.Expression] + +BINARY_OP_REIGSTRATION = OpRegistration[BinaryOpCompiler]() + + +def compile(op: ops.BinaryOp, left: TypedExpr, right: TypedExpr) -> sge.Expression: + return BINARY_OP_REIGSTRATION[op](op, left, right) + + +# TODO: add parenthesize for operators +@BINARY_OP_REIGSTRATION.register(ops.add_op) +def _(op, left: TypedExpr, right: TypedExpr) -> sge.Expression: + if left.dtype == dtypes.STRING_DTYPE and right.dtype == dtypes.STRING_DTYPE: + # String addition + return sge.Concat(expressions=[left.expr, right.expr]) + + # Numerical addition + return sge.Add(this=left.expr, expression=right.expr) + + +@BINARY_OP_REIGSTRATION.register(ops.ge_op) +def compile_ge(op, left: TypedExpr, right: TypedExpr) -> sge.Expression: + + return sge.GTE(this=left.expr, expression=right.expr) diff --git a/bigframes/core/compile/sqlglot/expressions/nary_compiler.py b/bigframes/core/compile/sqlglot/expressions/nary_compiler.py new file mode 100644 index 0000000000..d470009c2c --- /dev/null +++ b/bigframes/core/compile/sqlglot/expressions/nary_compiler.py @@ -0,0 +1,32 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import annotations + +import typing + +import sqlglot.expressions as sge + +from bigframes import operations as ops +from bigframes.core.compile.sqlglot.expressions.op_registration import OpRegistration +from bigframes.core.compile.sqlglot.expressions.typed_expr import TypedExpr + +# No simpler way to specify that the compilation function expects varargs. +NaryOpCompiler = typing.Callable[..., sge.Expression] + +NARY_OP_REIGSTRATION = OpRegistration[NaryOpCompiler]() + + +def compile(op: ops.NaryOp, *args: TypedExpr) -> sge.Expression: + return NARY_OP_REIGSTRATION[op](op, *args) diff --git a/bigframes/core/compile/sqlglot/expressions/op_registration.py b/bigframes/core/compile/sqlglot/expressions/op_registration.py new file mode 100644 index 0000000000..c2290e7c93 --- /dev/null +++ b/bigframes/core/compile/sqlglot/expressions/op_registration.py @@ -0,0 +1,44 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import annotations + +import typing +from typing import Generic, TypeVar + +from bigframes import operations as ops + +T = TypeVar("T") + + +class OpRegistration(Generic[T]): + _registered_ops: dict[str, T] = {} + + def register( + self, op: ops.ScalarOp | type[ops.ScalarOp] + ) -> typing.Callable[[T], T]: + key = typing.cast(str, op.name) + + def decorator(item: T): + if key in self._registered_ops: + raise ValueError(f"{key} is already registered") + self._registered_ops[key] = item + return item + + return decorator + + def __getitem__(self, key: str | ops.ScalarOp) -> T: + if isinstance(key, ops.ScalarOp): + return self._registered_ops[key.name] + return self._registered_ops[key] diff --git a/bigframes/core/compile/sqlglot/expressions/ternary_compiler.py b/bigframes/core/compile/sqlglot/expressions/ternary_compiler.py new file mode 100644 index 0000000000..ee33b48c17 --- /dev/null +++ b/bigframes/core/compile/sqlglot/expressions/ternary_compiler.py @@ -0,0 +1,35 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import annotations + +import typing + +import sqlglot.expressions as sge + +from bigframes import operations as ops +from bigframes.core.compile.sqlglot.expressions.op_registration import OpRegistration +from bigframes.core.compile.sqlglot.expressions.typed_expr import TypedExpr + +TernaryOpCompiler = typing.Callable[ + [ops.TernaryOp, TypedExpr, TypedExpr, TypedExpr], sge.Expression +] + +TERNATRY_OP_REIGSTRATION = OpRegistration[TernaryOpCompiler]() + + +def compile( + op: ops.TernaryOp, expr1: TypedExpr, expr2: TypedExpr, expr3: TypedExpr +) -> sge.Expression: + return TERNATRY_OP_REIGSTRATION[op](op, expr1, expr2, expr3) diff --git a/bigframes/core/compile/sqlglot/expressions/typed_expr.py b/bigframes/core/compile/sqlglot/expressions/typed_expr.py new file mode 100644 index 0000000000..e693dd94a2 --- /dev/null +++ b/bigframes/core/compile/sqlglot/expressions/typed_expr.py @@ -0,0 +1,27 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import dataclasses + +import sqlglot.expressions as sge + +from bigframes import dtypes + + +@dataclasses.dataclass(frozen=True) +class TypedExpr: + """SQLGlot expression with type.""" + + expr: sge.Expression + dtype: dtypes.ExpressionType diff --git a/bigframes/core/compile/sqlglot/expressions/unary_compiler.py b/bigframes/core/compile/sqlglot/expressions/unary_compiler.py new file mode 100644 index 0000000000..56951a58dd --- /dev/null +++ b/bigframes/core/compile/sqlglot/expressions/unary_compiler.py @@ -0,0 +1,31 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import annotations + +import typing + +import sqlglot.expressions as sge + +from bigframes import operations as ops +from bigframes.core.compile.sqlglot.expressions.op_registration import OpRegistration +from bigframes.core.compile.sqlglot.expressions.typed_expr import TypedExpr + +UnaryOpCompiler = typing.Callable[[ops.UnaryOp, TypedExpr], sge.Expression] + +UNARY_OP_REIGSTRATION = OpRegistration[UnaryOpCompiler]() + + +def compile(op: ops.UnaryOp, expr: TypedExpr) -> sge.Expression: + return UNARY_OP_REIGSTRATION[op](op, expr) diff --git a/bigframes/core/compile/sqlglot/scalar_compiler.py b/bigframes/core/compile/sqlglot/scalar_compiler.py index 59deb9c8f3..f553518300 100644 --- a/bigframes/core/compile/sqlglot/scalar_compiler.py +++ b/bigframes/core/compile/sqlglot/scalar_compiler.py @@ -13,25 +13,22 @@ # limitations under the License. from __future__ import annotations -import dataclasses import functools import sqlglot.expressions as sge -from bigframes import dtypes from bigframes.core import expression +from bigframes.core.compile.sqlglot.expressions import ( + binary_compiler, + nary_compiler, + ternary_compiler, + typed_expr, + unary_compiler, +) import bigframes.core.compile.sqlglot.sqlglot_ir as ir import bigframes.operations as ops -@dataclasses.dataclass(frozen=True) -class TypedExpr: - """SQLGlot expression with type.""" - - expr: sge.Expression - dtype: dtypes.ExpressionType - - @functools.singledispatch def compile_scalar_expression( expression: expression.Expression, @@ -63,46 +60,21 @@ def compile_constant_expression( def compile_op_expression(expr: expression.OpExpression) -> sge.Expression: # Non-recursively compiles the children scalar expressions. args = tuple( - TypedExpr(compile_scalar_expression(input), input.output_type) + typed_expr.TypedExpr(compile_scalar_expression(input), input.output_type) for input in expr.inputs ) op = expr.op - op_name = expr.op.__class__.__name__ - method_name = f"compile_{op_name.lower()}" - method = globals().get(method_name, None) - if method is None: - raise ValueError( - f"Compilation method '{method_name}' not found for operator '{op_name}'." - ) - if isinstance(op, ops.UnaryOp): - return method(op, args[0]) + return unary_compiler.compile(op, args[0]) elif isinstance(op, ops.BinaryOp): - return method(op, args[0], args[1]) + return binary_compiler.compile(op, args[0], args[1]) elif isinstance(op, ops.TernaryOp): - return method(op, args[0], args[1], args[2]) + return ternary_compiler.compile(op, args[0], args[1], args[2]) elif isinstance(op, ops.NaryOp): - return method(op, *args) + return nary_compiler.compile(op, *args) else: raise TypeError( - f"Operator '{op_name}' has an unrecognized arity or type " + f"Operator '{op.name}' has an unrecognized arity or type " "and cannot be compiled." ) - - -# TODO: add parenthesize for operators -def compile_addop(op: ops.AddOp, left: TypedExpr, right: TypedExpr) -> sge.Expression: - if left.dtype == dtypes.STRING_DTYPE and right.dtype == dtypes.STRING_DTYPE: - # String addition - return sge.Concat(expressions=[left.expr, right.expr]) - - # Numerical addition - return sge.Add(this=left.expr, expression=right.expr) - - -def compile_ge( - op: ops.ge_op, left: TypedExpr, right: TypedExpr # type: ignore[valid-type] -) -> sge.Expression: - - return sge.GTE(this=left.expr, expression=right.expr) From bc885bd1ea7987c60d3a73c18329461a40adb9f0 Mon Sep 17 00:00:00 2001 From: Chelsea Lin Date: Tue, 24 Jun 2025 17:10:32 -0700 Subject: [PATCH 09/28] chore: add compile_explode (#1848) Fixes internal issue 427306238 --- bigframes/core/compile/sqlglot/compiler.py | 8 ++ bigframes/core/compile/sqlglot/sqlglot_ir.py | 90 +++++++++++++++++++ .../test_compile_explode_dataframe/out.sql | 21 +++++ .../test_compile_explode_series/out.sql | 18 ++++ .../compile/sqlglot/test_compile_explode.py | 31 +++++++ .../bigframes_vendored/pandas/core/frame.py | 1 + 6 files changed, 169 insertions(+) create mode 100644 tests/unit/core/compile/sqlglot/snapshots/test_compile_explode/test_compile_explode_dataframe/out.sql create mode 100644 tests/unit/core/compile/sqlglot/snapshots/test_compile_explode/test_compile_explode_series/out.sql create mode 100644 tests/unit/core/compile/sqlglot/test_compile_explode.py diff --git a/bigframes/core/compile/sqlglot/compiler.py b/bigframes/core/compile/sqlglot/compiler.py index a38078b687..606fe41b5e 100644 --- a/bigframes/core/compile/sqlglot/compiler.py +++ b/bigframes/core/compile/sqlglot/compiler.py @@ -229,6 +229,14 @@ def compile_concat( uid_gen=self.uid_gen, ) + @_compile_node.register + def compile_explode( + self, node: nodes.ExplodeNode, child: ir.SQLGlotIR + ) -> ir.SQLGlotIR: + offsets_col = node.offsets_col.sql if (node.offsets_col is not None) else None + columns = tuple(ref.id.sql for ref in node.column_ids) + return child.explode(columns, offsets_col) + def _replace_unsupported_ops(node: nodes.BigFrameNode): node = nodes.bottom_up(node, rewrite.rewrite_slice) diff --git a/bigframes/core/compile/sqlglot/sqlglot_ir.py b/bigframes/core/compile/sqlglot/sqlglot_ir.py index 6b805802b0..6bc2b55162 100644 --- a/bigframes/core/compile/sqlglot/sqlglot_ir.py +++ b/bigframes/core/compile/sqlglot/sqlglot_ir.py @@ -290,6 +290,96 @@ def replace( ).sql(dialect=self.dialect, pretty=self.pretty) return f"{merge_str}\n{whens_str}" + def explode( + self, + column_names: tuple[str, ...], + offsets_col: typing.Optional[str], + ) -> SQLGlotIR: + num_columns = len(list(column_names)) + assert num_columns > 0, "At least one column must be provided for explode." + if num_columns == 1: + return self._explode_single_column(column_names[0], offsets_col) + else: + return self._explode_multiple_columns(column_names, offsets_col) + + def _explode_single_column( + self, column_name: str, offsets_col: typing.Optional[str] + ) -> SQLGlotIR: + """Helper method to handle the case of exploding a single column.""" + + offset = ( + sge.to_identifier(offsets_col, quoted=self.quoted) if offsets_col else None + ) + column = sge.to_identifier(column_name, quoted=self.quoted) + unnested_column_alias = sge.to_identifier( + next(self.uid_gen.get_uid_stream("bfcol_")), quoted=self.quoted + ) + unnest_expr = sge.Unnest( + expressions=[column], + alias=sge.TableAlias(columns=[unnested_column_alias]), + offset=offset, + ) + selection = sge.Star(replace=[unnested_column_alias.as_(column)]) + # TODO: "CROSS" if not keep_empty else "LEFT" + # TODO: overlaps_with_parent to replace existing column. + new_expr = ( + self._encapsulate_as_cte() + .select(selection, append=False) + .join(unnest_expr, join_type="CROSS") + ) + return SQLGlotIR(expr=new_expr, uid_gen=self.uid_gen) + + def _explode_multiple_columns( + self, + column_names: tuple[str, ...], + offsets_col: typing.Optional[str], + ) -> SQLGlotIR: + """Helper method to handle the case of exploding multiple columns.""" + offset = ( + sge.to_identifier(offsets_col, quoted=self.quoted) if offsets_col else None + ) + columns = [ + sge.to_identifier(column_name, quoted=self.quoted) + for column_name in column_names + ] + + # If there are multiple columns, we need to unnest by zipping the arrays: + # https://cloud.google.com/bigquery/docs/arrays#zipping_arrays + column_lengths = [ + sge.func("ARRAY_LENGTH", sge.to_identifier(column, quoted=self.quoted)) - 1 + for column in columns + ] + generate_array = sge.func( + "GENERATE_ARRAY", + sge.convert(0), + sge.func("LEAST", *column_lengths), + ) + unnested_offset_alias = sge.to_identifier( + next(self.uid_gen.get_uid_stream("bfcol_")), quoted=self.quoted + ) + unnest_expr = sge.Unnest( + expressions=[generate_array], + alias=sge.TableAlias(columns=[unnested_offset_alias]), + offset=offset, + ) + selection = sge.Star( + replace=[ + sge.Bracket( + this=column, + expressions=[unnested_offset_alias], + safe=True, + offset=False, + ).as_(column) + for column in columns + ] + ) + new_expr = ( + self._encapsulate_as_cte() + .select(selection, append=False) + .join(unnest_expr, join_type="CROSS") + ) + return SQLGlotIR(expr=new_expr, uid_gen=self.uid_gen) + def _encapsulate_as_cte( self, ) -> sge.Select: diff --git a/tests/unit/core/compile/sqlglot/snapshots/test_compile_explode/test_compile_explode_dataframe/out.sql b/tests/unit/core/compile/sqlglot/snapshots/test_compile_explode/test_compile_explode_dataframe/out.sql new file mode 100644 index 0000000000..679da58f44 --- /dev/null +++ b/tests/unit/core/compile/sqlglot/snapshots/test_compile_explode/test_compile_explode_dataframe/out.sql @@ -0,0 +1,21 @@ +WITH `bfcte_0` AS ( + SELECT + `rowindex` AS `bfcol_0`, + `int_list_col` AS `bfcol_1`, + `string_list_col` AS `bfcol_2` + FROM `bigframes-dev`.`sqlglot_test`.`repeated_types` +), `bfcte_1` AS ( + SELECT + * + REPLACE (`bfcol_1`[SAFE_OFFSET(`bfcol_13`)] AS `bfcol_1`, `bfcol_2`[SAFE_OFFSET(`bfcol_13`)] AS `bfcol_2`) + FROM `bfcte_0` + CROSS JOIN UNNEST(GENERATE_ARRAY(0, LEAST(ARRAY_LENGTH(`bfcol_1`) - 1, ARRAY_LENGTH(`bfcol_2`) - 1))) AS `bfcol_13` WITH OFFSET AS `bfcol_7` +) +SELECT + `bfcol_0` AS `rowindex`, + `bfcol_0` AS `rowindex_1`, + `bfcol_1` AS `int_list_col`, + `bfcol_2` AS `string_list_col` +FROM `bfcte_1` +ORDER BY + `bfcol_7` ASC NULLS LAST \ No newline at end of file diff --git a/tests/unit/core/compile/sqlglot/snapshots/test_compile_explode/test_compile_explode_series/out.sql b/tests/unit/core/compile/sqlglot/snapshots/test_compile_explode/test_compile_explode_series/out.sql new file mode 100644 index 0000000000..8bfd1eb005 --- /dev/null +++ b/tests/unit/core/compile/sqlglot/snapshots/test_compile_explode/test_compile_explode_series/out.sql @@ -0,0 +1,18 @@ +WITH `bfcte_0` AS ( + SELECT + `rowindex` AS `bfcol_0`, + `int_list_col` AS `bfcol_1` + FROM `bigframes-dev`.`sqlglot_test`.`repeated_types` +), `bfcte_1` AS ( + SELECT + * + REPLACE (`bfcol_8` AS `bfcol_1`) + FROM `bfcte_0` + CROSS JOIN UNNEST(`bfcol_1`) AS `bfcol_8` WITH OFFSET AS `bfcol_4` +) +SELECT + `bfcol_0` AS `rowindex`, + `bfcol_1` AS `int_list_col` +FROM `bfcte_1` +ORDER BY + `bfcol_4` ASC NULLS LAST \ No newline at end of file diff --git a/tests/unit/core/compile/sqlglot/test_compile_explode.py b/tests/unit/core/compile/sqlglot/test_compile_explode.py new file mode 100644 index 0000000000..34adbbd23a --- /dev/null +++ b/tests/unit/core/compile/sqlglot/test_compile_explode.py @@ -0,0 +1,31 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import pytest + +import bigframes.pandas as bpd + +pytest.importorskip("pytest_snapshot") + + +# TODO: check order by with offset +def test_compile_explode_series(repeated_types_df: bpd.DataFrame, snapshot): + s = repeated_types_df["int_list_col"].explode() + snapshot.assert_match(s.to_frame().sql, "out.sql") + + +def test_compile_explode_dataframe(repeated_types_df: bpd.DataFrame, snapshot): + exploded_columns = ["int_list_col", "string_list_col"] + df = repeated_types_df[["rowindex", *exploded_columns]].explode(exploded_columns) + snapshot.assert_match(df.sql, "out.sql") diff --git a/third_party/bigframes_vendored/pandas/core/frame.py b/third_party/bigframes_vendored/pandas/core/frame.py index 224fe25f16..0606032d34 100644 --- a/third_party/bigframes_vendored/pandas/core/frame.py +++ b/third_party/bigframes_vendored/pandas/core/frame.py @@ -4124,6 +4124,7 @@ def explode( **Examples:** >>> import bigframes.pandas as bpd + >>> import numpy as np >>> bpd.options.display.progress_bar = None >>> df = bpd.DataFrame({'A': [[0, 1, 2], [], [], [3, 4]], From c88a825342dfd6ec5a8a141b79c7da3a3b1dea83 Mon Sep 17 00:00:00 2001 From: Shenyang Cai Date: Wed, 25 Jun 2025 11:56:38 -0700 Subject: [PATCH 10/28] chore: add array operators to SQLGlot compiler (#1852) * [WIP] Add array operators. Need to finish tests * add tests * fix lint * fix typos * Use sge.Bracket() for safe_offset --- .../sqlglot/expressions/binary_compiler.py | 15 ++---- .../sqlglot/expressions/nary_compiler.py | 9 +--- .../sqlglot/expressions/op_registration.py | 30 ++++++++---- .../sqlglot/expressions/ternary_compiler.py | 10 +--- .../sqlglot/expressions/unary_compiler.py | 49 +++++++++++++++++-- .../compile/sqlglot/expressions/__init__.py | 13 +++++ .../test_add_numeric}/out.sql | 0 .../test_add_numeric_w_scalar}/out.sql | 0 .../test_add_string}/out.sql | 0 .../test_array_index/out.sql | 15 ++++++ .../test_array_slice_with_only_start/out.sql | 21 ++++++++ .../out.sql | 21 ++++++++ .../test_array_to_string/out.sql | 15 ++++++ .../out.sql | 16 ++++++ .../test_compile_string_add/out.sql | 16 ++++++ .../test_binary_compiler.py} | 12 +++-- .../expressions/test_op_registration.py | 43 ++++++++++++++++ .../expressions/test_unary_compiler.py | 44 +++++++++++++++++ 18 files changed, 287 insertions(+), 42 deletions(-) create mode 100644 tests/unit/core/compile/sqlglot/expressions/__init__.py rename tests/unit/core/compile/sqlglot/{snapshots/test_compile_scalar_expr/test_compile_numerical_add => expressions/snapshots/test_binary_compiler/test_add_numeric}/out.sql (100%) rename tests/unit/core/compile/sqlglot/{snapshots/test_compile_scalar_expr/test_compile_numerical_add_w_scalar => expressions/snapshots/test_binary_compiler/test_add_numeric_w_scalar}/out.sql (100%) rename tests/unit/core/compile/sqlglot/{snapshots/test_compile_scalar_expr/test_compile_string_add => expressions/snapshots/test_binary_compiler/test_add_string}/out.sql (100%) create mode 100644 tests/unit/core/compile/sqlglot/expressions/snapshots/test_unary_compiler/test_array_index/out.sql create mode 100644 tests/unit/core/compile/sqlglot/expressions/snapshots/test_unary_compiler/test_array_slice_with_only_start/out.sql create mode 100644 tests/unit/core/compile/sqlglot/expressions/snapshots/test_unary_compiler/test_array_slice_with_start_and_stop/out.sql create mode 100644 tests/unit/core/compile/sqlglot/expressions/snapshots/test_unary_compiler/test_array_to_string/out.sql create mode 100644 tests/unit/core/compile/sqlglot/expressions/snapshots/test_unary_compiler/test_compile_numerical_add_w_scalar/out.sql create mode 100644 tests/unit/core/compile/sqlglot/expressions/snapshots/test_unary_compiler/test_compile_string_add/out.sql rename tests/unit/core/compile/sqlglot/{test_compile_scalar_expr.py => expressions/test_binary_compiler.py} (82%) create mode 100644 tests/unit/core/compile/sqlglot/expressions/test_op_registration.py create mode 100644 tests/unit/core/compile/sqlglot/expressions/test_unary_compiler.py diff --git a/bigframes/core/compile/sqlglot/expressions/binary_compiler.py b/bigframes/core/compile/sqlglot/expressions/binary_compiler.py index 447b5af860..ec75d3a3a4 100644 --- a/bigframes/core/compile/sqlglot/expressions/binary_compiler.py +++ b/bigframes/core/compile/sqlglot/expressions/binary_compiler.py @@ -14,8 +14,6 @@ from __future__ import annotations -import typing - import sqlglot.expressions as sge from bigframes import dtypes @@ -23,17 +21,15 @@ from bigframes.core.compile.sqlglot.expressions.op_registration import OpRegistration from bigframes.core.compile.sqlglot.expressions.typed_expr import TypedExpr -BinaryOpCompiler = typing.Callable[[ops.BinaryOp, TypedExpr, TypedExpr], sge.Expression] - -BINARY_OP_REIGSTRATION = OpRegistration[BinaryOpCompiler]() +BINARY_OP_REGISTRATION = OpRegistration() def compile(op: ops.BinaryOp, left: TypedExpr, right: TypedExpr) -> sge.Expression: - return BINARY_OP_REIGSTRATION[op](op, left, right) + return BINARY_OP_REGISTRATION[op](op, left, right) # TODO: add parenthesize for operators -@BINARY_OP_REIGSTRATION.register(ops.add_op) +@BINARY_OP_REGISTRATION.register(ops.add_op) def _(op, left: TypedExpr, right: TypedExpr) -> sge.Expression: if left.dtype == dtypes.STRING_DTYPE and right.dtype == dtypes.STRING_DTYPE: # String addition @@ -43,7 +39,6 @@ def _(op, left: TypedExpr, right: TypedExpr) -> sge.Expression: return sge.Add(this=left.expr, expression=right.expr) -@BINARY_OP_REIGSTRATION.register(ops.ge_op) -def compile_ge(op, left: TypedExpr, right: TypedExpr) -> sge.Expression: - +@BINARY_OP_REGISTRATION.register(ops.ge_op) +def _(op, left: TypedExpr, right: TypedExpr) -> sge.Expression: return sge.GTE(this=left.expr, expression=right.expr) diff --git a/bigframes/core/compile/sqlglot/expressions/nary_compiler.py b/bigframes/core/compile/sqlglot/expressions/nary_compiler.py index d470009c2c..12f68613d7 100644 --- a/bigframes/core/compile/sqlglot/expressions/nary_compiler.py +++ b/bigframes/core/compile/sqlglot/expressions/nary_compiler.py @@ -14,19 +14,14 @@ from __future__ import annotations -import typing - import sqlglot.expressions as sge from bigframes import operations as ops from bigframes.core.compile.sqlglot.expressions.op_registration import OpRegistration from bigframes.core.compile.sqlglot.expressions.typed_expr import TypedExpr -# No simpler way to specify that the compilation function expects varargs. -NaryOpCompiler = typing.Callable[..., sge.Expression] - -NARY_OP_REIGSTRATION = OpRegistration[NaryOpCompiler]() +NARY_OP_REGISTRATION = OpRegistration() def compile(op: ops.NaryOp, *args: TypedExpr) -> sge.Expression: - return NARY_OP_REIGSTRATION[op](op, *args) + return NARY_OP_REGISTRATION[op](op, *args) diff --git a/bigframes/core/compile/sqlglot/expressions/op_registration.py b/bigframes/core/compile/sqlglot/expressions/op_registration.py index c2290e7c93..e30b58a6d2 100644 --- a/bigframes/core/compile/sqlglot/expressions/op_registration.py +++ b/bigframes/core/compile/sqlglot/expressions/op_registration.py @@ -15,30 +15,40 @@ from __future__ import annotations import typing -from typing import Generic, TypeVar + +from sqlglot import expressions as sge from bigframes import operations as ops -T = TypeVar("T") +# We should've been more specific about input types. Unfortunately, +# MyPy doesn't support more rigorous checks. +CompilationFunc = typing.Callable[..., sge.Expression] -class OpRegistration(Generic[T]): - _registered_ops: dict[str, T] = {} +class OpRegistration: + def __init__(self) -> None: + self._registered_ops: dict[str, CompilationFunc] = {} def register( self, op: ops.ScalarOp | type[ops.ScalarOp] - ) -> typing.Callable[[T], T]: - key = typing.cast(str, op.name) - - def decorator(item: T): + ) -> typing.Callable[[CompilationFunc], CompilationFunc]: + def decorator(item: CompilationFunc): + def arg_checker(*args, **kwargs): + if not isinstance(args[0], ops.ScalarOp): + raise ValueError( + f"The first parameter must be an operator. Got {type(args[0])}" + ) + return item(*args, **kwargs) + + key = typing.cast(str, op.name) if key in self._registered_ops: raise ValueError(f"{key} is already registered") self._registered_ops[key] = item - return item + return arg_checker return decorator - def __getitem__(self, key: str | ops.ScalarOp) -> T: + def __getitem__(self, key: str | ops.ScalarOp) -> CompilationFunc: if isinstance(key, ops.ScalarOp): return self._registered_ops[key.name] return self._registered_ops[key] diff --git a/bigframes/core/compile/sqlglot/expressions/ternary_compiler.py b/bigframes/core/compile/sqlglot/expressions/ternary_compiler.py index ee33b48c17..9b00771f7d 100644 --- a/bigframes/core/compile/sqlglot/expressions/ternary_compiler.py +++ b/bigframes/core/compile/sqlglot/expressions/ternary_compiler.py @@ -14,22 +14,16 @@ from __future__ import annotations -import typing - import sqlglot.expressions as sge from bigframes import operations as ops from bigframes.core.compile.sqlglot.expressions.op_registration import OpRegistration from bigframes.core.compile.sqlglot.expressions.typed_expr import TypedExpr -TernaryOpCompiler = typing.Callable[ - [ops.TernaryOp, TypedExpr, TypedExpr, TypedExpr], sge.Expression -] - -TERNATRY_OP_REIGSTRATION = OpRegistration[TernaryOpCompiler]() +TERNATRY_OP_REGISTRATION = OpRegistration() def compile( op: ops.TernaryOp, expr1: TypedExpr, expr2: TypedExpr, expr3: TypedExpr ) -> sge.Expression: - return TERNATRY_OP_REIGSTRATION[op](op, expr1, expr2, expr3) + return TERNATRY_OP_REGISTRATION[op](op, expr1, expr2, expr3) diff --git a/bigframes/core/compile/sqlglot/expressions/unary_compiler.py b/bigframes/core/compile/sqlglot/expressions/unary_compiler.py index 56951a58dd..716917b455 100644 --- a/bigframes/core/compile/sqlglot/expressions/unary_compiler.py +++ b/bigframes/core/compile/sqlglot/expressions/unary_compiler.py @@ -16,16 +16,57 @@ import typing +import sqlglot import sqlglot.expressions as sge from bigframes import operations as ops from bigframes.core.compile.sqlglot.expressions.op_registration import OpRegistration from bigframes.core.compile.sqlglot.expressions.typed_expr import TypedExpr -UnaryOpCompiler = typing.Callable[[ops.UnaryOp, TypedExpr], sge.Expression] - -UNARY_OP_REIGSTRATION = OpRegistration[UnaryOpCompiler]() +UNARY_OP_REGISTRATION = OpRegistration() def compile(op: ops.UnaryOp, expr: TypedExpr) -> sge.Expression: - return UNARY_OP_REIGSTRATION[op](op, expr) + return UNARY_OP_REGISTRATION[op](op, expr) + + +@UNARY_OP_REGISTRATION.register(ops.ArrayToStringOp) +def _(op: ops.ArrayToStringOp, expr: TypedExpr) -> sge.Expression: + return sge.ArrayToString(this=expr.expr, expression=f"'{op.delimiter}'") + + +@UNARY_OP_REGISTRATION.register(ops.ArrayIndexOp) +def _(op: ops.ArrayIndexOp, expr: TypedExpr) -> sge.Expression: + return sge.Bracket( + this=expr.expr, + expressions=[sge.Literal.number(op.index)], + safe=True, + offset=False, + ) + + +@UNARY_OP_REGISTRATION.register(ops.ArraySliceOp) +def _(op: ops.ArraySliceOp, expr: TypedExpr) -> sge.Expression: + slice_idx = sqlglot.to_identifier("slice_idx") + + conditions: typing.List[sge.Predicate] = [slice_idx >= op.start] + + if op.stop is not None: + conditions.append(slice_idx < op.stop) + + # local name for each element in the array + el = sqlglot.to_identifier("el") + + selected_elements = ( + sge.select(el) + .from_( + sge.Unnest( + expressions=[expr.expr], + alias=sge.TableAlias(columns=[el]), + offset=slice_idx, + ) + ) + .where(*conditions) + ) + + return sge.array(selected_elements) diff --git a/tests/unit/core/compile/sqlglot/expressions/__init__.py b/tests/unit/core/compile/sqlglot/expressions/__init__.py new file mode 100644 index 0000000000..0a2669d7a2 --- /dev/null +++ b/tests/unit/core/compile/sqlglot/expressions/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/tests/unit/core/compile/sqlglot/snapshots/test_compile_scalar_expr/test_compile_numerical_add/out.sql b/tests/unit/core/compile/sqlglot/expressions/snapshots/test_binary_compiler/test_add_numeric/out.sql similarity index 100% rename from tests/unit/core/compile/sqlglot/snapshots/test_compile_scalar_expr/test_compile_numerical_add/out.sql rename to tests/unit/core/compile/sqlglot/expressions/snapshots/test_binary_compiler/test_add_numeric/out.sql diff --git a/tests/unit/core/compile/sqlglot/snapshots/test_compile_scalar_expr/test_compile_numerical_add_w_scalar/out.sql b/tests/unit/core/compile/sqlglot/expressions/snapshots/test_binary_compiler/test_add_numeric_w_scalar/out.sql similarity index 100% rename from tests/unit/core/compile/sqlglot/snapshots/test_compile_scalar_expr/test_compile_numerical_add_w_scalar/out.sql rename to tests/unit/core/compile/sqlglot/expressions/snapshots/test_binary_compiler/test_add_numeric_w_scalar/out.sql diff --git a/tests/unit/core/compile/sqlglot/snapshots/test_compile_scalar_expr/test_compile_string_add/out.sql b/tests/unit/core/compile/sqlglot/expressions/snapshots/test_binary_compiler/test_add_string/out.sql similarity index 100% rename from tests/unit/core/compile/sqlglot/snapshots/test_compile_scalar_expr/test_compile_string_add/out.sql rename to tests/unit/core/compile/sqlglot/expressions/snapshots/test_binary_compiler/test_add_string/out.sql diff --git a/tests/unit/core/compile/sqlglot/expressions/snapshots/test_unary_compiler/test_array_index/out.sql b/tests/unit/core/compile/sqlglot/expressions/snapshots/test_unary_compiler/test_array_index/out.sql new file mode 100644 index 0000000000..33a8bded13 --- /dev/null +++ b/tests/unit/core/compile/sqlglot/expressions/snapshots/test_unary_compiler/test_array_index/out.sql @@ -0,0 +1,15 @@ +WITH `bfcte_0` AS ( + SELECT + `rowindex` AS `bfcol_0`, + `string_list_col` AS `bfcol_1` + FROM `bigframes-dev`.`sqlglot_test`.`repeated_types` +), `bfcte_1` AS ( + SELECT + *, + `bfcol_1`[SAFE_OFFSET(1)] AS `bfcol_4` + FROM `bfcte_0` +) +SELECT + `bfcol_0` AS `rowindex`, + `bfcol_4` AS `string_list_col` +FROM `bfcte_1` \ No newline at end of file diff --git a/tests/unit/core/compile/sqlglot/expressions/snapshots/test_unary_compiler/test_array_slice_with_only_start/out.sql b/tests/unit/core/compile/sqlglot/expressions/snapshots/test_unary_compiler/test_array_slice_with_only_start/out.sql new file mode 100644 index 0000000000..34d2225931 --- /dev/null +++ b/tests/unit/core/compile/sqlglot/expressions/snapshots/test_unary_compiler/test_array_slice_with_only_start/out.sql @@ -0,0 +1,21 @@ +WITH `bfcte_0` AS ( + SELECT + `rowindex` AS `bfcol_0`, + `string_list_col` AS `bfcol_1` + FROM `bigframes-dev`.`sqlglot_test`.`repeated_types` +), `bfcte_1` AS ( + SELECT + *, + ARRAY( + SELECT + el + FROM UNNEST(`bfcol_1`) AS el WITH OFFSET AS slice_idx + WHERE + slice_idx >= 1 + ) AS `bfcol_4` + FROM `bfcte_0` +) +SELECT + `bfcol_0` AS `rowindex`, + `bfcol_4` AS `string_list_col` +FROM `bfcte_1` \ No newline at end of file diff --git a/tests/unit/core/compile/sqlglot/expressions/snapshots/test_unary_compiler/test_array_slice_with_start_and_stop/out.sql b/tests/unit/core/compile/sqlglot/expressions/snapshots/test_unary_compiler/test_array_slice_with_start_and_stop/out.sql new file mode 100644 index 0000000000..d46803ce7c --- /dev/null +++ b/tests/unit/core/compile/sqlglot/expressions/snapshots/test_unary_compiler/test_array_slice_with_start_and_stop/out.sql @@ -0,0 +1,21 @@ +WITH `bfcte_0` AS ( + SELECT + `rowindex` AS `bfcol_0`, + `string_list_col` AS `bfcol_1` + FROM `bigframes-dev`.`sqlglot_test`.`repeated_types` +), `bfcte_1` AS ( + SELECT + *, + ARRAY( + SELECT + el + FROM UNNEST(`bfcol_1`) AS el WITH OFFSET AS slice_idx + WHERE + slice_idx >= 1 AND slice_idx < 5 + ) AS `bfcol_4` + FROM `bfcte_0` +) +SELECT + `bfcol_0` AS `rowindex`, + `bfcol_4` AS `string_list_col` +FROM `bfcte_1` \ No newline at end of file diff --git a/tests/unit/core/compile/sqlglot/expressions/snapshots/test_unary_compiler/test_array_to_string/out.sql b/tests/unit/core/compile/sqlglot/expressions/snapshots/test_unary_compiler/test_array_to_string/out.sql new file mode 100644 index 0000000000..e0db21f972 --- /dev/null +++ b/tests/unit/core/compile/sqlglot/expressions/snapshots/test_unary_compiler/test_array_to_string/out.sql @@ -0,0 +1,15 @@ +WITH `bfcte_0` AS ( + SELECT + `rowindex` AS `bfcol_0`, + `string_list_col` AS `bfcol_1` + FROM `bigframes-dev`.`sqlglot_test`.`repeated_types` +), `bfcte_1` AS ( + SELECT + *, + ARRAY_TO_STRING(`bfcol_1`, '.') AS `bfcol_4` + FROM `bfcte_0` +) +SELECT + `bfcol_0` AS `rowindex`, + `bfcol_4` AS `string_list_col` +FROM `bfcte_1` \ No newline at end of file diff --git a/tests/unit/core/compile/sqlglot/expressions/snapshots/test_unary_compiler/test_compile_numerical_add_w_scalar/out.sql b/tests/unit/core/compile/sqlglot/expressions/snapshots/test_unary_compiler/test_compile_numerical_add_w_scalar/out.sql new file mode 100644 index 0000000000..9c4b01a6df --- /dev/null +++ b/tests/unit/core/compile/sqlglot/expressions/snapshots/test_unary_compiler/test_compile_numerical_add_w_scalar/out.sql @@ -0,0 +1,16 @@ +WITH `bfcte_0` AS ( + SELECT + `int64_col` AS `bfcol_0`, + `rowindex` AS `bfcol_1` + FROM `bigframes-dev`.`sqlglot_test`.`scalar_types` +), `bfcte_1` AS ( + SELECT + *, + `bfcol_1` AS `bfcol_4`, + `bfcol_0` + 1 AS `bfcol_5` + FROM `bfcte_0` +) +SELECT + `bfcol_4` AS `rowindex`, + `bfcol_5` AS `int64_col` +FROM `bfcte_1` \ No newline at end of file diff --git a/tests/unit/core/compile/sqlglot/expressions/snapshots/test_unary_compiler/test_compile_string_add/out.sql b/tests/unit/core/compile/sqlglot/expressions/snapshots/test_unary_compiler/test_compile_string_add/out.sql new file mode 100644 index 0000000000..7a8ab83df1 --- /dev/null +++ b/tests/unit/core/compile/sqlglot/expressions/snapshots/test_unary_compiler/test_compile_string_add/out.sql @@ -0,0 +1,16 @@ +WITH `bfcte_0` AS ( + SELECT + `rowindex` AS `bfcol_0`, + `string_col` AS `bfcol_1` + FROM `bigframes-dev`.`sqlglot_test`.`scalar_types` +), `bfcte_1` AS ( + SELECT + *, + `bfcol_0` AS `bfcol_4`, + CONCAT(`bfcol_1`, 'a') AS `bfcol_5` + FROM `bfcte_0` +) +SELECT + `bfcol_4` AS `rowindex`, + `bfcol_5` AS `string_col` +FROM `bfcte_1` \ No newline at end of file diff --git a/tests/unit/core/compile/sqlglot/test_compile_scalar_expr.py b/tests/unit/core/compile/sqlglot/expressions/test_binary_compiler.py similarity index 82% rename from tests/unit/core/compile/sqlglot/test_compile_scalar_expr.py rename to tests/unit/core/compile/sqlglot/expressions/test_binary_compiler.py index 862ee2467c..180d43d771 100644 --- a/tests/unit/core/compile/sqlglot/test_compile_scalar_expr.py +++ b/tests/unit/core/compile/sqlglot/expressions/test_binary_compiler.py @@ -19,19 +19,25 @@ pytest.importorskip("pytest_snapshot") -def test_compile_numerical_add(scalars_types_df: bpd.DataFrame, snapshot): +def test_add_numeric(scalars_types_df: bpd.DataFrame, snapshot): bf_df = scalars_types_df[["int64_col"]] + bf_df["int64_col"] = bf_df["int64_col"] + bf_df["int64_col"] + snapshot.assert_match(bf_df.sql, "out.sql") -def test_compile_numerical_add_w_scalar(scalars_types_df: bpd.DataFrame, snapshot): +def test_add_numeric_w_scalar(scalars_types_df: bpd.DataFrame, snapshot): bf_df = scalars_types_df[["int64_col"]] + bf_df["int64_col"] = bf_df["int64_col"] + 1 + snapshot.assert_match(bf_df.sql, "out.sql") -def test_compile_string_add(scalars_types_df: bpd.DataFrame, snapshot): +def test_add_string(scalars_types_df: bpd.DataFrame, snapshot): bf_df = scalars_types_df[["string_col"]] + bf_df["string_col"] = bf_df["string_col"] + "a" + snapshot.assert_match(bf_df.sql, "out.sql") diff --git a/tests/unit/core/compile/sqlglot/expressions/test_op_registration.py b/tests/unit/core/compile/sqlglot/expressions/test_op_registration.py new file mode 100644 index 0000000000..1c49dde6ca --- /dev/null +++ b/tests/unit/core/compile/sqlglot/expressions/test_op_registration.py @@ -0,0 +1,43 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import pytest +from sqlglot import expressions as sge + +from bigframes.core.compile.sqlglot.expressions import op_registration +from bigframes.operations import numeric_ops + + +def test_register_then_get(): + reg = op_registration.OpRegistration() + input = sge.to_identifier("A") + op = numeric_ops.add_op + + @reg.register(numeric_ops.AddOp) + def test_func(op: numeric_ops.AddOp, input: sge.Expression) -> sge.Expression: + return input + + assert reg[numeric_ops.add_op](op, input) == test_func(op, input) + assert reg[numeric_ops.add_op.name](op, input) == test_func(op, input) + + +def test_register_function_first_argument_is_not_scalar_op_raise_error(): + reg = op_registration.OpRegistration() + + @reg.register(numeric_ops.AddOp) + def test_func(input: sge.Expression) -> sge.Expression: + return input + + with pytest.raises(ValueError, match=r".*first parameter must be an operator.*"): + test_func(sge.to_identifier("A")) diff --git a/tests/unit/core/compile/sqlglot/expressions/test_unary_compiler.py b/tests/unit/core/compile/sqlglot/expressions/test_unary_compiler.py new file mode 100644 index 0000000000..317c2f891b --- /dev/null +++ b/tests/unit/core/compile/sqlglot/expressions/test_unary_compiler.py @@ -0,0 +1,44 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import pytest + +from bigframes import bigquery +import bigframes.pandas as bpd + +pytest.importorskip("pytest_snapshot") + + +def test_array_to_string(repeated_types_df: bpd.DataFrame, snapshot): + result = bigquery.array_to_string(repeated_types_df["string_list_col"], ".") + + snapshot.assert_match(result.to_frame().sql, "out.sql") + + +def test_array_index(repeated_types_df: bpd.DataFrame, snapshot): + result = repeated_types_df["string_list_col"].list[1] + + snapshot.assert_match(result.to_frame().sql, "out.sql") + + +def test_array_slice_with_only_start(repeated_types_df: bpd.DataFrame, snapshot): + result = repeated_types_df["string_list_col"].list[1:] + + snapshot.assert_match(result.to_frame().sql, "out.sql") + + +def test_array_slice_with_start_and_stop(repeated_types_df: bpd.DataFrame, snapshot): + result = repeated_types_df["string_list_col"].list[1:5] + + snapshot.assert_match(result.to_frame().sql, "out.sql") From 80bac0ff6f4091b34ccae4ee5cb045e684c8c98c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Swe=C3=B1a=20=28Swast=29?= Date: Wed, 25 Jun 2025 16:50:45 -0500 Subject: [PATCH 11/28] chore: add script to generate `_read_gbq_colab` BigQuery benchmark tables (#1846) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add script to generate BigQuery benchmark tables This script creates 10 BigQuery tables with varying schemas and data volumes based on predefined statistics. Key features: - Dynamically generates table schemas to match target average row sizes, maximizing data type diversity. - Generates random data for each table, respecting BigQuery data types. - Includes placeholders for GCP project and dataset IDs. - Handles very large table data generation by capping row numbers for in-memory processing and printing warnings (actual BQ load for huge tables would require GCS load jobs). - Adds a specific requirements file for this script: `scripts/requirements-create_tables.txt`. * Refactor: Vectorize data generation in benchmark script Vectorized the `generate_random_data` function in `scripts/create_read_gbq_colab_benchmark_tables.py`. Changes include: - Using NumPy's vectorized operations (`size` parameter in random functions, `np.vectorize`) to generate arrays of random values for most data types at once. - Employing list comprehensions for transformations on these arrays (e.g., formatting dates, generating strings from character arrays). - Retaining loops for types where full vectorization is overly complex or offers little benefit (e.g., precise byte-length JSON strings, BYTES generation via `rng.bytes`). - Assembling the final list of row dictionaries from the generated columnar data. This should improve performance for data generation, especially for tables with a large number of rows. * Implement batched data generation and loading Refactored the script to process data in batches, significantly improving memory efficiency for large tables. Changes include: 1. `generate_random_data` function: * Modified to be a generator, yielding data in chunks of a specified `batch_size`. * The core vectorized logic for creating column data within each batch is retained. 2. `create_and_load_table` function: * Updated to consume data from the `generate_random_data` generator. * No longer accepts a full list of data rows. * For actual BigQuery loads, it iterates through generated batches and further sub-batches them (if necessary) for optimal `client.insert_rows_json` calls. * Simulation mode now reflects this batched processing by showing details of the first generated batch and estimated total batches. 3. `main` function: * Removed pre-generation of the entire dataset or a capped sample. * The call to `create_and_load_table` now passes parameters required for it to invoke and manage the data generator (total `num_rows`, `rng` object, and `DATA_GENERATION_BATCH_SIZE`). * Optimize DATETIME/TIMESTAMP generation with numpy.datetime_as_string Refactored the `generate_random_data` function to use `numpy.datetime_as_string` for converting `numpy.datetime64` arrays to ISO-formatted strings for DATETIME and TIMESTAMP columns. - For DATETIME: - Python `datetime.datetime` objects are created in a list first (to ensure date component validity) then converted to `numpy.datetime64[us]`. - `numpy.datetime_as_string` is used, and the output 'T' separator is replaced with a space. - For TIMESTAMP: - `numpy.datetime64[us]` arrays are constructed directly from epoch seconds and microsecond offsets. - `numpy.datetime_as_string` is used with `timezone='UTC'` to produce a 'Z'-suffixed UTC string. This change improves performance and code clarity for generating these timestamp string formats. * Add argparse for project and dataset IDs Implemented command-line arguments for specifying Google Cloud Project ID and BigQuery Dataset ID, replacing hardcoded global constants. Changes: - Imported `argparse` module. - Added optional `--project_id` (-p) and `--dataset_id` (-d) arguments to `main()`. - If `project_id` or `dataset_id` are not provided, the script defaults to simulation mode. - `create_and_load_table` now checks for the presence of both IDs to determine if it should attempt actual BigQuery operations or run in simulation. - Error handling in `create_and_load_table` for BQ operations was adjusted to log errors per table and continue processing remaining tables, rather than halting the script. * Add unit tests for table generation script Added unit tests for `get_bq_schema` and `generate_random_data` functions in `create_read_gbq_colab_benchmark_tables.py`. - Created `scripts/create_read_gbq_colab_benchmark_tables_test.py`. - Implemented pytest-style tests covering various scenarios: - For `get_bq_schema`: - Zero and small target byte sizes. - Exact fits with fixed-size types. - Inclusion and expansion of flexible types. - Generation of all fixed types where possible. - Uniqueness of column names. - Helper function `_calculate_row_size` used for validation. - For `generate_random_data`: - Zero rows case. - Basic schema and batching logic (single batch, multiple full batches, partial last batches). - Generation of all supported data types, checking Python types, string formats (using regex and `fromisoformat`), lengths for string/bytes, and JSON validity. - Added `pytest` and `pandas` (for pytest compatibility in the current project environment) to `scripts/requirements-create_tables.txt`. - All tests pass. * refactor * reduce duplicated work * only use percentile in table name * use annotations to not fail in 3.9 * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * Update scripts/create_read_gbq_colab_benchmark_tables.py * Delete scripts/requirements-create_tables.txt * base64 encode * refactor batch generation * adjust test formatting * parallel processing --------- Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> Co-authored-by: Owl Bot --- .pre-commit-config.yaml | 2 +- noxfile.py | 2 + .../create_read_gbq_colab_benchmark_tables.py | 552 ++++++++++++++++++ ...te_read_gbq_colab_benchmark_tables_test.py | 333 +++++++++++ scripts/readme-gen/readme_gen.py | 1 - 5 files changed, 888 insertions(+), 2 deletions(-) create mode 100644 scripts/create_read_gbq_colab_benchmark_tables.py create mode 100644 scripts/create_read_gbq_colab_benchmark_tables_test.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7e46c73d0d..93cc5e4210 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -39,6 +39,6 @@ repos: rev: v1.15.0 hooks: - id: mypy - additional_dependencies: [types-requests, types-tabulate, pandas-stubs<=2.2.3.241126] + additional_dependencies: [types-requests, types-tabulate, types-PyYAML, pandas-stubs<=2.2.3.241126] exclude: "^third_party" args: ["--check-untyped-defs", "--explicit-package-bases", "--ignore-missing-imports"] diff --git a/noxfile.py b/noxfile.py index a1e8e5b99b..9346f26cba 100644 --- a/noxfile.py +++ b/noxfile.py @@ -53,6 +53,7 @@ LINT_PATHS = [ "docs", "bigframes", + "scripts", "tests", "third_party", "noxfile.py", @@ -275,6 +276,7 @@ def mypy(session): "types-requests", "types-setuptools", "types-tabulate", + "types-PyYAML", "polars", "anywidget", ] diff --git a/scripts/create_read_gbq_colab_benchmark_tables.py b/scripts/create_read_gbq_colab_benchmark_tables.py new file mode 100644 index 0000000000..703c946360 --- /dev/null +++ b/scripts/create_read_gbq_colab_benchmark_tables.py @@ -0,0 +1,552 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://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. + +from __future__ import annotations + +import argparse +import base64 +import concurrent.futures +import datetime +import json +import math +import time +from typing import Any, Iterable, MutableSequence, Sequence + +from google.cloud import bigquery +import numpy as np + +# --- Input Data --- +# Generated by querying bigquery-magics usage. See internal issue b/420984164. +TABLE_STATS: dict[str, list[float]] = { + "percentile": [9, 19, 29, 39, 49, 59, 69, 79, 89, 99], + "materialized_or_scanned_bytes": [ + 0.0, + 0.0, + 4102.0, + 76901.0, + 351693.0, + 500000.0, + 500000.0, + 1320930.0, + 17486432.0, + 1919625975.0, + ], + "num_materialized_or_scanned_rows": [ + 0.0, + 6.0, + 100.0, + 4955.0, + 23108.0, + 139504.0, + 616341.0, + 3855698.0, + 83725698.0, + 5991998082.0, + ], + "avg_row_bytes": [ + 0.00014346299635435792, + 0.005370969708923197, + 0.3692756731526246, + 4.079344721151818, + 7.5418, + 12.528863516404146, + 22.686258546389798, + 48.69689224091025, + 100.90817356205852, + 2020, + ], + "materialized_mb": [ + 0.0, + 0.0, + 0.004102, + 0.076901, + 0.351693, + 0.5, + 0.5, + 1.32093, + 17.486432, + 1919.625975, + ], +} + +BIGQUERY_DATA_TYPE_SIZES = { + "BOOL": 1, + "DATE": 8, + "FLOAT64": 8, + "INT64": 8, + "DATETIME": 8, + "TIMESTAMP": 8, + "TIME": 8, + "NUMERIC": 16, + # Flexible types. + # JSON base size is its content, BYTES/STRING have 2 byte overhead + content + "JSON": 0, + "BYTES": 2, + "STRING": 2, +} +FIXED_TYPES = [ + "BOOL", + "INT64", + "FLOAT64", + "NUMERIC", + "DATE", + "DATETIME", + "TIMESTAMP", + "TIME", +] +FLEXIBLE_TYPES = ["STRING", "BYTES", "JSON"] + +JSON_CHAR_LIST = list("abcdef") +STRING_CHAR_LIST = list("abcdefghijklmnopqrstuvwxyz0123456789") + +# --- Helper Functions --- + + +def get_bq_schema(target_row_size_bytes: int) -> Sequence[tuple[str, str, int | None]]: + """ + Determines the BigQuery table schema to match the target_row_size_bytes. + Prioritizes fixed-size types for diversity, then uses flexible types. + Returns a list of tuples: (column_name, type_name, length_for_flexible_type). + Length is None for fixed-size types. + """ + schema: MutableSequence[tuple[str, str, int | None]] = [] + current_size = 0 + col_idx = 0 + + for bq_type in FIXED_TYPES: + # For simplicity, we'll allow slight overage if only fixed fields are chosen. + if current_size >= target_row_size_bytes: + break + + type_size = BIGQUERY_DATA_TYPE_SIZES[bq_type] + schema.append((f"col_{bq_type.lower()}_{col_idx}", bq_type, None)) + current_size += type_size + col_idx += 1 + + # Use flexible-size types to fill remaining space + + # Attempt to add one of each flexible type if space allows + if current_size < target_row_size_bytes: + remaining_bytes_for_content = target_row_size_bytes - current_size + + # For simplicity, divide the remaing bytes evenly across the flexible + # columns. + target_size = int(math.ceil(remaining_bytes_for_content / len(FLEXIBLE_TYPES))) + + for bq_type in FLEXIBLE_TYPES: + base_cost = BIGQUERY_DATA_TYPE_SIZES[bq_type] + min_content_size = max(0, target_size - base_cost) + + schema.append( + (f"col_{bq_type.lower()}_{col_idx}", bq_type, min_content_size) + ) + current_size += base_cost + min_content_size + col_idx += 1 + + return schema + + +def generate_bool_batch( + num_rows: int, rng: np.random.Generator, content_length: int | None = None +) -> np.ndarray: + return rng.choice([True, False], size=num_rows) + + +def generate_int64_batch( + num_rows: int, rng: np.random.Generator, content_length: int | None = None +) -> np.ndarray: + return rng.integers(-(10**18), 10**18, size=num_rows, dtype=np.int64) + + +def generate_float64_batch( + num_rows: int, rng: np.random.Generator, content_length: int | None = None +) -> np.ndarray: + return rng.random(size=num_rows) * 2 * 10**10 - 10**10 + + +def generate_numeric_batch( + num_rows: int, rng: np.random.Generator, content_length: int | None = None +) -> np.ndarray: + raw_numerics = rng.random(size=num_rows) * 2 * 10**28 - 10**28 + format_numeric_vectorized = np.vectorize(lambda x: f"{x:.9f}") + return format_numeric_vectorized(raw_numerics) + + +def generate_date_batch( + num_rows: int, rng: np.random.Generator, content_length: int | None = None +) -> np.ndarray: + start_date_ord = datetime.date(1, 1, 1).toordinal() + max_days = (datetime.date(9999, 12, 31) - datetime.date(1, 1, 1)).days + day_offsets = rng.integers(0, max_days + 1, size=num_rows) + date_ordinals = start_date_ord + day_offsets + return np.array( + [ + datetime.date.fromordinal(int(ordinal)).isoformat() + for ordinal in date_ordinals + ] + ) + + +def generate_numpy_datetimes(num_rows: int, rng: np.random.Generator) -> np.ndarray: + # Generate seconds from a broad range (e.g., year 1 to 9999) + # Note: Python's datetime.timestamp() might be limited by system's C mktime. + # For broader range with np.datetime64, it's usually fine. + # Let's generate epoch seconds relative to Unix epoch for np.datetime64 compatibility + min_epoch_seconds = int( + datetime.datetime(1, 1, 1, 0, 0, 0, tzinfo=datetime.timezone.utc).timestamp() + ) + # Max for datetime64[s] is far out, but let's bound it reasonably for BQ. + max_epoch_seconds = int( + datetime.datetime( + 9999, 12, 28, 23, 59, 59, tzinfo=datetime.timezone.utc + ).timestamp() + ) + + epoch_seconds = rng.integers( + min_epoch_seconds, + max_epoch_seconds + 1, + size=num_rows, + dtype=np.int64, + ) + microseconds_offset = rng.integers(0, 1000000, size=num_rows, dtype=np.int64) + + # Create datetime64[s] from epoch seconds and add microseconds as timedelta64[us] + np_timestamps_s = epoch_seconds.astype("datetime64[s]") + np_microseconds_td = microseconds_offset.astype("timedelta64[us]") + return np_timestamps_s + np_microseconds_td + + +def generate_datetime_batch( + num_rows: int, rng: np.random.Generator, content_length: int | None = None +) -> np.ndarray: + np_datetimes = generate_numpy_datetimes(num_rows, rng) + + # np.datetime_as_string produces 'YYYY-MM-DDTHH:MM:SS.ffffff' + # BQ DATETIME typically uses a space separator: 'YYYY-MM-DD HH:MM:SS.ffffff' + datetime_strings = np.datetime_as_string(np_datetimes, unit="us") + return np.array([s.replace("T", " ") for s in datetime_strings]) + + +def generate_timestamp_batch( + num_rows: int, rng: np.random.Generator, content_length: int | None = None +) -> np.ndarray: + np_datetimes = generate_numpy_datetimes(num_rows, rng) + + # Convert to string with UTC timezone indicator + # np.datetime_as_string with timezone='UTC' produces 'YYYY-MM-DDTHH:MM:SS.ffffffZ' + # BigQuery generally accepts this for TIMESTAMP. + return np.datetime_as_string(np_datetimes, unit="us", timezone="UTC") + + +def generate_time_batch( + num_rows: int, rng: np.random.Generator, content_length: int | None = None +) -> np.ndarray: + hours = rng.integers(0, 24, size=num_rows) + minutes = rng.integers(0, 60, size=num_rows) + seconds = rng.integers(0, 60, size=num_rows) + microseconds = rng.integers(0, 1000000, size=num_rows) + time_list = [ + datetime.time(hours[i], minutes[i], seconds[i], microseconds[i]).isoformat() + for i in range(num_rows) + ] + return np.array(time_list) + + +def generate_json_row(content_length: int, rng: np.random.Generator) -> str: + json_val_len = max(0, content_length - 5) + json_val_chars = rng.choice(JSON_CHAR_LIST, size=json_val_len) + json_obj = {"k": "".join(json_val_chars)} + return json.dumps(json_obj) + + +def generate_json_batch( + num_rows: int, rng: np.random.Generator, content_length: int | None = None +) -> np.ndarray: + content_length = content_length if content_length is not None else 10 + json_list = [ + generate_json_row(content_length=content_length, rng=rng) + for _ in range(num_rows) + ] + return np.array(json_list) + + +def generate_string_batch( + num_rows: int, rng: np.random.Generator, content_length: int | None = None +) -> np.ndarray: + content_length = content_length if content_length is not None else 1 + content_length = max(0, content_length) + chars_array = rng.choice(STRING_CHAR_LIST, size=(num_rows, content_length)) + return np.array(["".join(row_chars) for row_chars in chars_array]) + + +def generate_bytes_batch( + num_rows: int, rng: np.random.Generator, content_length: int | None = None +) -> np.ndarray: + content_length = content_length if content_length is not None else 1 + content_length = max(0, content_length) + return np.array( + [ + base64.b64encode(rng.bytes(content_length)).decode("utf-8") + for _ in range(num_rows) + ] + ) + + +BIGQUERY_DATA_TYPE_GENERATORS = { + "BOOL": generate_bool_batch, + "DATE": generate_date_batch, + "FLOAT64": generate_float64_batch, + "INT64": generate_int64_batch, + "DATETIME": generate_datetime_batch, + "TIMESTAMP": generate_timestamp_batch, + "TIME": generate_time_batch, + "NUMERIC": generate_numeric_batch, + "JSON": generate_json_batch, + "BYTES": generate_bytes_batch, + "STRING": generate_string_batch, +} + + +def generate_work_items( + table_id: str, + schema: Sequence[tuple[str, str, int | None]], + num_rows: int, + batch_size: int, +) -> Iterable[tuple[str, Sequence[tuple[str, str, int | None]], int]]: + """ + Generates work items of appropriate batch sizes. + """ + if num_rows == 0: + return + + generated_rows_total = 0 + + while generated_rows_total < num_rows: + current_batch_size = min(batch_size, num_rows - generated_rows_total) + if current_batch_size == 0: + break + + yield (table_id, schema, current_batch_size) + generated_rows_total += current_batch_size + + +def generate_batch( + schema: Sequence[tuple[str, str, int | None]], + num_rows: int, + rng: np.random.Generator, +) -> list[dict[str, Any]]: + col_names_ordered = [s[0] for s in schema] + + columns_data_batch = {} + for col_name, bq_type, length in schema: + generate_batch = BIGQUERY_DATA_TYPE_GENERATORS[bq_type] + columns_data_batch[col_name] = generate_batch( + num_rows, rng, content_length=length + ) + + # Turn numpy objects into Python objects. + # https://stackoverflow.com/a/32850511/101923 + columns_data_batch_json = {} + for column in columns_data_batch: + columns_data_batch_json[column] = columns_data_batch[column].tolist() + + # Assemble batch of rows + batch_data = [] + for i in range(num_rows): + row = { + col_name: columns_data_batch_json[col_name][i] + for col_name in col_names_ordered + } + batch_data.append(row) + + return batch_data + + +def generate_and_load_batch( + client: bigquery.Client, + table_id: str, + schema_def: Sequence[tuple[str, str, int | None]], + num_rows: int, + rng: np.random.Generator, +): + bq_schema = [] + for col_name, type_name, _ in schema_def: + bq_schema.append(bigquery.SchemaField(col_name, type_name)) + table = bigquery.Table(table_id, schema=bq_schema) + + generated_data_chunk = generate_batch(schema_def, num_rows, rng) + errors = client.insert_rows_json(table, generated_data_chunk) + if errors: + raise ValueError(f"Encountered errors while inserting sub-batch: {errors}") + + +def create_and_load_table( + client: bigquery.Client | None, + project_id: str, + dataset_id: str, + table_name: str, + schema_def: Sequence[tuple[str, str, int | None]], + num_rows: int, + executor: concurrent.futures.Executor, +): + """Creates a BigQuery table and loads data into it by consuming a data generator.""" + + if not client: + print(f"Simulating: Generated schema: {schema_def}") + return + + # BQ client library streaming insert batch size (rows per API call) + # This is different from data_gen_batch_size which is for generating data. + # We can make BQ_LOAD_BATCH_SIZE smaller than data_gen_batch_size if needed. + BQ_LOAD_BATCH_SIZE = 500 + + # Actual BigQuery operations occur here because both project_id and dataset_id are provided + print( + f"Attempting BigQuery operations for table {table_name} in project '{project_id}', dataset '{dataset_id}'." + ) + table_id = f"{project_id}.{dataset_id}.{table_name}" + + bq_schema = [] + for col_name, type_name, _ in schema_def: + bq_schema.append(bigquery.SchemaField(col_name, type_name)) + + table = bigquery.Table(table_id, schema=bq_schema) + print(f"(Re)creating table {table_id}...") + table = client.create_table(table, exists_ok=True) + print(f"Table {table_id} created successfully or already exists.") + + # Query in case there's something in the streaming buffer already. + table_rows = next( + iter(client.query_and_wait(f"SELECT COUNT(*) FROM `{table_id}`")) + )[0] + print(f"Table {table_id} has {table_rows} rows.") + num_rows = max(0, num_rows - table_rows) + + if num_rows <= 0: + print(f"No rows to load. Requested {num_rows} rows. Skipping.") + return + + print(f"Starting to load {num_rows} rows into {table_id} in batches...") + + previous_status_time = 0.0 + generated_rows_total = 0 + + for completed_rows in executor.map( + worker_process_item, + generate_work_items( + table_id, + schema_def, + num_rows, + BQ_LOAD_BATCH_SIZE, + ), + ): + generated_rows_total += completed_rows + + current_time = time.monotonic() + if current_time - previous_status_time > 5: + print(f"Wrote {generated_rows_total} out of {num_rows} rows.") + previous_status_time = current_time + + +worker_client: bigquery.Client | None = None +worker_rng: np.random.Generator | None = None + + +def worker_initializer(project_id: str | None): + global worker_client, worker_rng + + # One client per process, since multiprocessing and client connections don't + # play nicely together. + if project_id is not None: + worker_client = bigquery.Client(project=project_id) + + worker_rng = np.random.default_rng() + + +def worker_process_item( + work_item: tuple[str, Sequence[tuple[str, str, int | None]], int] +): + global worker_client, worker_rng + + if worker_client is None or worker_rng is None: + raise ValueError("Worker not initialized.") + + table_id, schema_def, num_rows = work_item + generate_and_load_batch(worker_client, table_id, schema_def, num_rows, worker_rng) + return num_rows + + +# --- Main Script Logic --- +def main(): + """Main function to create and populate BigQuery tables.""" + + parser = argparse.ArgumentParser( + description="Generate and load BigQuery benchmark tables." + ) + parser.add_argument( + "-p", + "--project_id", + type=str, + default=None, + help="Google Cloud Project ID. If not provided, script runs in simulation mode.", + ) + parser.add_argument( + "-d", + "--dataset_id", + type=str, + default=None, + help="BigQuery Dataset ID within the project. If not provided, script runs in simulation mode.", + ) + args = parser.parse_args() + + num_percentiles = len(TABLE_STATS["percentile"]) + client = None + + if args.project_id and args.dataset_id: + client = bigquery.Client(project=args.project_id) + dataset = bigquery.Dataset(f"{args.project_id}.{args.dataset_id}") + client.create_dataset(dataset, exists_ok=True) + + with concurrent.futures.ProcessPoolExecutor( + initializer=worker_initializer, initargs=(args.project_id,) + ) as executor: + for i in range(num_percentiles): + percentile = TABLE_STATS["percentile"][i] + avg_row_bytes_raw = TABLE_STATS["avg_row_bytes"][i] + num_rows_raw = TABLE_STATS["num_materialized_or_scanned_rows"][i] + + target_row_bytes = max(1, int(math.ceil(avg_row_bytes_raw))) + num_rows = max(1, int(math.ceil(num_rows_raw))) + + table_name = f"percentile_{percentile:02d}" + print(f"\n--- Processing Table: {table_name} ---") + print(f"Target average row bytes (rounded up): {target_row_bytes}") + print(f"Number of rows (rounded up): {num_rows}") + + schema_definition = get_bq_schema(target_row_bytes) + print(f"Generated Schema: {schema_definition}") + + create_and_load_table( + client, + args.project_id or "", + args.dataset_id or "", + table_name, + schema_definition, + num_rows, + executor, + ) + + +if __name__ == "__main__": + main() diff --git a/scripts/create_read_gbq_colab_benchmark_tables_test.py b/scripts/create_read_gbq_colab_benchmark_tables_test.py new file mode 100644 index 0000000000..89c49e4243 --- /dev/null +++ b/scripts/create_read_gbq_colab_benchmark_tables_test.py @@ -0,0 +1,333 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://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. + +from __future__ import annotations + +import base64 +import datetime +import json +import math +import re + +# Assuming the script to be tested is in the same directory or accessible via PYTHONPATH +from create_read_gbq_colab_benchmark_tables import ( + BIGQUERY_DATA_TYPE_SIZES, + generate_batch, + generate_work_items, + get_bq_schema, +) +import numpy as np +import pytest + + +# Helper function to calculate estimated row size from schema +def _calculate_row_size(schema: list[tuple[str, str, int | None]]) -> int: + """Calculates the estimated byte size of a row based on the schema. + Note: This is a simplified calculation for testing and might not perfectly + match BigQuery's internal storage, especially for complex types or NULLs. + """ + size = 0 + for _, bq_type, length in schema: + if bq_type in ["STRING", "BYTES", "JSON"]: + # Base cost (e.g., 2 bytes) + content length + size += BIGQUERY_DATA_TYPE_SIZES[bq_type] + ( + length if length is not None else 0 + ) + elif bq_type in BIGQUERY_DATA_TYPE_SIZES: + size += BIGQUERY_DATA_TYPE_SIZES[bq_type] + else: + raise AssertionError(f"Got unexpected type {bq_type}") + return size + + +# --- Tests for get_bq_schema --- + + +def test_get_bq_schema_zero_bytes(): + assert get_bq_schema(0) == [] + + +def test_get_bq_schema_one_byte(): + schema = get_bq_schema(1) + + assert len(schema) == 1 + assert schema[0][1] == "BOOL" # ('col_bool_fallback_0', 'BOOL', None) or similar + assert _calculate_row_size(schema) == 1 + + +def test_get_bq_schema_exact_fixed_fit(): + # BOOL (1) + INT64 (8) = 9 bytes + target_size = 9 + schema = get_bq_schema(target_size) + + assert len(schema) == 2 + assert schema[0][1] == "BOOL" + assert schema[1][1] == "INT64" + assert _calculate_row_size(schema) == target_size + + +def test_get_bq_schema_needs_flexible_string(): + # Sum of all fixed types: + # BOOL 1, INT64 8, FLOAT64 8, NUMERIC 16, DATE 8, DATETIME 8, TIMESTAMP 8, TIME 8 + # Total = 1+8+8+16+8+8+8+8 = 65 + target_size = 65 + 1 + schema = get_bq_schema(target_size) + + assert _calculate_row_size(schema) == 65 + 2 + 2 + 1 + + string_cols = [s for s in schema if s[1] == "STRING"] + assert len(string_cols) == 1 + assert string_cols[0][2] == 0 + + bytes_cols = [s for s in schema if s[1] == "BYTES"] + assert len(bytes_cols) == 1 + assert bytes_cols[0][2] == 0 + + json_cols = [s for s in schema if s[1] == "JSON"] + assert len(json_cols) == 1 + assert json_cols[0][2] == 1 + + +def test_get_bq_schema_flexible_expansion(): + # Sum of all fixed types: + # BOOL 1, INT64 8, FLOAT64 8, NUMERIC 16, DATE 8, DATETIME 8, TIMESTAMP 8, TIME 8 + # Total = 1+8+8+16+8+8+8+8 = 65 + target_size = 65 + 3 * 5 + schema = get_bq_schema(target_size) + + assert _calculate_row_size(schema) == target_size + + string_cols = [s for s in schema if s[1] == "STRING"] + assert len(string_cols) == 1 + assert string_cols[0][2] == 3 + + bytes_cols = [s for s in schema if s[1] == "BYTES"] + assert len(bytes_cols) == 1 + assert bytes_cols[0][2] == 3 + + json_cols = [s for s in schema if s[1] == "JSON"] + assert len(json_cols) == 1 + assert json_cols[0][2] == 5 + + +def test_get_bq_schema_all_fixed_types_possible(): + # Sum of all fixed types: + # BOOL 1, INT64 8, FLOAT64 8, NUMERIC 16, DATE 8, DATETIME 8, TIMESTAMP 8, TIME 8 + # Total = 1+8+8+16+8+8+8+8 = 65 + target_size = 65 + schema = get_bq_schema(target_size) + + expected_fixed_types = { + "BOOL", + "INT64", + "FLOAT64", + "NUMERIC", + "DATE", + "DATETIME", + "TIMESTAMP", + "TIME", + } + present_types = {s[1] for s in schema} + + assert expected_fixed_types.issubset(present_types) + + # Check if the size is close to target. + # All fixed (65) + calculated_size = _calculate_row_size(schema) + assert calculated_size == target_size + + +def test_get_bq_schema_uniqueness_of_column_names(): + target_size = 100 # A size that generates multiple columns + schema = get_bq_schema(target_size) + + column_names = [s[0] for s in schema] + assert len(column_names) == len(set(column_names)) + + +# --- Tests for generate_work_items --- + + +def test_generate_work_items_zero_rows(): + schema = [("col_int", "INT64", None)] + data_generator = generate_work_items( + "some_table", schema, num_rows=0, batch_size=10 + ) + + # Expect the generator to be exhausted + with pytest.raises(StopIteration): + next(data_generator) + + +def test_generate_work_items_basic_schema_and_batching(): + schema = [("id", "INT64", None), ("is_active", "BOOL", None)] + num_rows = 25 + batch_size = 10 + + generated_rows_count = 0 + batch_count = 0 + for work_item in generate_work_items("some_table", schema, num_rows, batch_size): + table_id, schema_def, num_rows_in_batch = work_item + assert table_id == "some_table" + assert schema_def == schema + assert num_rows_in_batch <= num_rows + assert num_rows_in_batch <= batch_size + batch_count += 1 + generated_rows_count += num_rows_in_batch + + assert generated_rows_count == num_rows + assert batch_count == math.ceil(num_rows / batch_size) # 25/10 = 2.5 -> 3 batches + + +def test_generate_work_items_batch_size_larger_than_num_rows(): + schema = [("value", "FLOAT64", None)] + num_rows = 5 + batch_size = 100 + + generated_rows_count = 0 + batch_count = 0 + for work_item in generate_work_items("some_table", schema, num_rows, batch_size): + table_id, schema_def, num_rows_in_batch = work_item + assert table_id == "some_table" + assert schema_def == schema + assert num_rows_in_batch == num_rows # Should be one batch with all rows + batch_count += 1 + generated_rows_count += num_rows_in_batch + + assert generated_rows_count == num_rows + assert batch_count == 1 + + +def test_generate_work_items_all_datatypes(rng): + schema = [ + ("c_bool", "BOOL", None), + ("c_int64", "INT64", None), + ("c_float64", "FLOAT64", None), + ("c_numeric", "NUMERIC", None), + ("c_date", "DATE", None), + ("c_datetime", "DATETIME", None), + ("c_timestamp", "TIMESTAMP", None), + ("c_time", "TIME", None), + ("c_string", "STRING", 10), + ("c_bytes", "BYTES", 5), + ("c_json", "JSON", 20), # Length for JSON is content hint + ] + num_rows = 3 + batch_size = 2 # To test multiple batches + + total_rows_processed = 0 + for work_item in generate_work_items("some_table", schema, num_rows, batch_size): + table_id, schema_def, num_rows_in_batch = work_item + assert table_id == "some_table" + assert schema_def == schema + assert num_rows_in_batch <= batch_size + assert num_rows_in_batch <= num_rows + + total_rows_processed += num_rows_in_batch + + assert total_rows_processed == num_rows + + +# --- Pytest Fixture for RNG --- +@pytest.fixture +def rng(): + return np.random.default_rng(seed=42) + + +def test_generate_batch_basic_schema(rng): + schema = [("id", "INT64", None), ("is_active", "BOOL", None)] + batch = generate_batch(schema, 5, rng) + + assert len(batch) == 5 + + for row in batch: + assert isinstance(row, dict) + assert "id" in row + assert "is_active" in row + assert isinstance(row["id"], int) + assert isinstance(row["is_active"], bool) + + +def test_generate_batch_all_datatypes(rng): + schema = [ + ("c_bool", "BOOL", None), + ("c_int64", "INT64", None), + ("c_float64", "FLOAT64", None), + ("c_numeric", "NUMERIC", None), + ("c_date", "DATE", None), + ("c_datetime", "DATETIME", None), + ("c_timestamp", "TIMESTAMP", None), + ("c_time", "TIME", None), + ("c_string", "STRING", 10), + ("c_bytes", "BYTES", 5), + ("c_json", "JSON", 20), # Length for JSON is content hint + ] + num_rows = 3 + + date_pattern = re.compile(r"^\d{4}-\d{2}-\d{2}$") + time_pattern = re.compile(r"^\d{2}:\d{2}:\d{2}(\.\d{1,6})?$") + # BQ DATETIME: YYYY-MM-DD HH:MM:SS.ffffff + datetime_pattern = re.compile(r"^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}(\.\d{1,6})?$") + # BQ TIMESTAMP (UTC 'Z'): YYYY-MM-DDTHH:MM:SS.ffffffZ + timestamp_pattern = re.compile( + r"^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{1,6})?Z$" + ) + numeric_pattern = re.compile(r"^-?\d+\.\d{9}$") + + batch = generate_batch(schema, num_rows, rng) + assert len(batch) == num_rows + + for row in batch: + assert isinstance(row["c_bool"], bool) + assert isinstance(row["c_int64"], int) + assert isinstance(row["c_float64"], float) + + assert isinstance(row["c_numeric"], str) + assert numeric_pattern.match(row["c_numeric"]) + + assert isinstance(row["c_date"], str) + assert date_pattern.match(row["c_date"]) + datetime.date.fromisoformat(row["c_date"]) # Check parsable + + assert isinstance(row["c_datetime"], str) + assert datetime_pattern.match(row["c_datetime"]) + datetime.datetime.fromisoformat(row["c_datetime"]) # Check parsable + + assert isinstance(row["c_timestamp"], str) + assert timestamp_pattern.match(row["c_timestamp"]) + # datetime.fromisoformat can parse 'Z' if Python >= 3.11, or needs replace('Z', '+00:00') + dt_obj = datetime.datetime.fromisoformat( + row["c_timestamp"].replace("Z", "+00:00") + ) + assert dt_obj.tzinfo == datetime.timezone.utc + + assert isinstance(row["c_time"], str) + assert time_pattern.match(row["c_time"]) + datetime.time.fromisoformat(row["c_time"]) # Check parsable + + assert isinstance(row["c_string"], str) + assert len(row["c_string"]) == 10 + + c_bytes = base64.b64decode(row["c_bytes"]) + assert isinstance(c_bytes, bytes) + assert len(c_bytes) == 5 + + assert isinstance(row["c_json"], str) + try: + json.loads(row["c_json"]) # Check if it's valid JSON + except json.JSONDecodeError: + pytest.fail(f"Invalid JSON string generated: {row['c_json']}") + # Note: Exact length check for JSON is hard due to content variability and escaping. + # The 'length' parameter for JSON in schema is a hint for content size. + # We are primarily testing that it's valid JSON. diff --git a/scripts/readme-gen/readme_gen.py b/scripts/readme-gen/readme_gen.py index 8f5e248a0d..ceb1eada7c 100644 --- a/scripts/readme-gen/readme_gen.py +++ b/scripts/readme-gen/readme_gen.py @@ -24,7 +24,6 @@ import jinja2 import yaml - jinja_env = jinja2.Environment( trim_blocks=True, loader=jinja2.FileSystemLoader( From 4da333eb5fa70537f6cf30c437330373f2d748f5 Mon Sep 17 00:00:00 2001 From: TrevorBergeron Date: Wed, 25 Jun 2025 16:22:39 -0700 Subject: [PATCH 12/28] test: Enable floordiv local testing (#1856) --- bigframes/core/compile/polars/compiler.py | 4 ++ bigframes/core/compile/polars/lowering.py | 47 +++++++++++++++++++ bigframes/core/expression.py | 26 ++++++++++- bigframes/core/nodes.py | 37 ++++++++++++--- bigframes/core/rewrite/op_lowering.py | 57 +++++++++++++++++++++++ tests/unit/test_dataframe_polars.py | 12 ++--- 6 files changed, 168 insertions(+), 15 deletions(-) create mode 100644 bigframes/core/compile/polars/lowering.py create mode 100644 bigframes/core/rewrite/op_lowering.py diff --git a/bigframes/core/compile/polars/compiler.py b/bigframes/core/compile/polars/compiler.py index 62654c1518..cc007623e1 100644 --- a/bigframes/core/compile/polars/compiler.py +++ b/bigframes/core/compile/polars/compiler.py @@ -23,9 +23,11 @@ import bigframes.core from bigframes.core import identifiers, nodes, ordering, window_spec +from bigframes.core.compile.polars import lowering import bigframes.core.expression as ex import bigframes.core.guid as guid import bigframes.core.rewrite +import bigframes.core.rewrite.schema_binding import bigframes.dtypes import bigframes.operations as ops import bigframes.operations.aggregations as agg_ops @@ -403,6 +405,8 @@ def compile(self, array_value: bigframes.core.ArrayValue) -> pl.LazyFrame: node = bigframes.core.rewrite.column_pruning(node) node = nodes.bottom_up(node, bigframes.core.rewrite.rewrite_slice) node = bigframes.core.rewrite.pull_out_window_order(node) + node = bigframes.core.rewrite.schema_binding.bind_schema_to_tree(node) + node = lowering.lower_ops_to_polars(node) return self.compile_node(node) @functools.singledispatchmethod diff --git a/bigframes/core/compile/polars/lowering.py b/bigframes/core/compile/polars/lowering.py new file mode 100644 index 0000000000..88e2d6e599 --- /dev/null +++ b/bigframes/core/compile/polars/lowering.py @@ -0,0 +1,47 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from bigframes import dtypes +from bigframes.core import bigframe_node, expression +from bigframes.core.rewrite import op_lowering +from bigframes.operations import numeric_ops +import bigframes.operations as ops + +# TODO: Would be more precise to actually have separate op set for polars ops (where they diverge from the original ops) + + +class LowerFloorDivRule(op_lowering.OpLoweringRule): + @property + def op(self) -> type[ops.ScalarOp]: + return numeric_ops.FloorDivOp + + def lower(self, expr: expression.OpExpression) -> expression.Expression: + dividend = expr.children[0] + divisor = expr.children[1] + using_floats = (dividend.output_type == dtypes.FLOAT_DTYPE) or ( + divisor.output_type == dtypes.FLOAT_DTYPE + ) + inf_or_zero = ( + expression.const(float("INF")) if using_floats else expression.const(0) + ) + zero_result = ops.mul_op.as_expr(inf_or_zero, dividend) + divisor_is_zero = ops.eq_op.as_expr(divisor, expression.const(0)) + return ops.where_op.as_expr(zero_result, divisor_is_zero, expr) + + +POLARS_LOWERING_RULES = (LowerFloorDivRule(),) + + +def lower_ops_to_polars(root: bigframe_node.BigFrameNode) -> bigframe_node.BigFrameNode: + return op_lowering.lower_ops(root, rules=POLARS_LOWERING_RULES) diff --git a/bigframes/core/expression.py b/bigframes/core/expression.py index 238b588fea..40ba70c555 100644 --- a/bigframes/core/expression.py +++ b/bigframes/core/expression.py @@ -19,7 +19,7 @@ import functools import itertools import typing -from typing import Generator, Mapping, TypeVar, Union +from typing import Callable, Generator, Mapping, TypeVar, Union import pandas as pd @@ -249,6 +249,10 @@ def is_identity(self) -> bool: """True for identity operation that does not transform input.""" return False + @abc.abstractmethod + def transform_children(self, t: Callable[[Expression], Expression]) -> Expression: + ... + def walk(self) -> Generator[Expression, None, None]: yield self for child in self.children: @@ -311,6 +315,9 @@ def __eq__(self, other): return self.value == other.value and self.dtype == other.dtype + def transform_children(self, t: Callable[[Expression], Expression]) -> Expression: + return self + @dataclasses.dataclass(frozen=True) class UnboundVariableExpression(Expression): @@ -362,6 +369,9 @@ def is_bijective(self) -> bool: def is_identity(self) -> bool: return True + def transform_children(self, t: Callable[[Expression], Expression]) -> Expression: + return self + @dataclasses.dataclass(frozen=True) class DerefOp(Expression): @@ -414,6 +424,9 @@ def is_bijective(self) -> bool: def is_identity(self) -> bool: return True + def transform_children(self, t: Callable[[Expression], Expression]) -> Expression: + return self + @dataclasses.dataclass(frozen=True) class SchemaFieldRefExpression(Expression): @@ -463,12 +476,15 @@ def is_bijective(self) -> bool: def is_identity(self) -> bool: return True + def transform_children(self, t: Callable[[Expression], Expression]) -> Expression: + return self + @dataclasses.dataclass(frozen=True) class OpExpression(Expression): """An expression representing a scalar operation applied to 1 or more argument sub-expressions.""" - op: bigframes.operations.RowOp + op: bigframes.operations.ScalarOp inputs: typing.Tuple[Expression, ...] @property @@ -553,6 +569,12 @@ def deterministic(self) -> bool: all(input.deterministic for input in self.inputs) and self.op.deterministic ) + def transform_children(self, t: Callable[[Expression], Expression]) -> Expression: + new_inputs = tuple(t(input) for input in self.inputs) + if new_inputs != self.inputs: + return dataclasses.replace(self, inputs=new_inputs) + return self + def bind_schema_fields( expr: Expression, field_by_id: Mapping[ids.ColumnId, field.Field] diff --git a/bigframes/core/nodes.py b/bigframes/core/nodes.py index 38becd29df..205621fee2 100644 --- a/bigframes/core/nodes.py +++ b/bigframes/core/nodes.py @@ -1008,6 +1008,14 @@ def referenced_ids(self) -> COLUMN_SET: def _node_expressions(self): return (self.predicate,) + def transform_exprs( + self, fn: Callable[[ex.Expression], ex.Expression] + ) -> FilterNode: + return dataclasses.replace( + self, + predicate=fn(self.predicate), + ) + def remap_vars( self, mappings: Mapping[identifiers.ColumnId, identifiers.ColumnId] ) -> FilterNode: @@ -1066,6 +1074,20 @@ def referenced_ids(self) -> COLUMN_SET: def _node_expressions(self): return tuple(map(lambda x: x.scalar_expression, self.by)) + def transform_exprs( + self, fn: Callable[[ex.Expression], ex.Expression] + ) -> OrderByNode: + new_by = cast( + tuple[OrderingExpression, ...], + tuple( + dataclasses.replace( + by_expr, scalar_expression=fn(by_expr.scalar_expression) + ) + for by_expr in self.by + ), + ) + return dataclasses.replace(self, by=new_by) + def remap_vars( self, mappings: Mapping[identifiers.ColumnId, identifiers.ColumnId] ) -> OrderByNode: @@ -1078,14 +1100,9 @@ def remap_refs( itertools.chain.from_iterable(map(lambda x: x.referenced_columns, self.by)) ) ref_mapping = {id: ex.DerefOp(mappings[id]) for id in all_refs} - new_by = cast( - tuple[OrderingExpression, ...], - tuple( - by_expr.bind_refs(ref_mapping, allow_partial_bindings=True) - for by_expr in self.by - ), + return self.transform_exprs( + lambda ex: ex.bind_refs(ref_mapping, allow_partial_bindings=True) ) - return dataclasses.replace(self, by=new_by) @dataclasses.dataclass(frozen=True, eq=False) @@ -1293,6 +1310,12 @@ def _node_expressions(self): def additive_base(self) -> BigFrameNode: return self.child + def transform_exprs( + self, fn: Callable[[ex.Expression], ex.Expression] + ) -> ProjectionNode: + new_fields = tuple((fn(ex), id) for ex, id in self.assignments) + return dataclasses.replace(self, assignments=new_fields) + def replace_additive_base(self, node: BigFrameNode) -> ProjectionNode: return dataclasses.replace(self, child=node) diff --git a/bigframes/core/rewrite/op_lowering.py b/bigframes/core/rewrite/op_lowering.py new file mode 100644 index 0000000000..a64a4cc8c4 --- /dev/null +++ b/bigframes/core/rewrite/op_lowering.py @@ -0,0 +1,57 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +from __future__ import annotations + +import abc +from typing import Sequence + +from bigframes.core import bigframe_node, expression, nodes +import bigframes.operations as ops + + +class OpLoweringRule(abc.ABC): + @property + @abc.abstractmethod + def op(self) -> type[ops.ScalarOp]: + ... + + @abc.abstractmethod + def lower(self, expr: expression.OpExpression) -> expression.Expression: + ... + + +def lower_ops( + root: bigframe_node.BigFrameNode, rules: Sequence[OpLoweringRule] +) -> bigframe_node.BigFrameNode: + rules_by_op = {rule.op: rule for rule in rules} + + def lower_expr(expr: expression.Expression): + def lower_expr_step(expr: expression.Expression) -> expression.Expression: + if isinstance(expr, expression.OpExpression): + maybe_rule = rules_by_op.get(expr.op.__class__) + if maybe_rule: + return maybe_rule.lower(expr) + return expr + + return lower_expr_step(expr.transform_children(lower_expr_step)) + + def lower_node(node: bigframe_node.BigFrameNode) -> bigframe_node.BigFrameNode: + if isinstance( + node, (nodes.ProjectionNode, nodes.FilterNode, nodes.OrderByNode) + ): + return node.transform_exprs(lower_expr) + else: + return node + + return root.bottom_up(lower_node) diff --git a/tests/unit/test_dataframe_polars.py b/tests/unit/test_dataframe_polars.py index b434e473e9..f7f0cc80bb 100644 --- a/tests/unit/test_dataframe_polars.py +++ b/tests/unit/test_dataframe_polars.py @@ -2150,7 +2150,7 @@ def test_df_corrwith_series(scalars_dfs): operator.sub, operator.mul, operator.truediv, - # operator.floordiv, + operator.floordiv, operator.eq, operator.ne, operator.gt, @@ -2163,7 +2163,7 @@ def test_df_corrwith_series(scalars_dfs): "subtract", "multiply", "true_divide", - # "floor_divide", + "floor_divide", "eq", "ne", "gt", @@ -2217,8 +2217,8 @@ def test_scalar_binop_str_exception(scalars_dfs): (lambda x, y: x.rmul(y, axis="index")), (lambda x, y: x.truediv(y, axis="index")), (lambda x, y: x.rtruediv(y, axis="index")), - # (lambda x, y: x.floordiv(y, axis="index")), - # (lambda x, y: x.floordiv(y, axis="index")), + (lambda x, y: x.floordiv(y, axis="index")), + (lambda x, y: x.floordiv(y, axis="index")), (lambda x, y: x.gt(y, axis="index")), (lambda x, y: x.ge(y, axis="index")), (lambda x, y: x.lt(y, axis="index")), @@ -2233,8 +2233,8 @@ def test_scalar_binop_str_exception(scalars_dfs): "rmul", "truediv", "rtruediv", - # "floordiv", - # "rfloordiv", + "floordiv", + "rfloordiv", "gt", "ge", "lt", From 9fb3cb444607df6736d383a2807059bca470c453 Mon Sep 17 00:00:00 2001 From: Shobhit Singh Date: Thu, 26 Jun 2025 07:26:41 -0700 Subject: [PATCH 13/28] fix: revert dict back to protobuf in the iam binding update (#1838) * fix: update the iam binding update logic This is needed as some recent updates to a dependency (google-cloud-resource-manager or its dependencies) broke the existing logic, and we are seeing the error like this: --> policy.bindings.append(new_binding) TypeError: Expected a message object, but got {'role': 'roles/run.invoker', 'members': [...]} * use right import * add unit test mocking the iam update method * use mock bq connection client * import module rephrasing --- bigframes/clients.py | 6 ++---- tests/unit/test_clients.py | 28 ++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/bigframes/clients.py b/bigframes/clients.py index f1f6d686fd..e6ddd5c6cb 100644 --- a/bigframes/clients.py +++ b/bigframes/clients.py @@ -24,6 +24,7 @@ import google.api_core.exceptions import google.api_core.retry from google.cloud import bigquery_connection_v1, resourcemanager_v3 +from google.iam.v1 import policy_pb2 logger = logging.getLogger(__name__) @@ -172,10 +173,7 @@ def _ensure_iam_binding( return # Create a new binding - new_binding = { - "role": role, - "members": [service_account], - } # Use a dictionary to avoid problematic google.iam namespace package. + new_binding = policy_pb2.Binding(role=role, members=[service_account]) policy.bindings.append(new_binding) request = { "resource": project, diff --git a/tests/unit/test_clients.py b/tests/unit/test_clients.py index 032512c26e..9daa759838 100644 --- a/tests/unit/test_clients.py +++ b/tests/unit/test_clients.py @@ -12,6 +12,10 @@ # See the License for the specific language governing permissions and # limitations under the License. +from unittest import mock + +from google.cloud import bigquery_connection_v1, resourcemanager_v3 +from google.iam.v1 import policy_pb2 import pytest from bigframes import clients @@ -65,3 +69,27 @@ def test_get_canonical_bq_connection_id_invalid_path(): default_project="default-project", default_location="us", ) + + +def test_ensure_iam_binding(): + bq_connection_client = mock.create_autospec( + bigquery_connection_v1.ConnectionServiceClient, instance=True + ) + resource_manager_client = mock.create_autospec( + resourcemanager_v3.ProjectsClient, instance=True + ) + resource_manager_client.get_iam_policy.return_value = policy_pb2.Policy( + bindings=[ + policy_pb2.Binding( + role="roles/test.role1", members=["serviceAccount:serviceAccount1"] + ) + ] + ) + bq_connection_manager = clients.BqConnectionManager( + bq_connection_client, resource_manager_client + ) + bq_connection_manager._IAM_WAIT_SECONDS = 0 # no need to wait in test + bq_connection_manager._ensure_iam_binding( + "test-project", "serviceAccount2", "roles/test.role2" + ) + resource_manager_client.set_iam_policy.assert_called_once() From daf0c3b349fb1e85e7070c54a2d3f5460f5e40c9 Mon Sep 17 00:00:00 2001 From: TrevorBergeron Date: Thu, 26 Jun 2025 10:57:40 -0700 Subject: [PATCH 14/28] feat: Add experimental polars execution (#1747) --- bigframes/_config/bigquery_options.py | 24 +++++++ bigframes/_importing.py | 30 ++++++++ bigframes/core/compile/polars/compiler.py | 4 +- bigframes/session/__init__.py | 1 + bigframes/session/bq_caching_executor.py | 24 +++++-- bigframes/session/polars_executor.py | 2 +- bigframes/testing/polars_session.py | 4 +- noxfile.py | 2 +- testing/constraints-3.10.txt | 1 + tests/system/small/test_polars_execution.py | 76 +++++++++++++++++++++ 10 files changed, 157 insertions(+), 11 deletions(-) create mode 100644 bigframes/_importing.py create mode 100644 tests/system/small/test_polars_execution.py diff --git a/bigframes/_config/bigquery_options.py b/bigframes/_config/bigquery_options.py index d591ea85b3..09ffee95d4 100644 --- a/bigframes/_config/bigquery_options.py +++ b/bigframes/_config/bigquery_options.py @@ -22,6 +22,7 @@ import google.auth.credentials import requests.adapters +import bigframes._importing import bigframes.enums import bigframes.exceptions as bfe @@ -94,6 +95,7 @@ def __init__( requests_transport_adapters: Sequence[ Tuple[str, requests.adapters.BaseAdapter] ] = (), + enable_polars_execution: bool = False, ): self._credentials = credentials self._project = project @@ -113,6 +115,9 @@ def __init__( client_endpoints_override = {} self._client_endpoints_override = client_endpoints_override + if enable_polars_execution: + bigframes._importing.import_polars() + self._enable_polars_execution = enable_polars_execution @property def application_name(self) -> Optional[str]: @@ -424,3 +429,22 @@ def requests_transport_adapters( SESSION_STARTED_MESSAGE.format(attribute="requests_transport_adapters") ) self._requests_transport_adapters = value + + @property + def enable_polars_execution(self) -> bool: + """If True, will use polars to execute some simple query plans locally.""" + return self._enable_polars_execution + + @enable_polars_execution.setter + def enable_polars_execution(self, value: bool): + if self._session_started and self._enable_polars_execution != value: + raise ValueError( + SESSION_STARTED_MESSAGE.format(attribute="enable_polars_execution") + ) + if value is True: + msg = bfe.format_message( + "Polars execution is an experimental feature, and may not be stable. Must have polars installed." + ) + warnings.warn(msg, category=bfe.PreviewWarning) + bigframes._importing.import_polars() + self._enable_polars_execution = value diff --git a/bigframes/_importing.py b/bigframes/_importing.py new file mode 100644 index 0000000000..095a1d9c51 --- /dev/null +++ b/bigframes/_importing.py @@ -0,0 +1,30 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import importlib +from types import ModuleType + +from packaging import version + +# Keep this in sync with setup.py +POLARS_MIN_VERSION = version.Version("1.7.0") + + +def import_polars() -> ModuleType: + polars_module = importlib.import_module("polars") + imported_version = version.Version(polars_module.build_info()["version"]) + if imported_version < POLARS_MIN_VERSION: + raise ImportError( + f"Imported polars version: {imported_version} is below the minimum version: {POLARS_MIN_VERSION}" + ) + return polars_module diff --git a/bigframes/core/compile/polars/compiler.py b/bigframes/core/compile/polars/compiler.py index cc007623e1..6b76f3f53d 100644 --- a/bigframes/core/compile/polars/compiler.py +++ b/bigframes/core/compile/polars/compiler.py @@ -393,7 +393,7 @@ class PolarsCompiler: expr_compiler = PolarsExpressionCompiler() agg_compiler = PolarsAggregateCompiler() - def compile(self, array_value: bigframes.core.ArrayValue) -> pl.LazyFrame: + def compile(self, plan: nodes.BigFrameNode) -> pl.LazyFrame: if not polars_installed: raise ValueError( "Polars is not installed, cannot compile to polars engine." @@ -401,7 +401,7 @@ def compile(self, array_value: bigframes.core.ArrayValue) -> pl.LazyFrame: # TODO: Create standard way to configure BFET -> BFET rewrites # Polars has incomplete slice support in lazy mode - node = array_value.node + node = plan node = bigframes.core.rewrite.column_pruning(node) node = nodes.bottom_up(node, bigframes.core.rewrite.rewrite_slice) node = bigframes.core.rewrite.pull_out_window_order(node) diff --git a/bigframes/session/__init__.py b/bigframes/session/__init__.py index 13db6823c1..8cbcf8612e 100644 --- a/bigframes/session/__init__.py +++ b/bigframes/session/__init__.py @@ -255,6 +255,7 @@ def __init__( storage_manager=self._temp_storage_manager, strictly_ordered=self._strictly_ordered, metrics=self._metrics, + enable_polars_execution=context.enable_polars_execution, ) def __del__(self): diff --git a/bigframes/session/bq_caching_executor.py b/bigframes/session/bq_caching_executor.py index 9ad8da33a8..6750652bc2 100644 --- a/bigframes/session/bq_caching_executor.py +++ b/bigframes/session/bq_caching_executor.py @@ -41,7 +41,13 @@ import bigframes.core.tree_properties as tree_properties import bigframes.dtypes import bigframes.features -from bigframes.session import executor, loader, local_scan_executor, read_api_execution +from bigframes.session import ( + executor, + loader, + local_scan_executor, + read_api_execution, + semi_executor, +) import bigframes.session._io.bigquery as bq_io import bigframes.session.metrics import bigframes.session.planner @@ -147,6 +153,7 @@ def __init__( *, strictly_ordered: bool = True, metrics: Optional[bigframes.session.metrics.ExecutionMetrics] = None, + enable_polars_execution: bool = False, ): self.bqclient = bqclient self.storage_manager = storage_manager @@ -155,14 +162,21 @@ def __init__( self.metrics = metrics self.loader = loader self.bqstoragereadclient = bqstoragereadclient - # Simple left-to-right precedence for now - self._semi_executors = ( + self._enable_polars_execution = enable_polars_execution + self._semi_executors: Sequence[semi_executor.SemiExecutor] = ( read_api_execution.ReadApiSemiExecutor( bqstoragereadclient=bqstoragereadclient, project=self.bqclient.project, ), local_scan_executor.LocalScanExecutor(), ) + if enable_polars_execution: + from bigframes.session import polars_executor + + self._semi_executors = ( + *self._semi_executors, + polars_executor.PolarsExecutor(), + ) self._upload_lock = threading.Lock() def to_sql( @@ -637,8 +651,8 @@ def _execute_plan( """Just execute whatever plan as is, without further caching or decomposition.""" # First try to execute fast-paths if not output_spec.require_bq_table: - for semi_executor in self._semi_executors: - maybe_result = semi_executor.execute(plan, ordered=ordered, peek=peek) + for exec in self._semi_executors: + maybe_result = exec.execute(plan, ordered=ordered, peek=peek) if maybe_result: return maybe_result diff --git a/bigframes/session/polars_executor.py b/bigframes/session/polars_executor.py index b2f7f5ccd6..8d415032fb 100644 --- a/bigframes/session/polars_executor.py +++ b/bigframes/session/polars_executor.py @@ -73,7 +73,7 @@ def execute( # Note: Ignoring ordered flag, as just executing totally ordered is fine. try: lazy_frame: pl.LazyFrame = self._compiler.compile( - array_value.ArrayValue(plan) + array_value.ArrayValue(plan).node ) except Exception: return None diff --git a/bigframes/testing/polars_session.py b/bigframes/testing/polars_session.py index 7b898a9f00..3710c40eae 100644 --- a/bigframes/testing/polars_session.py +++ b/bigframes/testing/polars_session.py @@ -41,7 +41,7 @@ def peek( """ A 'peek' efficiently accesses a small number of rows in the dataframe. """ - lazy_frame: polars.LazyFrame = self.compiler.compile(array_value) + lazy_frame: polars.LazyFrame = self.compiler.compile(array_value.node) pa_table = lazy_frame.collect().limit(n_rows).to_arrow() # Currently, pyarrow types might not quite be exactly the ones in the bigframes schema. # Nullability may be different, and might use large versions of list, string datatypes. @@ -64,7 +64,7 @@ def execute( """ Execute the ArrayValue, storing the result to a temporary session-owned table. """ - lazy_frame: polars.LazyFrame = self.compiler.compile(array_value) + lazy_frame: polars.LazyFrame = self.compiler.compile(array_value.node) pa_table = lazy_frame.collect().to_arrow() # Currently, pyarrow types might not quite be exactly the ones in the bigframes schema. # Nullability may be different, and might use large versions of list, string datatypes. diff --git a/noxfile.py b/noxfile.py index 9346f26cba..96b59d6776 100644 --- a/noxfile.py +++ b/noxfile.py @@ -108,7 +108,7 @@ SYSTEM_TEST_EXTRAS: List[str] = [] SYSTEM_TEST_EXTRAS_BY_PYTHON: Dict[str, List[str]] = { "3.9": ["tests", "anywidget"], - "3.10": ["tests"], + "3.10": ["tests", "polars"], "3.12": ["tests", "scikit-learn", "polars", "anywidget"], "3.13": ["tests", "polars"], } diff --git a/testing/constraints-3.10.txt b/testing/constraints-3.10.txt index b11ab5a88d..12ad443aab 100644 --- a/testing/constraints-3.10.txt +++ b/testing/constraints-3.10.txt @@ -15,3 +15,4 @@ matplotlib==3.7.1 psutil==5.9.5 seaborn==0.13.1 traitlets==5.7.1 +polars==1.7.0 diff --git a/tests/system/small/test_polars_execution.py b/tests/system/small/test_polars_execution.py new file mode 100644 index 0000000000..0aed693b80 --- /dev/null +++ b/tests/system/small/test_polars_execution.py @@ -0,0 +1,76 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import pytest + +import bigframes +from bigframes.testing.utils import assert_pandas_df_equal + +polars = pytest.importorskip("polars", reason="polars is required for this test") + + +@pytest.fixture(scope="module") +def session_w_polars(): + context = bigframes.BigQueryOptions(location="US", enable_polars_execution=True) + session = bigframes.Session(context=context) + yield session + session.close() # close generated session at cleanup time + + +def test_polar_execution_sorted(session_w_polars, scalars_pandas_df_index): + execution_count_before = session_w_polars._metrics.execution_count + bf_df = session_w_polars.read_pandas(scalars_pandas_df_index) + + pd_result = scalars_pandas_df_index.sort_index(ascending=False)[ + ["int64_too", "bool_col"] + ] + bf_result = bf_df.sort_index(ascending=False)[["int64_too", "bool_col"]].to_pandas() + + assert session_w_polars._metrics.execution_count == execution_count_before + assert_pandas_df_equal(bf_result, pd_result) + + +def test_polar_execution_sorted_filtered(session_w_polars, scalars_pandas_df_index): + execution_count_before = session_w_polars._metrics.execution_count + bf_df = session_w_polars.read_pandas(scalars_pandas_df_index) + + pd_result = scalars_pandas_df_index.sort_index(ascending=False).dropna( + subset=["int64_col", "string_col"] + ) + bf_result = ( + bf_df.sort_index(ascending=False) + .dropna(subset=["int64_col", "string_col"]) + .to_pandas() + ) + + # Filter and isnull not supported by polar engine yet, so falls back to bq execution + assert session_w_polars._metrics.execution_count == (execution_count_before + 1) + assert_pandas_df_equal(bf_result, pd_result) + + +def test_polar_execution_unsupported_sql_fallback( + session_w_polars, scalars_pandas_df_index +): + execution_count_before = session_w_polars._metrics.execution_count + bf_df = session_w_polars.read_pandas(scalars_pandas_df_index) + + pd_df = scalars_pandas_df_index.copy() + pd_df["str_len_col"] = pd_df.string_col.str.len() + pd_result = pd_df + + bf_df["str_len_col"] = bf_df.string_col.str.len() + bf_result = bf_df.to_pandas() + + # str len not supported by polar engine yet, so falls back to bq execution + assert session_w_polars._metrics.execution_count == (execution_count_before + 1) + assert_pandas_df_equal(bf_result, pd_result) From 25684ff60367f49dd318d4677a7438abdc98bff9 Mon Sep 17 00:00:00 2001 From: Shuowei Li Date: Thu, 26 Jun 2025 12:25:22 -0700 Subject: [PATCH 15/28] feat: support item assignment in series (#1859) * Add item assignment for bigframes dataframe * testcase update --- bigframes/series.py | 4 ++ tests/system/small/test_series.py | 63 +++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+) diff --git a/bigframes/series.py b/bigframes/series.py index ae6cd7b2ad..ebc2913f78 100644 --- a/bigframes/series.py +++ b/bigframes/series.py @@ -1598,6 +1598,10 @@ def __getattr__(self, key: str): else: raise AttributeError(key) + def __setitem__(self, key, value) -> None: + """Set item using direct assignment, delegating to .loc indexer.""" + self.loc[key] = value + def _apply_aggregation( self, op: agg_ops.UnaryAggregateOp | agg_ops.NullaryAggregateOp ) -> Any: diff --git a/tests/system/small/test_series.py b/tests/system/small/test_series.py index 6760d63a20..d513b0e780 100644 --- a/tests/system/small/test_series.py +++ b/tests/system/small/test_series.py @@ -510,6 +510,69 @@ def test_series___getitem___with_default_index(scalars_dfs): assert bf_result == pd_result +@pytest.mark.parametrize( + ("index_col", "key", "value"), + ( + ("int64_too", 2, "new_string_value"), + ("string_col", "Hello, World!", "updated_value"), + ("int64_too", 0, None), + ), +) +def test_series___setitem__(scalars_dfs, index_col, key, value): + col_name = "string_col" + scalars_df, scalars_pandas_df = scalars_dfs + scalars_df = scalars_df.set_index(index_col, drop=False) + scalars_pandas_df = scalars_pandas_df.set_index(index_col, drop=False) + + bf_series = scalars_df[col_name] + pd_series = scalars_pandas_df[col_name].copy() + + bf_series[key] = value + pd_series[key] = value + + pd.testing.assert_series_equal(bf_series.to_pandas(), pd_series) + + +@pytest.mark.parametrize( + ("key", "value"), + ( + (0, 999), + (1, 888), + (0, None), + (-2345, 777), + ), +) +def test_series___setitem___with_int_key_numeric(scalars_dfs, key, value): + col_name = "int64_col" + index_col = "int64_too" + scalars_df, scalars_pandas_df = scalars_dfs + scalars_df = scalars_df.set_index(index_col, drop=False) + scalars_pandas_df = scalars_pandas_df.set_index(index_col, drop=False) + + bf_series = scalars_df[col_name] + pd_series = scalars_pandas_df[col_name].copy() + + bf_series[key] = value + pd_series[key] = value + + pd.testing.assert_series_equal(bf_series.to_pandas(), pd_series) + + +def test_series___setitem___with_default_index(scalars_dfs): + col_name = "float64_col" + key = 2 + value = 123.456 + scalars_df, scalars_pandas_df = scalars_dfs + + bf_series = scalars_df[col_name] + pd_series = scalars_pandas_df[col_name].copy() + + bf_series[key] = value + pd_series[key] = value + + assert bf_series.to_pandas().iloc[key] == pd_series.iloc[key] + + @pytest.mark.parametrize( ("col_name",), ( From 942e66c483c9afbb680a7af56c9e9a76172a33e1 Mon Sep 17 00:00:00 2001 From: TrevorBergeron Date: Fri, 27 Jun 2025 10:42:36 -0700 Subject: [PATCH 16/28] feat: Add size op support in local engine (#1865) --- bigframes/session/polars_executor.py | 6 +- .../system/small/engines/test_aggregation.py | 82 +++++++++++++++++++ 2 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 tests/system/small/engines/test_aggregation.py diff --git a/bigframes/session/polars_executor.py b/bigframes/session/polars_executor.py index 8d415032fb..24acda35dc 100644 --- a/bigframes/session/polars_executor.py +++ b/bigframes/session/polars_executor.py @@ -20,6 +20,7 @@ from bigframes.core import array_value, bigframe_node, expression, local_data, nodes import bigframes.operations +from bigframes.operations import aggregations as agg_ops from bigframes.session import executor, semi_executor if TYPE_CHECKING: @@ -32,9 +33,11 @@ nodes.ReversedNode, nodes.SelectionNode, nodes.SliceNode, + nodes.AggregateNode, ) _COMPATIBLE_SCALAR_OPS = () +_COMPATIBLE_AGG_OPS = (agg_ops.SizeOp, agg_ops.SizeUnaryOp) def _get_expr_ops(expr: expression.Expression) -> set[bigframes.operations.ScalarOp]: @@ -48,7 +51,8 @@ def _is_node_polars_executable(node: nodes.BigFrameNode): return False for expr in node._node_expressions: if isinstance(expr, expression.Aggregation): - return False + if not type(expr.op) in _COMPATIBLE_AGG_OPS: + return False if isinstance(expr, expression.Expression): if not _get_expr_ops(expr).issubset(_COMPATIBLE_SCALAR_OPS): return False diff --git a/tests/system/small/engines/test_aggregation.py b/tests/system/small/engines/test_aggregation.py new file mode 100644 index 0000000000..2c323a5f28 --- /dev/null +++ b/tests/system/small/engines/test_aggregation.py @@ -0,0 +1,82 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import pytest + +from bigframes.core import array_value, expression, identifiers, nodes +import bigframes.operations.aggregations as agg_ops +from bigframes.session import polars_executor +from bigframes.testing.engine_utils import assert_equivalence_execution + +pytest.importorskip("polars") + +# Polars used as reference as its fast and local. Generally though, prefer gbq engine where they disagree. +REFERENCE_ENGINE = polars_executor.PolarsExecutor() + + +@pytest.mark.parametrize("engine", ["polars", "bq"], indirect=True) +def test_engines_aggregate_size( + scalars_array_value: array_value.ArrayValue, + engine, +): + node = nodes.AggregateNode( + scalars_array_value.node, + aggregations=( + ( + expression.NullaryAggregation(agg_ops.SizeOp()), + identifiers.ColumnId("size_op"), + ), + ( + expression.UnaryAggregation( + agg_ops.SizeUnaryOp(), expression.deref("string_col") + ), + identifiers.ColumnId("unary_size_op"), + ), + ), + ) + assert_equivalence_execution(node, REFERENCE_ENGINE, engine) + + +@pytest.mark.parametrize("engine", ["polars", "bq"], indirect=True) +@pytest.mark.parametrize( + "grouping_cols", + [ + ["bool_col"], + ["string_col", "int64_col"], + ["date_col"], + ["datetime_col"], + ["timestamp_col"], + ["bytes_col"], + ], +) +def test_engines_grouped_aggregate( + scalars_array_value: array_value.ArrayValue, engine, grouping_cols +): + node = nodes.AggregateNode( + scalars_array_value.node, + aggregations=( + ( + expression.NullaryAggregation(agg_ops.SizeOp()), + identifiers.ColumnId("size_op"), + ), + ( + expression.UnaryAggregation( + agg_ops.SizeUnaryOp(), expression.deref("string_col") + ), + identifiers.ColumnId("unary_size_op"), + ), + ), + by_column_ids=tuple(expression.deref(id) for id in grouping_cols), + ) + assert_equivalence_execution(node, REFERENCE_ENGINE, engine) From c5d251a1d454bb4ef55ea9905faeadd646a23b14 Mon Sep 17 00:00:00 2001 From: Shuowei Li Date: Fri, 27 Jun 2025 13:03:21 -0700 Subject: [PATCH 17/28] feat: support index item assign in Series (#1868) * implement index item assignment * add testcase * final touch up --- bigframes/core/indexes/base.py | 5 +++++ tests/system/small/test_index.py | 26 ++++++++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/bigframes/core/indexes/base.py b/bigframes/core/indexes/base.py index bc8b47d216..f653b8700b 100644 --- a/bigframes/core/indexes/base.py +++ b/bigframes/core/indexes/base.py @@ -174,6 +174,11 @@ def dtypes(self) -> pandas.Series: index=typing.cast(typing.Tuple, self._block.index.names), ) + def __setitem__(self, key, value) -> None: + """Index objects are immutable. Use Index constructor to create + modified Index.""" + raise TypeError("Index does not support mutable operations") + @property def size(self) -> int: return self.shape[0] diff --git a/tests/system/small/test_index.py b/tests/system/small/test_index.py index 3b9854be26..c7e316a9d2 100644 --- a/tests/system/small/test_index.py +++ b/tests/system/small/test_index.py @@ -499,3 +499,29 @@ def test_index_item_with_empty(session): with pytest.raises(ValueError, match=re.escape(expected_message)): bf_idx_empty.item() + + +@pytest.mark.parametrize( + ("key", "value"), + [ + (0, "string_value"), + (1, 42), + ("label", None), + (-1, 3.14), + ], +) +def test_index_setitem_different_types(scalars_dfs, key, value): + """Tests that custom Index setitem raises TypeError.""" + scalars_df, _ = scalars_dfs + index = scalars_df.index + + with pytest.raises(TypeError, match="Index does not support mutable operations"): + index[key] = value + + +def test_custom_index_setitem_error(): + """Tests that custom Index setitem raises TypeError.""" + custom_index = bpd.Index([1, 2, 3, 4, 5], name="custom") + + with pytest.raises(TypeError, match="Index does not support mutable operations"): + custom_index[2] = 999 From ed75cd99ff373181a6d9576f5cf8efa83bb394db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Swe=C3=B1a=20=28Swast=29?= Date: Fri, 27 Jun 2025 16:24:46 -0500 Subject: [PATCH 18/28] chore: add benchmarks for read_gbq_colab (#1860) * chore: add benchmarks for read_gbq_colab * correct project id * exclude error too * Delete tests/benchmark/read_gbq_colab/first_page.py_percentile_99.error * explain column selection for groupby --- tests/benchmark/.gitignore | 6 ++ .../read_gbq_colab/aggregate_output.py | 72 +++++++++++++++++++ tests/benchmark/read_gbq_colab/config.jsonl | 10 +++ tests/benchmark/read_gbq_colab/dry_run.py | 48 +++++++++++++ .../benchmark/read_gbq_colab/filter_output.py | 60 ++++++++++++++++ tests/benchmark/read_gbq_colab/first_page.py | 53 ++++++++++++++ tests/benchmark/read_gbq_colab/last_page.py | 54 ++++++++++++++ tests/benchmark/read_gbq_colab/sort_output.py | 64 +++++++++++++++++ tests/benchmark/utils.py | 3 + 9 files changed, 370 insertions(+) create mode 100644 tests/benchmark/.gitignore create mode 100644 tests/benchmark/read_gbq_colab/aggregate_output.py create mode 100644 tests/benchmark/read_gbq_colab/config.jsonl create mode 100644 tests/benchmark/read_gbq_colab/dry_run.py create mode 100644 tests/benchmark/read_gbq_colab/filter_output.py create mode 100644 tests/benchmark/read_gbq_colab/first_page.py create mode 100644 tests/benchmark/read_gbq_colab/last_page.py create mode 100644 tests/benchmark/read_gbq_colab/sort_output.py diff --git a/tests/benchmark/.gitignore b/tests/benchmark/.gitignore new file mode 100644 index 0000000000..f1bf042bf7 --- /dev/null +++ b/tests/benchmark/.gitignore @@ -0,0 +1,6 @@ +*.bytesprocessed +*.bq_exec_time_seconds +*.error +*.local_exec_time_seconds +*.query_char_count +*.slotmillis diff --git a/tests/benchmark/read_gbq_colab/aggregate_output.py b/tests/benchmark/read_gbq_colab/aggregate_output.py new file mode 100644 index 0000000000..b612e2998c --- /dev/null +++ b/tests/benchmark/read_gbq_colab/aggregate_output.py @@ -0,0 +1,72 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://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. +import pathlib + +import benchmark.utils as utils + +import bigframes.session + +PAGE_SIZE = utils.READ_GBQ_COLAB_PAGE_SIZE + + +def aggregate_output( + *, project_id, dataset_id, table_id, session: bigframes.session.Session +): + # TODO(tswast): Support alternative query if table_id is a local DataFrame, + # e.g. "{local_inline}" or "{local_large}" + df = session._read_gbq_colab( + f"SELECT * FROM `{project_id}`.{dataset_id}.{table_id}" + ) + + # Simulate getting the first page, since we'll always do that first in the UI. + df.shape + next(iter(df.to_pandas_batches(page_size=PAGE_SIZE))) + + # To simulate very small rows that can only fit a boolean, + # some tables don't have an integer column. If an integer column is available, + # we prefer to group by that to get a more realistic number of groups. + group_column = "col_int64_1" + if group_column not in df.columns: + group_column = "col_bool_0" + + # Simulate the user aggregating by a column and visualizing those results + df_aggregated = ( + df.assign(rounded=df[group_column].astype("Int64").round(-9)) + .groupby("rounded") + .sum() + ) + + df_aggregated.shape + next(iter(df_aggregated.to_pandas_batches(page_size=PAGE_SIZE))) + + +if __name__ == "__main__": + ( + project_id, + dataset_id, + table_id, + session, + suffix, + ) = utils.get_configuration(include_table_id=True) + current_path = pathlib.Path(__file__).absolute() + + utils.get_execution_time( + aggregate_output, + current_path, + suffix, + project_id=project_id, + dataset_id=dataset_id, + table_id=table_id, + session=session, + ) diff --git a/tests/benchmark/read_gbq_colab/config.jsonl b/tests/benchmark/read_gbq_colab/config.jsonl new file mode 100644 index 0000000000..6f1ddf4a5f --- /dev/null +++ b/tests/benchmark/read_gbq_colab/config.jsonl @@ -0,0 +1,10 @@ +{"benchmark_suffix": "percentile_09", "project_id": "bigframes-dev-perf", "dataset_id": "read_gbq_colab_benchmark", "table_id": "percentile_09", "ordered": false} +{"benchmark_suffix": "percentile_19", "project_id": "bigframes-dev-perf", "dataset_id": "read_gbq_colab_benchmark", "table_id": "percentile_19", "ordered": false} +{"benchmark_suffix": "percentile_29", "project_id": "bigframes-dev-perf", "dataset_id": "read_gbq_colab_benchmark", "table_id": "percentile_29", "ordered": false} +{"benchmark_suffix": "percentile_39", "project_id": "bigframes-dev-perf", "dataset_id": "read_gbq_colab_benchmark", "table_id": "percentile_39", "ordered": false} +{"benchmark_suffix": "percentile_49", "project_id": "bigframes-dev-perf", "dataset_id": "read_gbq_colab_benchmark", "table_id": "percentile_49", "ordered": false} +{"benchmark_suffix": "percentile_59", "project_id": "bigframes-dev-perf", "dataset_id": "read_gbq_colab_benchmark", "table_id": "percentile_59", "ordered": false} +{"benchmark_suffix": "percentile_69", "project_id": "bigframes-dev-perf", "dataset_id": "read_gbq_colab_benchmark", "table_id": "percentile_69", "ordered": false} +{"benchmark_suffix": "percentile_79", "project_id": "bigframes-dev-perf", "dataset_id": "read_gbq_colab_benchmark", "table_id": "percentile_79", "ordered": false} +{"benchmark_suffix": "percentile_89", "project_id": "bigframes-dev-perf", "dataset_id": "read_gbq_colab_benchmark", "table_id": "percentile_89", "ordered": false} +{"benchmark_suffix": "percentile_99", "project_id": "bigframes-dev-perf", "dataset_id": "read_gbq_colab_benchmark", "table_id": "percentile_99", "ordered": false} diff --git a/tests/benchmark/read_gbq_colab/dry_run.py b/tests/benchmark/read_gbq_colab/dry_run.py new file mode 100644 index 0000000000..c2de1b7cc4 --- /dev/null +++ b/tests/benchmark/read_gbq_colab/dry_run.py @@ -0,0 +1,48 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://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. +import pathlib + +import benchmark.utils as utils + +import bigframes.session + + +def dry_run(*, project_id, dataset_id, table_id, session: bigframes.session.Session): + # TODO(tswast): Support alternative query if table_id is a local DataFrame, + # e.g. "{local_inline}" or "{local_large}" + session._read_gbq_colab( + f"SELECT * FROM `{project_id}`.{dataset_id}.{table_id}", + dry_run=True, + ) + + +if __name__ == "__main__": + ( + project_id, + dataset_id, + table_id, + session, + suffix, + ) = utils.get_configuration(include_table_id=True) + current_path = pathlib.Path(__file__).absolute() + + utils.get_execution_time( + dry_run, + current_path, + suffix, + project_id=project_id, + dataset_id=dataset_id, + table_id=table_id, + session=session, + ) diff --git a/tests/benchmark/read_gbq_colab/filter_output.py b/tests/benchmark/read_gbq_colab/filter_output.py new file mode 100644 index 0000000000..7945d9f0c6 --- /dev/null +++ b/tests/benchmark/read_gbq_colab/filter_output.py @@ -0,0 +1,60 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://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. +import pathlib + +import benchmark.utils as utils + +import bigframes.session + +PAGE_SIZE = utils.READ_GBQ_COLAB_PAGE_SIZE + + +def filter_output( + *, project_id, dataset_id, table_id, session: bigframes.session.Session +): + # TODO(tswast): Support alternative query if table_id is a local DataFrame, + # e.g. "{local_inline}" or "{local_large}" + df = session._read_gbq_colab( + f"SELECT * FROM `{project_id}`.{dataset_id}.{table_id}" + ) + + # Simulate getting the first page, since we'll always do that first in the UI. + df.shape + next(iter(df.to_pandas_batches(page_size=PAGE_SIZE))) + + # Simulate the user filtering by a column and visualizing those results + df_filtered = df[df["col_bool_0"]] + df_filtered.shape + next(iter(df_filtered.to_pandas_batches(page_size=PAGE_SIZE))) + + +if __name__ == "__main__": + ( + project_id, + dataset_id, + table_id, + session, + suffix, + ) = utils.get_configuration(include_table_id=True) + current_path = pathlib.Path(__file__).absolute() + + utils.get_execution_time( + filter_output, + current_path, + suffix, + project_id=project_id, + dataset_id=dataset_id, + table_id=table_id, + session=session, + ) diff --git a/tests/benchmark/read_gbq_colab/first_page.py b/tests/benchmark/read_gbq_colab/first_page.py new file mode 100644 index 0000000000..2df9990d22 --- /dev/null +++ b/tests/benchmark/read_gbq_colab/first_page.py @@ -0,0 +1,53 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://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. +import pathlib + +import benchmark.utils as utils + +import bigframes.session + +PAGE_SIZE = utils.READ_GBQ_COLAB_PAGE_SIZE + + +def first_page(*, project_id, dataset_id, table_id, session: bigframes.session.Session): + # TODO(tswast): Support alternative query if table_id is a local DataFrame, + # e.g. "{local_inline}" or "{local_large}" + df = session._read_gbq_colab( + f"SELECT * FROM `{project_id}`.{dataset_id}.{table_id}" + ) + + # Get number of rows (to calculate number of pages) and the first page. + df.shape + next(iter(df.to_pandas_batches(page_size=PAGE_SIZE))) + + +if __name__ == "__main__": + ( + project_id, + dataset_id, + table_id, + session, + suffix, + ) = utils.get_configuration(include_table_id=True) + current_path = pathlib.Path(__file__).absolute() + + utils.get_execution_time( + first_page, + current_path, + suffix, + project_id=project_id, + dataset_id=dataset_id, + table_id=table_id, + session=session, + ) diff --git a/tests/benchmark/read_gbq_colab/last_page.py b/tests/benchmark/read_gbq_colab/last_page.py new file mode 100644 index 0000000000..ad785a29e8 --- /dev/null +++ b/tests/benchmark/read_gbq_colab/last_page.py @@ -0,0 +1,54 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://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. +import pathlib + +import benchmark.utils as utils + +import bigframes.session + +PAGE_SIZE = utils.READ_GBQ_COLAB_PAGE_SIZE + + +def last_page(*, project_id, dataset_id, table_id, session: bigframes.session.Session): + # TODO(tswast): Support alternative query if table_id is a local DataFrame, + # e.g. "{local_inline}" or "{local_large}" + df = session._read_gbq_colab( + f"SELECT * FROM `{project_id}`.{dataset_id}.{table_id}" + ) + + # Get number of rows (to calculate number of pages) and then all pages. + df.shape + for _ in df.to_pandas_batches(page_size=PAGE_SIZE): + pass + + +if __name__ == "__main__": + ( + project_id, + dataset_id, + table_id, + session, + suffix, + ) = utils.get_configuration(include_table_id=True) + current_path = pathlib.Path(__file__).absolute() + + utils.get_execution_time( + last_page, + current_path, + suffix, + project_id=project_id, + dataset_id=dataset_id, + table_id=table_id, + session=session, + ) diff --git a/tests/benchmark/read_gbq_colab/sort_output.py b/tests/benchmark/read_gbq_colab/sort_output.py new file mode 100644 index 0000000000..997de5683d --- /dev/null +++ b/tests/benchmark/read_gbq_colab/sort_output.py @@ -0,0 +1,64 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://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. +import pathlib + +import benchmark.utils as utils + +import bigframes.session + +PAGE_SIZE = utils.READ_GBQ_COLAB_PAGE_SIZE + + +def sort_output( + *, project_id, dataset_id, table_id, session: bigframes.session.Session +): + # TODO(tswast): Support alternative query if table_id is a local DataFrame, + # e.g. "{local_inline}" or "{local_large}" + df = session._read_gbq_colab( + f"SELECT * FROM `{project_id}`.{dataset_id}.{table_id}" + ) + + # Simulate getting the first page, since we'll always do that first in the UI. + df.shape + next(iter(df.to_pandas_batches(page_size=PAGE_SIZE))) + + # Simulate the user sorting by a column and visualizing those results + sort_column = "col_int64_1" + if sort_column not in df.columns: + sort_column = "col_bool_0" + + df_sorted = df.sort_values(sort_column) + df_sorted.shape + next(iter(df_sorted.to_pandas_batches(page_size=PAGE_SIZE))) + + +if __name__ == "__main__": + ( + project_id, + dataset_id, + table_id, + session, + suffix, + ) = utils.get_configuration(include_table_id=True) + current_path = pathlib.Path(__file__).absolute() + + utils.get_execution_time( + sort_output, + current_path, + suffix, + project_id=project_id, + dataset_id=dataset_id, + table_id=table_id, + session=session, + ) diff --git a/tests/benchmark/utils.py b/tests/benchmark/utils.py index 887d54dba2..48357ddde7 100644 --- a/tests/benchmark/utils.py +++ b/tests/benchmark/utils.py @@ -17,6 +17,8 @@ import bigframes +READ_GBQ_COLAB_PAGE_SIZE = 100 + def get_configuration(include_table_id=False): parser = argparse.ArgumentParser() @@ -94,6 +96,7 @@ def _str_to_bool(value): def _initialize_session(ordered: bool): + # TODO(tswast): add a flag to enable the polars semi-executor. context = bigframes.BigQueryOptions( location="US", ordering_mode="strict" if ordered else "partial" ) From cc339e9938129cac896460e3a794b3ec8479fa4a Mon Sep 17 00:00:00 2001 From: TrevorBergeron Date: Fri, 27 Jun 2025 15:01:34 -0700 Subject: [PATCH 19/28] fix: Fix bug selecting column repeatedly (#1858) --- bigframes/core/array_value.py | 25 ++++++++++++++++++++----- bigframes/core/blocks.py | 5 ++++- tests/system/small/test_dataframe.py | 9 +++++++++ 3 files changed, 33 insertions(+), 6 deletions(-) diff --git a/bigframes/core/array_value.py b/bigframes/core/array_value.py index 4b05781cb7..b47637cb59 100644 --- a/bigframes/core/array_value.py +++ b/bigframes/core/array_value.py @@ -330,12 +330,27 @@ def create_constant( return self.project_to_id(ex.const(value, dtype)) - def select_columns(self, column_ids: typing.Sequence[str]) -> ArrayValue: + def select_columns( + self, column_ids: typing.Sequence[str], allow_renames: bool = False + ) -> ArrayValue: # This basically just drops and reorders columns - logically a no-op except as a final step - selections = ( - bigframes.core.nodes.AliasedRef.identity(ids.ColumnId(col_id)) - for col_id in column_ids - ) + selections = [] + seen = set() + + for id in column_ids: + if id not in seen: + ref = nodes.AliasedRef.identity(ids.ColumnId(id)) + elif allow_renames: + ref = nodes.AliasedRef( + ex.deref(id), ids.ColumnId(bigframes.core.guid.generate_guid()) + ) + else: + raise ValueError( + "Must set allow_renames=True to select columns repeatedly" + ) + selections.append(ref) + seen.add(id) + return ArrayValue( nodes.SelectionNode( child=self.node, diff --git a/bigframes/core/blocks.py b/bigframes/core/blocks.py index 675e8c8b7a..1426459912 100644 --- a/bigframes/core/blocks.py +++ b/bigframes/core/blocks.py @@ -1210,7 +1210,10 @@ def select_column(self, id: str) -> Block: return self.select_columns([id]) def select_columns(self, ids: typing.Sequence[str]) -> Block: - expr = self._expr.select_columns([*self.index_columns, *ids]) + # Allow renames as may end up selecting same columns multiple times + expr = self._expr.select_columns( + [*self.index_columns, *ids], allow_renames=True + ) col_labels = self._get_labels_for_columns(ids) return Block(expr, self.index_columns, col_labels, self.index.names) diff --git a/tests/system/small/test_dataframe.py b/tests/system/small/test_dataframe.py index b037c6f371..d5446efcd0 100644 --- a/tests/system/small/test_dataframe.py +++ b/tests/system/small/test_dataframe.py @@ -3408,6 +3408,15 @@ def test__dir__with_rename(scalars_dfs): assert "drop" in results +def test_loc_select_columns_w_repeats(scalars_df_index, scalars_pandas_df_index): + bf_result = scalars_df_index[["int64_col", "int64_col", "int64_too"]].to_pandas() + pd_result = scalars_pandas_df_index[["int64_col", "int64_col", "int64_too"]] + pd.testing.assert_frame_equal( + bf_result, + pd_result, + ) + + @pytest.mark.parametrize( ("start", "stop", "step"), [ From 6c3f68a757ff12382f5da6c67da5667f9bcb1f8b Mon Sep 17 00:00:00 2001 From: Shuowei Li Date: Fri, 27 Jun 2025 15:35:35 -0700 Subject: [PATCH 20/28] upload logo to repo (#1869) --- third_party/logo/colab-logo.png | Bin 0 -> 1539 bytes third_party/logo/github-logo.png | Bin 0 -> 1714 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 third_party/logo/colab-logo.png create mode 100644 third_party/logo/github-logo.png diff --git a/third_party/logo/colab-logo.png b/third_party/logo/colab-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..75740a2b6ae87543bd97bb5a50bb8ac05b5c0d74 GIT binary patch literal 1539 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 literal 0 HcmV?d00001 From 024be5989044f02f3ea7a50e47b5619893a5182f Mon Sep 17 00:00:00 2001 From: Shenyang Cai Date: Fri, 27 Jun 2025 15:47:24 -0700 Subject: [PATCH 21/28] refactor: rename scalars_types_df to scalar_types_df (#1862) --- tests/unit/core/compile/sqlglot/conftest.py | 10 +++++----- .../sqlglot/expressions/test_binary_compiler.py | 12 ++++++------ .../unit/core/compile/sqlglot/test_compile_concat.py | 4 ++-- .../unit/core/compile/sqlglot/test_compile_filter.py | 4 ++-- .../core/compile/sqlglot/test_compile_readlocal.py | 4 ++-- .../core/compile/sqlglot/test_compile_readtable.py | 12 ++++++------ 6 files changed, 23 insertions(+), 23 deletions(-) diff --git a/tests/unit/core/compile/sqlglot/conftest.py b/tests/unit/core/compile/sqlglot/conftest.py index 6d5fac8184..645daddd46 100644 --- a/tests/unit/core/compile/sqlglot/conftest.py +++ b/tests/unit/core/compile/sqlglot/conftest.py @@ -46,9 +46,9 @@ def _create_compiler_session(table_name, table_schema): @pytest.fixture(scope="session") -def compiler_session(scalars_types_table_schema): +def compiler_session(scalar_types_table_schema): """Compiler session for scalar types.""" - return _create_compiler_session("scalar_types", scalars_types_table_schema) + return _create_compiler_session("scalar_types", scalar_types_table_schema) @pytest.fixture(scope="session") @@ -72,7 +72,7 @@ def compiler_session_w_json_types(json_types_table_schema): @pytest.fixture(scope="session") -def scalars_types_table_schema() -> typing.Sequence[bigquery.SchemaField]: +def scalar_types_table_schema() -> typing.Sequence[bigquery.SchemaField]: return [ bigquery.SchemaField("bool_col", "BOOLEAN"), bigquery.SchemaField("bytes_col", "BYTES"), @@ -92,7 +92,7 @@ def scalars_types_table_schema() -> typing.Sequence[bigquery.SchemaField]: @pytest.fixture(scope="session") -def scalars_types_df(compiler_session) -> bpd.DataFrame: +def scalar_types_df(compiler_session) -> bpd.DataFrame: """Returns a BigFrames DataFrame containing all scalar types and using the `rowindex` column as the index.""" bf_df = compiler_session.read_gbq_table("bigframes-dev.sqlglot_test.scalar_types") @@ -101,7 +101,7 @@ def scalars_types_df(compiler_session) -> bpd.DataFrame: @pytest.fixture(scope="session") -def scalars_types_pandas_df() -> pd.DataFrame: +def scalar_types_pandas_df() -> pd.DataFrame: """Returns a pandas DataFrame containing all scalar types and using the `rowindex` column as the index.""" # TODO: add tests for empty dataframes diff --git a/tests/unit/core/compile/sqlglot/expressions/test_binary_compiler.py b/tests/unit/core/compile/sqlglot/expressions/test_binary_compiler.py index 180d43d771..f3c96e9253 100644 --- a/tests/unit/core/compile/sqlglot/expressions/test_binary_compiler.py +++ b/tests/unit/core/compile/sqlglot/expressions/test_binary_compiler.py @@ -19,24 +19,24 @@ pytest.importorskip("pytest_snapshot") -def test_add_numeric(scalars_types_df: bpd.DataFrame, snapshot): - bf_df = scalars_types_df[["int64_col"]] +def test_add_numeric(scalar_types_df: bpd.DataFrame, snapshot): + bf_df = scalar_types_df[["int64_col"]] bf_df["int64_col"] = bf_df["int64_col"] + bf_df["int64_col"] snapshot.assert_match(bf_df.sql, "out.sql") -def test_add_numeric_w_scalar(scalars_types_df: bpd.DataFrame, snapshot): - bf_df = scalars_types_df[["int64_col"]] +def test_add_numeric_w_scalar(scalar_types_df: bpd.DataFrame, snapshot): + bf_df = scalar_types_df[["int64_col"]] bf_df["int64_col"] = bf_df["int64_col"] + 1 snapshot.assert_match(bf_df.sql, "out.sql") -def test_add_string(scalars_types_df: bpd.DataFrame, snapshot): - bf_df = scalars_types_df[["string_col"]] +def test_add_string(scalar_types_df: bpd.DataFrame, snapshot): + bf_df = scalar_types_df[["string_col"]] bf_df["string_col"] = bf_df["string_col"] + "a" diff --git a/tests/unit/core/compile/sqlglot/test_compile_concat.py b/tests/unit/core/compile/sqlglot/test_compile_concat.py index ec7e83a4b0..79f73d3113 100644 --- a/tests/unit/core/compile/sqlglot/test_compile_concat.py +++ b/tests/unit/core/compile/sqlglot/test_compile_concat.py @@ -22,11 +22,11 @@ def test_compile_concat( - scalars_types_pandas_df: pd.DataFrame, compiler_session: bigframes.Session, snapshot + scalar_types_pandas_df: pd.DataFrame, compiler_session: bigframes.Session, snapshot ): # TODO: concat two same dataframes, which SQL does not get reused. # TODO: concat dataframes from a gbq table but trigger a windows compiler. - df1 = bpd.DataFrame(scalars_types_pandas_df, session=compiler_session) + df1 = bpd.DataFrame(scalar_types_pandas_df, session=compiler_session) df1 = df1[["rowindex", "int64_col", "string_col"]] concat_df = bpd.concat([df1, df1]) snapshot.assert_match(concat_df.sql, "out.sql") diff --git a/tests/unit/core/compile/sqlglot/test_compile_filter.py b/tests/unit/core/compile/sqlglot/test_compile_filter.py index 03b54f289a..0afb5eb45b 100644 --- a/tests/unit/core/compile/sqlglot/test_compile_filter.py +++ b/tests/unit/core/compile/sqlglot/test_compile_filter.py @@ -19,7 +19,7 @@ pytest.importorskip("pytest_snapshot") -def test_compile_filter(scalars_types_df: bpd.DataFrame, snapshot): - bf_df = scalars_types_df[["rowindex", "int64_col"]] +def test_compile_filter(scalar_types_df: bpd.DataFrame, snapshot): + bf_df = scalar_types_df[["rowindex", "int64_col"]] bf_filter = bf_df[bf_df["rowindex"] >= 1] snapshot.assert_match(bf_filter.sql, "out.sql") diff --git a/tests/unit/core/compile/sqlglot/test_compile_readlocal.py b/tests/unit/core/compile/sqlglot/test_compile_readlocal.py index bd27ad450e..7307fd9b4e 100644 --- a/tests/unit/core/compile/sqlglot/test_compile_readlocal.py +++ b/tests/unit/core/compile/sqlglot/test_compile_readlocal.py @@ -22,9 +22,9 @@ def test_compile_readlocal( - scalars_types_pandas_df: pd.DataFrame, compiler_session: bigframes.Session, snapshot + scalar_types_pandas_df: pd.DataFrame, compiler_session: bigframes.Session, snapshot ): - bf_df = bpd.DataFrame(scalars_types_pandas_df, session=compiler_session) + bf_df = bpd.DataFrame(scalar_types_pandas_df, session=compiler_session) snapshot.assert_match(bf_df.sql, "out.sql") diff --git a/tests/unit/core/compile/sqlglot/test_compile_readtable.py b/tests/unit/core/compile/sqlglot/test_compile_readtable.py index d3b5140471..a5692e5fbf 100644 --- a/tests/unit/core/compile/sqlglot/test_compile_readtable.py +++ b/tests/unit/core/compile/sqlglot/test_compile_readtable.py @@ -19,8 +19,8 @@ pytest.importorskip("pytest_snapshot") -def test_compile_readtable(scalars_types_df: bpd.DataFrame, snapshot): - snapshot.assert_match(scalars_types_df.sql, "out.sql") +def test_compile_readtable(scalar_types_df: bpd.DataFrame, snapshot): + snapshot.assert_match(scalar_types_df.sql, "out.sql") def test_compile_readtable_w_repeated_types(repeated_types_df: bpd.DataFrame, snapshot): @@ -37,13 +37,13 @@ def test_compile_readtable_w_json_types(json_types_df: bpd.DataFrame, snapshot): snapshot.assert_match(json_types_df.sql, "out.sql") -def test_compile_readtable_w_ordering(scalars_types_df: bpd.DataFrame, snapshot): - bf_df = scalars_types_df[["int64_col"]] +def test_compile_readtable_w_ordering(scalar_types_df: bpd.DataFrame, snapshot): + bf_df = scalar_types_df[["int64_col"]] bf_df = bf_df.sort_values("int64_col") snapshot.assert_match(bf_df.sql, "out.sql") -def test_compile_readtable_w_limit(scalars_types_df: bpd.DataFrame, snapshot): - bf_df = scalars_types_df[["int64_col"]] +def test_compile_readtable_w_limit(scalar_types_df: bpd.DataFrame, snapshot): + bf_df = scalar_types_df[["int64_col"]] bf_df = bf_df.sort_index().head(10) snapshot.assert_match(bf_df.sql, "out.sql") From e3c06b4a07d0669a42460d081f1582b681ae3dd5 Mon Sep 17 00:00:00 2001 From: Shuowei Li Date: Fri, 27 Jun 2025 16:41:43 -0700 Subject: [PATCH 22/28] docs: changed broken logo (#1866) * docs: change broken logo * use real url --- notebooks/experimental/ai_operators.ipynb | 4 ++-- notebooks/generative_ai/bq_dataframes_ai_forecast.ipynb | 4 ++-- .../generative_ai/bq_dataframes_llm_code_generation.ipynb | 4 ++-- notebooks/generative_ai/bq_dataframes_llm_kmeans.ipynb | 4 ++-- .../generative_ai/bq_dataframes_llm_output_schema.ipynb | 4 ++-- .../generative_ai/bq_dataframes_llm_vector_search.ipynb | 4 ++-- .../bq_dataframes_ml_drug_name_generation.ipynb | 4 ++-- notebooks/getting_started/bq_dataframes_template.ipynb | 4 ++-- .../getting_started/getting_started_bq_dataframes.ipynb | 6 +++--- .../getting_started/ml_fundamentals_bq_dataframes.ipynb | 4 ++-- notebooks/ml/bq_dataframes_ml_linear_regression.ipynb | 4 ++-- notebooks/ml/bq_dataframes_ml_linear_regression_big.ipynb | 4 ++-- notebooks/multimodal/multimodal_dataframe.ipynb | 4 ++-- .../remote_function_vertex_claude_model.ipynb | 4 ++-- .../visualization/bq_dataframes_covid_line_graphs.ipynb | 4 ++-- notebooks/visualization/tutorial.ipynb | 4 ++-- 16 files changed, 33 insertions(+), 33 deletions(-) diff --git a/notebooks/experimental/ai_operators.ipynb b/notebooks/experimental/ai_operators.ipynb index 07e20f6bbd..977f7b9d74 100644 --- a/notebooks/experimental/ai_operators.ipynb +++ b/notebooks/experimental/ai_operators.ipynb @@ -35,12 +35,12 @@ "\n", " \n", " \n", - " \"Colab Run in Colab\n", + " \"Colab Run in Colab\n", " \n", " \n", " \n", " \n", - " \"GitHub\n", + " \"GitHub\n", " View on GitHub\n", " \n", " \n", diff --git a/notebooks/generative_ai/bq_dataframes_ai_forecast.ipynb b/notebooks/generative_ai/bq_dataframes_ai_forecast.ipynb index 7734bd815d..5f6dede106 100644 --- a/notebooks/generative_ai/bq_dataframes_ai_forecast.ipynb +++ b/notebooks/generative_ai/bq_dataframes_ai_forecast.ipynb @@ -31,12 +31,12 @@ "\n", " \n", " \n", - " \"Colab Run in Colab\n", + " \"Colab Run in Colab\n", " \n", " \n", " \n", " \n", - " \"GitHub\n", + " \"GitHub\n", " View on GitHub\n", " \n", " \n", diff --git a/notebooks/generative_ai/bq_dataframes_llm_code_generation.ipynb b/notebooks/generative_ai/bq_dataframes_llm_code_generation.ipynb index edb864613c..68e10cb5ed 100644 --- a/notebooks/generative_ai/bq_dataframes_llm_code_generation.ipynb +++ b/notebooks/generative_ai/bq_dataframes_llm_code_generation.ipynb @@ -35,12 +35,12 @@ "\n", " \n", " \n", - " \"Colab Run in Colab\n", + " \"Colab Run in Colab\n", " \n", " \n", " \n", " \n", - " \"GitHub\n", + " \"GitHub\n", " View on GitHub\n", " \n", " \n", diff --git a/notebooks/generative_ai/bq_dataframes_llm_kmeans.ipynb b/notebooks/generative_ai/bq_dataframes_llm_kmeans.ipynb index 9b05e1ab02..bc55096942 100644 --- a/notebooks/generative_ai/bq_dataframes_llm_kmeans.ipynb +++ b/notebooks/generative_ai/bq_dataframes_llm_kmeans.ipynb @@ -32,12 +32,12 @@ "\n", " \n", " \n", - " \"Colab Run in Colab\n", + " \"Colab Run in Colab\n", " \n", " \n", " \n", " \n", - " \"GitHub\n", + " \"GitHub\n", " View on GitHub\n", " \n", " \n", diff --git a/notebooks/generative_ai/bq_dataframes_llm_output_schema.ipynb b/notebooks/generative_ai/bq_dataframes_llm_output_schema.ipynb index 04ea0571df..70714c823c 100644 --- a/notebooks/generative_ai/bq_dataframes_llm_output_schema.ipynb +++ b/notebooks/generative_ai/bq_dataframes_llm_output_schema.ipynb @@ -31,12 +31,12 @@ "\n", " \n", " \n", - " \"Colab Run in Colab\n", + " \"Colab Run in Colab\n", " \n", " \n", " \n", " \n", - " \"GitHub\n", + " \"GitHub\n", " View on GitHub\n", " \n", " \n", diff --git a/notebooks/generative_ai/bq_dataframes_llm_vector_search.ipynb b/notebooks/generative_ai/bq_dataframes_llm_vector_search.ipynb index 15929fd666..b964117b67 100644 --- a/notebooks/generative_ai/bq_dataframes_llm_vector_search.ipynb +++ b/notebooks/generative_ai/bq_dataframes_llm_vector_search.ipynb @@ -35,12 +35,12 @@ "\n", " \n", " \n", - " \"Colab Run in Colab\n", + " \"Colab Run in Colab\n", " \n", " \n", " \n", " \n", - " \"GitHub\n", + " \"GitHub\n", " View on GitHub\n", " \n", " \n", diff --git a/notebooks/generative_ai/bq_dataframes_ml_drug_name_generation.ipynb b/notebooks/generative_ai/bq_dataframes_ml_drug_name_generation.ipynb index 413e473c2f..3220bbf6cd 100644 --- a/notebooks/generative_ai/bq_dataframes_ml_drug_name_generation.ipynb +++ b/notebooks/generative_ai/bq_dataframes_ml_drug_name_generation.ipynb @@ -34,12 +34,12 @@ "\n", " \n", " \n", diff --git a/notebooks/getting_started/bq_dataframes_template.ipynb b/notebooks/getting_started/bq_dataframes_template.ipynb index ae772d035e..e8002fd611 100644 --- a/notebooks/getting_started/bq_dataframes_template.ipynb +++ b/notebooks/getting_started/bq_dataframes_template.ipynb @@ -35,12 +35,12 @@ "\n", " \n", " \n", diff --git a/notebooks/getting_started/getting_started_bq_dataframes.ipynb b/notebooks/getting_started/getting_started_bq_dataframes.ipynb index ccecd09cb9..384f3b9c10 100644 --- a/notebooks/getting_started/getting_started_bq_dataframes.ipynb +++ b/notebooks/getting_started/getting_started_bq_dataframes.ipynb @@ -35,12 +35,12 @@ "\n", " \n", " \n", @@ -1658,7 +1658,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.12.10" + "version": "3.10.15" } }, "nbformat": 4, diff --git a/notebooks/getting_started/ml_fundamentals_bq_dataframes.ipynb b/notebooks/getting_started/ml_fundamentals_bq_dataframes.ipynb index d95447f7e5..3370e94713 100644 --- a/notebooks/getting_started/ml_fundamentals_bq_dataframes.ipynb +++ b/notebooks/getting_started/ml_fundamentals_bq_dataframes.ipynb @@ -35,12 +35,12 @@ "\n", " \n", " \n", diff --git a/notebooks/ml/bq_dataframes_ml_linear_regression.ipynb b/notebooks/ml/bq_dataframes_ml_linear_regression.ipynb index 4123dd0e1c..00aa7a347c 100644 --- a/notebooks/ml/bq_dataframes_ml_linear_regression.ipynb +++ b/notebooks/ml/bq_dataframes_ml_linear_regression.ipynb @@ -36,12 +36,12 @@ "\n", " \n", " \n", diff --git a/notebooks/ml/bq_dataframes_ml_linear_regression_big.ipynb b/notebooks/ml/bq_dataframes_ml_linear_regression_big.ipynb index 0c5106f8f4..5c016f9157 100644 --- a/notebooks/ml/bq_dataframes_ml_linear_regression_big.ipynb +++ b/notebooks/ml/bq_dataframes_ml_linear_regression_big.ipynb @@ -36,12 +36,12 @@ "\n", " \n", " \n", diff --git a/notebooks/multimodal/multimodal_dataframe.ipynb b/notebooks/multimodal/multimodal_dataframe.ipynb index fbe074b0d0..f6f80b0009 100644 --- a/notebooks/multimodal/multimodal_dataframe.ipynb +++ b/notebooks/multimodal/multimodal_dataframe.ipynb @@ -33,12 +33,12 @@ "\n", " \n", " \n", diff --git a/notebooks/remote_functions/remote_function_vertex_claude_model.ipynb b/notebooks/remote_functions/remote_function_vertex_claude_model.ipynb index 605f879bc7..9792c90205 100644 --- a/notebooks/remote_functions/remote_function_vertex_claude_model.ipynb +++ b/notebooks/remote_functions/remote_function_vertex_claude_model.ipynb @@ -10,12 +10,12 @@ "\n", " \n", " \n", diff --git a/notebooks/visualization/bq_dataframes_covid_line_graphs.ipynb b/notebooks/visualization/bq_dataframes_covid_line_graphs.ipynb index b98589c2ae..f0dd5eb678 100644 --- a/notebooks/visualization/bq_dataframes_covid_line_graphs.ipynb +++ b/notebooks/visualization/bq_dataframes_covid_line_graphs.ipynb @@ -35,12 +35,12 @@ "\n", " \n", " \n", diff --git a/notebooks/visualization/tutorial.ipynb b/notebooks/visualization/tutorial.ipynb index 96aff12452..0923e03bc7 100644 --- a/notebooks/visualization/tutorial.ipynb +++ b/notebooks/visualization/tutorial.ipynb @@ -33,12 +33,12 @@ "\n", " \n", " \n", From fab3c387b2ad66043244fa813a366e613b41c60f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Swe=C3=B1a=20=28Swast=29?= Date: Mon, 30 Jun 2025 08:33:41 -0500 Subject: [PATCH 23/28] fix: generate GoogleSQL instead of legacy SQL data types for `dry_run=True` from `bpd._read_gbq_colab` with local pandas DataFrame (#1867) * fix: generate GoogleSQL instead of legacy SQL data types for `dry_run=True` from `bpd._read_gbq_colab` with local pandas DataFrame * finish adding system tests * add unit tests * map legacy sql types to googlesql * fix more unit tests * fix mypy * fix python 3.9 tests shapely --- bigframes/core/tools/bigquery_schema.py | 13 +- bigframes/pandas/io/api.py | 51 ++- tests/system/small/pandas/io/__init__.py | 13 + tests/system/small/pandas/io/api/__init__.py | 13 + .../pandas/io/api/test_read_gbq_colab.py | 329 ++++++++++++++++++ tests/unit/core/test_pyformat.py | 315 ++++++++++++++++- tests/unit/core/tools/test_bigquery_schema.py | 26 +- 7 files changed, 713 insertions(+), 47 deletions(-) create mode 100644 tests/system/small/pandas/io/__init__.py create mode 100644 tests/system/small/pandas/io/api/__init__.py create mode 100644 tests/system/small/pandas/io/api/test_read_gbq_colab.py diff --git a/bigframes/core/tools/bigquery_schema.py b/bigframes/core/tools/bigquery_schema.py index 227a69e0f7..eef7364a1b 100644 --- a/bigframes/core/tools/bigquery_schema.py +++ b/bigframes/core/tools/bigquery_schema.py @@ -18,6 +18,12 @@ import google.cloud.bigquery +_LEGACY_TO_GOOGLESQL_TYPES = { + "BOOLEAN": "BOOL", + "INTEGER": "INT64", + "FLOAT": "FLOAT64", +} + def _type_to_sql(field: google.cloud.bigquery.SchemaField): """Turn the type information of the field into SQL. @@ -26,7 +32,12 @@ def _type_to_sql(field: google.cloud.bigquery.SchemaField): """ if field.field_type.casefold() in ("record", "struct"): return _to_struct(field.fields) - return field.field_type + + # Map from legacy SQL names (the ones used in the BigQuery schema API) to + # the GoogleSQL types. Importantly, FLOAT is from legacy SQL, but not valid + # in GoogleSQL. See internal issue b/428190014. + type_ = _LEGACY_TO_GOOGLESQL_TYPES.get(field.field_type.upper(), field.field_type) + return type_ def _field_to_sql(field: google.cloud.bigquery.SchemaField): diff --git a/bigframes/pandas/io/api.py b/bigframes/pandas/io/api.py index 608eaf5a82..003de5913f 100644 --- a/bigframes/pandas/io/api.py +++ b/bigframes/pandas/io/api.py @@ -218,8 +218,27 @@ def read_gbq( read_gbq.__doc__ = inspect.getdoc(bigframes.session.Session.read_gbq) +def _run_read_gbq_colab_sessionless_dry_run( + query: str, + *, + pyformat_args: Dict[str, Any], +) -> pandas.Series: + """Run a dry_run without a session.""" + + query_formatted = bigframes.core.pyformat.pyformat( + query, + pyformat_args=pyformat_args, + dry_run=True, + ) + bqclient = _get_bqclient() + job = _dry_run(query_formatted, bqclient) + return dry_runs.get_query_stats_with_inferred_dtypes(job, (), ()) + + def _try_read_gbq_colab_sessionless_dry_run( - create_query: Callable[[], str], + query: str, + *, + pyformat_args: Dict[str, Any], ) -> Optional[pandas.Series]: """Run a dry_run without a session, only if the session hasn't yet started.""" @@ -230,10 +249,9 @@ def _try_read_gbq_colab_sessionless_dry_run( # to local data and not any BigQuery tables. with _default_location_lock: if not config.options.bigquery._session_started: - bqclient = _get_bqclient() - query = create_query() - job = _dry_run(query, bqclient) - return dry_runs.get_query_stats_with_inferred_dtypes(job, (), ()) + return _run_read_gbq_colab_sessionless_dry_run( + query, pyformat_args=pyformat_args + ) # Explicitly return None to indicate that we didn't run the dry run query. return None @@ -286,21 +304,13 @@ def _read_gbq_colab( if pyformat_args is None: pyformat_args = {} - # Delay formatting the query with the special "session-less" logic. This - # avoids doing unnecessary work if the session already has a location or has - # already started. - create_query = functools.partial( - bigframes.core.pyformat.pyformat, - query_or_table, - pyformat_args=pyformat_args, - dry_run=True, - ) - # Only try to set the global location if it's not a dry run. We don't want # to bind to a location too early. This is especially important if the query # only refers to local data and not any BigQuery tables. if dry_run: - result = _try_read_gbq_colab_sessionless_dry_run(create_query) + result = _try_read_gbq_colab_sessionless_dry_run( + query_or_table, pyformat_args=pyformat_args + ) if result is not None: return result @@ -309,6 +319,15 @@ def _read_gbq_colab( # started. That means we can safely call the "real" _read_gbq_colab, # which generates slightly nicer SQL. else: + # Delay formatting the query with the special "session-less" logic. This + # avoids doing unnecessary work if the session already has a location or has + # already started. + create_query = functools.partial( + bigframes.core.pyformat.pyformat, + query_or_table, + pyformat_args=pyformat_args, + dry_run=True, + ) _set_default_session_location_if_possible_deferred_query(create_query) return global_session.with_default_session( diff --git a/tests/system/small/pandas/io/__init__.py b/tests/system/small/pandas/io/__init__.py new file mode 100644 index 0000000000..0a2669d7a2 --- /dev/null +++ b/tests/system/small/pandas/io/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/tests/system/small/pandas/io/api/__init__.py b/tests/system/small/pandas/io/api/__init__.py new file mode 100644 index 0000000000..0a2669d7a2 --- /dev/null +++ b/tests/system/small/pandas/io/api/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/tests/system/small/pandas/io/api/test_read_gbq_colab.py b/tests/system/small/pandas/io/api/test_read_gbq_colab.py new file mode 100644 index 0000000000..6e848ed9ea --- /dev/null +++ b/tests/system/small/pandas/io/api/test_read_gbq_colab.py @@ -0,0 +1,329 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import annotations + +import datetime +import decimal + +import db_dtypes # type: ignore +import geopandas # type: ignore +import numpy +import pandas +import pyarrow +import pytest +import shapely.geometry # type: ignore + +from bigframes.pandas.io import api as module_under_test + + +@pytest.mark.parametrize( + ("df_pd",), + ( + # Regression tests for b/428190014. + # + # Test every BigQuery type we support, especially those where the legacy + # SQL type name differs from the GoogleSQL type name. + # + # See: + # https://cloud.google.com/bigquery/docs/reference/standard-sql/data-types + # and compare to the legacy types at + # https://cloud.google.com/bigquery/docs/data-types + pytest.param( + pandas.DataFrame( + { + "ints": pandas.Series( + [[1], [2], [3]], + dtype=pandas.ArrowDtype(pyarrow.list_(pyarrow.int64())), + ), + "floats": pandas.Series( + [[1.0], [2.0], [3.0]], + dtype=pandas.ArrowDtype(pyarrow.list_(pyarrow.float64())), + ), + } + ), + id="arrays", + ), + pytest.param( + pandas.DataFrame( + { + "bool": pandas.Series([True, False, True], dtype="bool"), + "boolean": pandas.Series([True, None, True], dtype="boolean"), + "object": pandas.Series([True, None, True], dtype="object"), + "arrow": pandas.Series( + [True, None, True], dtype=pandas.ArrowDtype(pyarrow.bool_()) + ), + } + ), + id="bools", + ), + pytest.param( + pandas.DataFrame( + { + "bytes": pandas.Series([b"a", b"b", b"c"], dtype=numpy.bytes_), + "object": pandas.Series([b"a", None, b"c"], dtype="object"), + "arrow": pandas.Series( + [b"a", None, b"c"], dtype=pandas.ArrowDtype(pyarrow.binary()) + ), + } + ), + id="bytes", + ), + pytest.param( + pandas.DataFrame( + { + "object": pandas.Series( + [ + datetime.date(2023, 11, 23), + None, + datetime.date(1970, 1, 1), + ], + dtype="object", + ), + "arrow": pandas.Series( + [ + datetime.date(2023, 11, 23), + None, + datetime.date(1970, 1, 1), + ], + dtype=pandas.ArrowDtype(pyarrow.date32()), + ), + } + ), + id="dates", + ), + pytest.param( + pandas.DataFrame( + { + "object": pandas.Series( + [ + datetime.datetime(2023, 11, 23, 13, 14, 15), + None, + datetime.datetime(1970, 1, 1, 0, 0, 0), + ], + dtype="object", + ), + "datetime64": pandas.Series( + [ + datetime.datetime(2023, 11, 23, 13, 14, 15), + None, + datetime.datetime(1970, 1, 1, 0, 0, 0), + ], + dtype="datetime64[us]", + ), + "arrow": pandas.Series( + [ + datetime.datetime(2023, 11, 23, 13, 14, 15), + None, + datetime.datetime(1970, 1, 1, 0, 0, 0), + ], + dtype=pandas.ArrowDtype(pyarrow.timestamp("us")), + ), + } + ), + id="datetimes", + ), + pytest.param( + pandas.DataFrame( + { + "object": pandas.Series( + [ + shapely.geometry.Point(145.0, -37.8), + None, + shapely.geometry.Point(-122.3, 47.6), + ], + dtype="object", + ), + "geopandas": geopandas.GeoSeries( + [ + shapely.geometry.Point(145.0, -37.8), + None, + shapely.geometry.Point(-122.3, 47.6), + ] + ), + } + ), + id="geographys", + ), + # TODO(tswast): Add INTERVAL once BigFrames supports it. + pytest.param( + pandas.DataFrame( + { + # TODO(tswast): Is there an equivalent object type we can use here? + # TODO(tswast): Add built-in Arrow extension type + "db_dtypes": pandas.Series( + ["{}", None, "123"], + dtype=pandas.ArrowDtype(db_dtypes.JSONArrowType()), + ), + } + ), + id="jsons", + ), + pytest.param( + pandas.DataFrame( + { + "int64": pandas.Series([1, 2, 3], dtype="int64"), + "Int64": pandas.Series([1, None, 3], dtype="Int64"), + "object": pandas.Series([1, None, 3], dtype="object"), + "arrow": pandas.Series( + [1, None, 3], dtype=pandas.ArrowDtype(pyarrow.int64()) + ), + } + ), + id="ints", + ), + pytest.param( + pandas.DataFrame( + { + "object": pandas.Series( + [decimal.Decimal("1.23"), None, decimal.Decimal("4.56")], + dtype="object", + ), + "arrow": pandas.Series( + [decimal.Decimal("1.23"), None, decimal.Decimal("4.56")], + dtype=pandas.ArrowDtype(pyarrow.decimal128(38, 9)), + ), + } + ), + id="numerics", + ), + pytest.param( + pandas.DataFrame( + { + # TODO(tswast): Add object type for BIGNUMERIC. Can bigframes disambiguate? + "arrow": pandas.Series( + [decimal.Decimal("1.23"), None, decimal.Decimal("4.56")], + dtype=pandas.ArrowDtype(pyarrow.decimal256(76, 38)), + ), + } + ), + id="bignumerics", + ), + pytest.param( + pandas.DataFrame( + { + "float64": pandas.Series([1.23, None, 4.56], dtype="float64"), + "Float64": pandas.Series([1.23, None, 4.56], dtype="Float64"), + "object": pandas.Series([1.23, None, 4.56], dtype="object"), + "arrow": pandas.Series( + [1.23, None, 4.56], dtype=pandas.ArrowDtype(pyarrow.float64()) + ), + } + ), + id="floats", + ), + # TODO(tswast): Add RANGE once BigFrames supports it. + pytest.param( + pandas.DataFrame( + { + "string": pandas.Series(["a", "b", "c"], dtype="string[python]"), + "object": pandas.Series(["a", None, "c"], dtype="object"), + "arrow": pandas.Series(["a", None, "c"], dtype="string[pyarrow]"), + } + ), + id="strings", + ), + pytest.param( + pandas.DataFrame( + { + # TODO(tswast): Add object type for STRUCT? How to tell apart from JSON? + "arrow": pandas.Series( + [{"a": 1, "b": 1.0, "c": "c"}], + dtype=pandas.ArrowDtype( + pyarrow.struct( + [ + ("a", pyarrow.int64()), + ("b", pyarrow.float64()), + ("c", pyarrow.string()), + ] + ) + ), + ), + } + ), + id="structs", + ), + pytest.param( + pandas.DataFrame( + { + "object": pandas.Series( + [ + datetime.time(0, 0, 0), + None, + datetime.time(13, 7, 11), + ], + dtype="object", + ), + "arrow": pandas.Series( + [ + datetime.time(0, 0, 0), + None, + datetime.time(13, 7, 11), + ], + dtype=pandas.ArrowDtype(pyarrow.time64("us")), + ), + } + ), + id="times", + ), + pytest.param( + pandas.DataFrame( + { + "object": pandas.Series( + [ + datetime.datetime( + 2023, 11, 23, 13, 14, 15, tzinfo=datetime.timezone.utc + ), + None, + datetime.datetime( + 1970, 1, 1, 0, 0, 0, tzinfo=datetime.timezone.utc + ), + ], + dtype="object", + ), + "datetime64": pandas.Series( + [ + datetime.datetime(2023, 11, 23, 13, 14, 15), + None, + datetime.datetime(1970, 1, 1, 0, 0, 0), + ], + dtype="datetime64[us]", + ).dt.tz_localize("UTC"), + "arrow": pandas.Series( + [ + datetime.datetime( + 2023, 11, 23, 13, 14, 15, tzinfo=datetime.timezone.utc + ), + None, + datetime.datetime( + 1970, 1, 1, 0, 0, 0, tzinfo=datetime.timezone.utc + ), + ], + dtype=pandas.ArrowDtype(pyarrow.timestamp("us", "UTC")), + ), + } + ), + id="timestamps", + ), + ), +) +def test_read_gbq_colab_sessionless_dry_run_generates_valid_sql_for_local_dataframe( + df_pd: pandas.DataFrame, +): + # This method will fail with an exception if it receives invalid SQL. + result = module_under_test._run_read_gbq_colab_sessionless_dry_run( + query="SELECT * FROM {df_pd}", + pyformat_args={"df_pd": df_pd}, + ) + assert isinstance(result, pandas.Series) diff --git a/tests/unit/core/test_pyformat.py b/tests/unit/core/test_pyformat.py index 05110d8485..447ce37766 100644 --- a/tests/unit/core/test_pyformat.py +++ b/tests/unit/core/test_pyformat.py @@ -19,13 +19,19 @@ from __future__ import annotations +import datetime import decimal from typing import Any, Dict, List +import db_dtypes # type: ignore +import geopandas # type: ignore import google.cloud.bigquery import google.cloud.bigquery.table +import numpy import pandas +import pyarrow import pytest +import shapely.geometry # type: ignore from bigframes.core import pyformat from bigframes.testing import mocks @@ -91,42 +97,313 @@ def test_pyformat_with_no_variables(session): pytest.param( # Empty columns default to floating point, just like pandas. pandas.DataFrame({"empty column": []}), - "STRUCT<`empty column` FLOAT>", + "STRUCT<`empty column` FLOAT64>", id="empty column", ), + # Regression tests for b/428190014. + # + # Test every BigQuery type we support, especially those where the legacy + # SQL type name differs from the GoogleSQL type name. + # + # See: + # https://cloud.google.com/bigquery/docs/reference/standard-sql/data-types + # and compare to the legacy types at + # https://cloud.google.com/bigquery/docs/data-types + # + # Test these against the real BigQuery dry run API in + # tests/system/small/pandas/io/api/test_read_gbq_colab.py pytest.param( pandas.DataFrame( { - "col1": [1, 2, 3], - "col2": ["a", "b", "c"], - "col3": [ - decimal.Decimal(1), - decimal.Decimal(2), - decimal.Decimal(3), - ], + "ints": pandas.Series( + [[1], [2], [3]], + dtype=pandas.ArrowDtype(pyarrow.list_(pyarrow.int64())), + ), + "floats": pandas.Series( + [[1.0], [2.0], [3.0]], + dtype=pandas.ArrowDtype(pyarrow.list_(pyarrow.float64())), + ), } ), - "STRUCT<`col1` INTEGER, `col2` STRING, `col3` NUMERIC>", - id="scalars", + "STRUCT<`ints` ARRAY, `floats` ARRAY>", + id="arrays", ), pytest.param( pandas.DataFrame( - {"array col": [[1, 2, 3]], "another array": [["a", "b", "c"]]} + { + "bool": pandas.Series([True, False, True], dtype="bool"), + "boolean": pandas.Series([True, None, True], dtype="boolean"), + "object": pandas.Series([True, None, True], dtype="object"), + "arrow": pandas.Series( + [True, None, True], dtype=pandas.ArrowDtype(pyarrow.bool_()) + ), + } ), - "STRUCT<`array col` ARRAY, `another array` ARRAY>", - id="arrays", + "STRUCT<`bool` BOOL, `boolean` BOOL, `object` BOOL, `arrow` BOOL>", + id="bools", ), pytest.param( pandas.DataFrame( { - "struct col": [ - {"subfield": {"subsubfield": 1}, "subfield2": 2}, - ], + "bytes": pandas.Series([b"a", b"b", b"c"], dtype=numpy.bytes_), + "object": pandas.Series([b"a", None, b"c"], dtype="object"), + "arrow": pandas.Series( + [b"a", None, b"c"], dtype=pandas.ArrowDtype(pyarrow.binary()) + ), + } + ), + "STRUCT<`bytes` BYTES, `object` BYTES, `arrow` BYTES>", + id="bytes", + ), + pytest.param( + pandas.DataFrame( + { + "object": pandas.Series( + [ + datetime.date(2023, 11, 23), + None, + datetime.date(1970, 1, 1), + ], + dtype="object", + ), + "arrow": pandas.Series( + [ + datetime.date(2023, 11, 23), + None, + datetime.date(1970, 1, 1), + ], + dtype=pandas.ArrowDtype(pyarrow.date32()), + ), + } + ), + "STRUCT<`object` DATE, `arrow` DATE>", + id="dates", + ), + pytest.param( + pandas.DataFrame( + { + "object": pandas.Series( + [ + datetime.datetime(2023, 11, 23, 13, 14, 15), + None, + datetime.datetime(1970, 1, 1, 0, 0, 0), + ], + dtype="object", + ), + "datetime64": pandas.Series( + [ + datetime.datetime(2023, 11, 23, 13, 14, 15), + None, + datetime.datetime(1970, 1, 1, 0, 0, 0), + ], + dtype="datetime64[us]", + ), + "arrow": pandas.Series( + [ + datetime.datetime(2023, 11, 23, 13, 14, 15), + None, + datetime.datetime(1970, 1, 1, 0, 0, 0), + ], + dtype=pandas.ArrowDtype(pyarrow.timestamp("us")), + ), + } + ), + "STRUCT<`object` DATETIME, `datetime64` DATETIME, `arrow` DATETIME>", + id="datetimes", + ), + pytest.param( + pandas.DataFrame( + { + "object": pandas.Series( + [ + shapely.geometry.Point(145.0, -37.8), + None, + shapely.geometry.Point(-122.3, 47.6), + ], + dtype="object", + ), + "geopandas": geopandas.GeoSeries( + [ + shapely.geometry.Point(145.0, -37.8), + None, + shapely.geometry.Point(-122.3, 47.6), + ] + ), + } + ), + "STRUCT<`object` GEOGRAPHY, `geopandas` GEOGRAPHY>", + id="geographys", + ), + # TODO(tswast): Add INTERVAL once BigFrames supports it. + pytest.param( + pandas.DataFrame( + { + # TODO(tswast): Is there an equivalent object type we can use here? + # TODO(tswast): Add built-in Arrow extension type + "db_dtypes": pandas.Series( + ["{}", None, "123"], + dtype=pandas.ArrowDtype(db_dtypes.JSONArrowType()), + ), + } + ), + "STRUCT<`db_dtypes` JSON>", + id="jsons", + ), + pytest.param( + pandas.DataFrame( + { + "int64": pandas.Series([1, 2, 3], dtype="int64"), + "Int64": pandas.Series([1, None, 3], dtype="Int64"), + "object": pandas.Series([1, None, 3], dtype="object"), + "arrow": pandas.Series( + [1, None, 3], dtype=pandas.ArrowDtype(pyarrow.int64()) + ), + } + ), + "STRUCT<`int64` INT64, `Int64` INT64, `object` INT64, `arrow` INT64>", + id="ints", + ), + pytest.param( + pandas.DataFrame( + { + "object": pandas.Series( + [decimal.Decimal("1.23"), None, decimal.Decimal("4.56")], + dtype="object", + ), + "arrow": pandas.Series( + [decimal.Decimal("1.23"), None, decimal.Decimal("4.56")], + dtype=pandas.ArrowDtype(pyarrow.decimal128(38, 9)), + ), + } + ), + "STRUCT<`object` NUMERIC, `arrow` NUMERIC>", + id="numerics", + ), + pytest.param( + pandas.DataFrame( + { + # TODO(tswast): Add object type for BIGNUMERIC. Can bigframes disambiguate? + "arrow": pandas.Series( + [decimal.Decimal("1.23"), None, decimal.Decimal("4.56")], + dtype=pandas.ArrowDtype(pyarrow.decimal256(76, 38)), + ), + } + ), + "STRUCT<`arrow` BIGNUMERIC>", + id="bignumerics", + ), + pytest.param( + pandas.DataFrame( + { + "float64": pandas.Series([1.23, None, 4.56], dtype="float64"), + "Float64": pandas.Series([1.23, None, 4.56], dtype="Float64"), + "object": pandas.Series([1.23, None, 4.56], dtype="object"), + "arrow": pandas.Series( + [1.23, None, 4.56], dtype=pandas.ArrowDtype(pyarrow.float64()) + ), } ), - "STRUCT<`struct col` STRUCT<`subfield` STRUCT<`subsubfield` INTEGER>, `subfield2` INTEGER>>", + "STRUCT<`float64` FLOAT64, `Float64` FLOAT64, `object` FLOAT64, `arrow` FLOAT64>", + id="floats", + ), + # TODO(tswast): Add RANGE once BigFrames supports it. + pytest.param( + pandas.DataFrame( + { + "string": pandas.Series(["a", "b", "c"], dtype="string[python]"), + "object": pandas.Series(["a", None, "c"], dtype="object"), + "arrow": pandas.Series(["a", None, "c"], dtype="string[pyarrow]"), + } + ), + "STRUCT<`string` STRING, `object` STRING, `arrow` STRING>", + id="strings", + ), + pytest.param( + pandas.DataFrame( + { + # TODO(tswast): Add object type for STRUCT? How to tell apart from JSON? + "arrow": pandas.Series( + [{"a": 1, "b": 1.0, "c": "c"}], + dtype=pandas.ArrowDtype( + pyarrow.struct( + [ + ("a", pyarrow.int64()), + ("b", pyarrow.float64()), + ("c", pyarrow.string()), + ] + ) + ), + ), + } + ), + "STRUCT<`arrow` STRUCT<`a` INT64, `b` FLOAT64, `c` STRING>>", id="structs", ), + pytest.param( + pandas.DataFrame( + { + "object": pandas.Series( + [ + datetime.time(0, 0, 0), + None, + datetime.time(13, 7, 11), + ], + dtype="object", + ), + "arrow": pandas.Series( + [ + datetime.time(0, 0, 0), + None, + datetime.time(13, 7, 11), + ], + dtype=pandas.ArrowDtype(pyarrow.time64("us")), + ), + } + ), + "STRUCT<`object` TIME, `arrow` TIME>", + id="times", + ), + pytest.param( + pandas.DataFrame( + { + "object": pandas.Series( + [ + datetime.datetime( + 2023, 11, 23, 13, 14, 15, tzinfo=datetime.timezone.utc + ), + None, + datetime.datetime( + 1970, 1, 1, 0, 0, 0, tzinfo=datetime.timezone.utc + ), + ], + dtype="object", + ), + "datetime64": pandas.Series( + [ + datetime.datetime(2023, 11, 23, 13, 14, 15), + None, + datetime.datetime(1970, 1, 1, 0, 0, 0), + ], + dtype="datetime64[us]", + ).dt.tz_localize("UTC"), + "arrow": pandas.Series( + [ + datetime.datetime( + 2023, 11, 23, 13, 14, 15, tzinfo=datetime.timezone.utc + ), + None, + datetime.datetime( + 1970, 1, 1, 0, 0, 0, tzinfo=datetime.timezone.utc + ), + ], + dtype=pandas.ArrowDtype(pyarrow.timestamp("us", "UTC")), + ), + } + ), + "STRUCT<`object` TIMESTAMP, `datetime64` TIMESTAMP, `arrow` TIMESTAMP>", + id="timestamps", + ), + # More complicated edge cases: pytest.param( pandas.DataFrame( { @@ -135,14 +412,14 @@ def test_pyformat_with_no_variables(session): ], } ), - "STRUCT<`array of struct col` ARRAY, `subfield2` INTEGER>>>", + "STRUCT<`array of struct col` ARRAY, `subfield2` INT64>>>", id="array_of_structs", ), pytest.param( pandas.DataFrame({"c1": [1, 2, 3], "c2": ["a", "b", "c"]}).rename( columns={"c1": "c", "c2": "c"} ), - "STRUCT<`c` INTEGER, `c_1` STRING>", + "STRUCT<`c` INT64, `c_1` STRING>", id="duplicate_column_names", ), ), diff --git a/tests/unit/core/tools/test_bigquery_schema.py b/tests/unit/core/tools/test_bigquery_schema.py index a5b0087801..aed8ae0323 100644 --- a/tests/unit/core/tools/test_bigquery_schema.py +++ b/tests/unit/core/tools/test_bigquery_schema.py @@ -9,9 +9,11 @@ "field, expected_sql", [ # Simple types - (bigquery.SchemaField("test_field", "INTEGER"), "INTEGER"), + # Note: the REST API will return Legacy SQL data types, but we need to + # map to GoogleSQL. See internal issue b/428190014. + (bigquery.SchemaField("test_field", "INTEGER"), "INT64"), (bigquery.SchemaField("test_field", "STRING"), "STRING"), - (bigquery.SchemaField("test_field", "BOOLEAN"), "BOOLEAN"), + (bigquery.SchemaField("test_field", "BOOLEAN"), "BOOL"), # RECORD/STRUCT types with nested fields directly ( bigquery.SchemaField( @@ -30,7 +32,7 @@ bigquery.SchemaField("another", "BOOLEAN"), ), ), - "STRUCT<`sub_field` INTEGER, `another` BOOLEAN>", + "STRUCT<`sub_field` INT64, `another` BOOL>", ), # Array is handled by _field_to_sql, instead. (bigquery.SchemaField("test_field", "NUMERIC", mode="REPEATED"), "NUMERIC"), @@ -54,7 +56,9 @@ def test_type_to_sql(field, expected_sql): "field, expected_sql", [ # Simple field - (bigquery.SchemaField("id", "INTEGER", "NULLABLE"), "`id` INTEGER"), + # Note: the REST API will return Legacy SQL data types, but we need to + # map to GoogleSQL. See internal issue b/428190014. + (bigquery.SchemaField("id", "INTEGER", "NULLABLE"), "`id` INT64"), (bigquery.SchemaField("name", "STRING", "NULLABLE"), "`name` STRING"), # Repeated field (bigquery.SchemaField("tags", "STRING", "REPEATED"), "`tags` ARRAY"), @@ -69,7 +73,7 @@ def test_type_to_sql(field, expected_sql): bigquery.SchemaField("zip", "INTEGER"), ), ), - "`addresses` ARRAY>", + "`addresses` ARRAY>", ), # Simple STRUCT ( @@ -82,7 +86,7 @@ def test_type_to_sql(field, expected_sql): bigquery.SchemaField("city", "STRING"), ), ), - "`person` STRUCT<`age` INTEGER, `city` STRING>", + "`person` STRUCT<`age` INT64, `city` STRING>", ), ], ) @@ -102,7 +106,7 @@ def test_field_to_sql(field, expected_sql): bigquery.SchemaField("id", "INTEGER"), bigquery.SchemaField("name", "STRING"), ), - "STRUCT<`id` INTEGER, `name` STRING>", + "STRUCT<`id` INT64, `name` STRING>", ), # Nested RECORD/STRUCT ( @@ -118,7 +122,7 @@ def test_field_to_sql(field, expected_sql): ), ), ), - "STRUCT<`item_id` INTEGER, `details` STRUCT<`price` NUMERIC, `currency` STRING>>", + "STRUCT<`item_id` INT64, `details` STRUCT<`price` NUMERIC, `currency` STRING>>", ), # Repeated field ( @@ -143,7 +147,7 @@ def test_field_to_sql(field, expected_sql): ), bigquery.SchemaField("timestamp", "TIMESTAMP"), ), - "STRUCT<`event_name` STRING, `participants` ARRAY>>, `timestamp` TIMESTAMP>", + "STRUCT<`event_name` STRING, `participants` ARRAY>>, `timestamp` TIMESTAMP>", ), ], ) @@ -163,7 +167,7 @@ def test_to_struct(bqschema, expected_sql): bigquery.SchemaField("id", "INTEGER"), bigquery.SchemaField("name", "STRING"), ), - "UNNEST(ARRAY>[])", + "UNNEST(ARRAY>[])", ), # Complex schema with nested and repeated fields ( @@ -179,7 +183,7 @@ def test_to_struct(bqschema, expected_sql): ), ), ), - "UNNEST(ARRAY>>>[])", + "UNNEST(ARRAY>>>[])", ), ], ) From 633bf98fde33264be4fc9d7454e541c560589152 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Swe=C3=B1a=20=28Swast=29?= Date: Mon, 30 Jun 2025 08:34:04 -0500 Subject: [PATCH 24/28] feat: add `bpd.read_arrow` to convert an Arrow object into a bigframes DataFrame (#1855) * feat: Add read_arrow methods to Session and pandas Adds `read_arrow` methods to `bigframes.session.Session` and `bigframes.pandas.read_arrow` for creating BigQuery DataFrames DataFrames from PyArrow Tables. The implementation refactors existing logic from `bigframes.session._io.bigquery.read_gbq_query` for converting Arrow data into BigFrames DataFrames. Includes: - New file `bigframes/session/_io/arrow.py` with the core conversion logic. - `read_arrow(pa.Table) -> bpd.DataFrame` in `Session` class. - `read_arrow(pa.Table) -> bpd.DataFrame` in `pandas` module. - Unit and system tests for the new functionality. - Docstrings for new methods/functions. Note: Unit tests for direct DataFrame operations (shape, to_pandas) on the result of read_arrow are currently failing due to the complexity of mocking the session and executor for LocalDataNode interactions. System tests are recommended for full end-to-end validation. * rearrange * fix unit tests --------- Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> --- bigframes/core/blocks.py | 31 +++++++ bigframes/pandas/__init__.py | 2 + bigframes/pandas/io/api.py | 16 ++++ bigframes/session/__init__.py | 18 ++++ tests/unit/session/test_io_arrow.py | 133 ++++++++++++++++++++++++++++ 5 files changed, 200 insertions(+) create mode 100644 tests/unit/session/test_io_arrow.py diff --git a/bigframes/core/blocks.py b/bigframes/core/blocks.py index 1426459912..312035c7e0 100644 --- a/bigframes/core/blocks.py +++ b/bigframes/core/blocks.py @@ -50,6 +50,7 @@ import bigframes.core.identifiers import bigframes.core.join_def as join_defs import bigframes.core.ordering as ordering +import bigframes.core.pyarrow_utils as pyarrow_utils import bigframes.core.schema as bf_schema import bigframes.core.sql as sql import bigframes.core.utils as utils @@ -156,6 +157,36 @@ def __init__( self._view_ref: Optional[bigquery.TableReference] = None self._view_ref_dry_run: Optional[bigquery.TableReference] = None + @classmethod + def from_pyarrow( + cls, + data: pa.Table, + session: bigframes.Session, + ) -> Block: + column_labels = data.column_names + + # TODO(tswast): Use array_value.promote_offsets() instead once that node is + # supported by the local engine. + offsets_col = bigframes.core.guid.generate_guid() + index_ids = [offsets_col] + index_labels = [None] + + # TODO(https://github.com/googleapis/python-bigquery-dataframes/issues/859): + # Allow users to specify the "total ordering" column(s) or allow multiple + # such columns. + data = pyarrow_utils.append_offsets(data, offsets_col=offsets_col) + + # from_pyarrow will normalize the types for us. + managed_data = local_data.ManagedArrowTable.from_pyarrow(data) + array_value = core.ArrayValue.from_managed(managed_data, session=session) + block = cls( + array_value, + column_labels=column_labels, + index_columns=index_ids, + index_labels=index_labels, + ) + return block + @classmethod def from_local( cls, diff --git a/bigframes/pandas/__init__.py b/bigframes/pandas/__init__.py index ed999e62c1..f163d25757 100644 --- a/bigframes/pandas/__init__.py +++ b/bigframes/pandas/__init__.py @@ -40,6 +40,7 @@ from bigframes.pandas.io.api import ( _read_gbq_colab, from_glob_path, + read_arrow, read_csv, read_gbq, read_gbq_function, @@ -367,6 +368,7 @@ def reset_session(): merge, qcut, read_csv, + read_arrow, read_gbq, _read_gbq_colab, read_gbq_function, diff --git a/bigframes/pandas/io/api.py b/bigframes/pandas/io/api.py index 003de5913f..65435bd902 100644 --- a/bigframes/pandas/io/api.py +++ b/bigframes/pandas/io/api.py @@ -44,6 +44,7 @@ ReadPickleBuffer, StorageOptions, ) +import pyarrow as pa import bigframes._config as config import bigframes.core.global_session as global_session @@ -72,6 +73,21 @@ # method and its arguments. +def read_arrow(pa_table: pa.Table) -> bigframes.dataframe.DataFrame: + """Load a PyArrow Table to a BigQuery DataFrames DataFrame. + + Args: + pa_table (pyarrow.Table): + PyArrow table to load data from. + + Returns: + bigframes.dataframe.DataFrame: + A new DataFrame representing the data from the PyArrow table. + """ + session = global_session.get_global_session() + return session.read_arrow(pa_table=pa_table) + + def read_csv( filepath_or_buffer: str | IO["bytes"], *, diff --git a/bigframes/session/__init__.py b/bigframes/session/__init__.py index 8cbcf8612e..9d113743cf 100644 --- a/bigframes/session/__init__.py +++ b/bigframes/session/__init__.py @@ -55,12 +55,14 @@ ReadPickleBuffer, StorageOptions, ) +import pyarrow as pa from bigframes import exceptions as bfe from bigframes import version import bigframes._config.bigquery_options as bigquery_options import bigframes.clients import bigframes.constants +import bigframes.core from bigframes.core import blocks, log_adapter, utils import bigframes.core.pyformat @@ -967,6 +969,22 @@ def _read_pandas_inline( local_block = blocks.Block.from_local(pandas_dataframe, self) return dataframe.DataFrame(local_block) + def read_arrow(self, pa_table: pa.Table) -> bigframes.dataframe.DataFrame: + """Load a PyArrow Table to a BigQuery DataFrames DataFrame. + + Args: + pa_table (pyarrow.Table): + PyArrow table to load data from. + + Returns: + bigframes.dataframe.DataFrame: + A new DataFrame representing the data from the PyArrow table. + """ + import bigframes.dataframe as dataframe + + local_block = blocks.Block.from_pyarrow(pa_table, self) + return dataframe.DataFrame(local_block) + def read_csv( self, filepath_or_buffer: str | IO["bytes"], diff --git a/tests/unit/session/test_io_arrow.py b/tests/unit/session/test_io_arrow.py new file mode 100644 index 0000000000..d5266220d9 --- /dev/null +++ b/tests/unit/session/test_io_arrow.py @@ -0,0 +1,133 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import datetime + +import pyarrow as pa +import pytest + +import bigframes.pandas as bpd +from bigframes.testing import mocks + + +@pytest.fixture(scope="module") +def session(): + # Use the mock session from bigframes.testing + return mocks.create_bigquery_session() + + +def test_read_arrow_empty_table(session): + empty_table = pa.Table.from_pydict( + { + "col_a": pa.array([], type=pa.int64()), + "col_b": pa.array([], type=pa.string()), + } + ) + df = session.read_arrow(empty_table) + assert isinstance(df, bpd.DataFrame) + assert df.shape == (0, 2) + assert list(df.columns) == ["col_a", "col_b"] + pd_df = df.to_pandas() + assert pd_df.empty + assert list(pd_df.columns) == ["col_a", "col_b"] + assert pd_df["col_a"].dtype == "Int64" + assert pd_df["col_b"].dtype == "string[pyarrow]" + + +@pytest.mark.parametrize( + "data,arrow_type,expected_bq_type_kind", + [ + ([1, 2], pa.int8(), "INTEGER"), + ([1, 2], pa.int16(), "INTEGER"), + ([1, 2], pa.int32(), "INTEGER"), + ([1, 2], pa.int64(), "INTEGER"), + ([1.0, 2.0], pa.float32(), "FLOAT"), + ([1.0, 2.0], pa.float64(), "FLOAT"), + ([True, False], pa.bool_(), "BOOLEAN"), + (["a", "b"], pa.string(), "STRING"), + (["a", "b"], pa.large_string(), "STRING"), + ([b"a", b"b"], pa.binary(), "BYTES"), + ([b"a", b"b"], pa.large_binary(), "BYTES"), + ( + [ + pa.scalar(1000, type=pa.duration("s")), + pa.scalar(2000, type=pa.duration("s")), + ], + pa.duration("s"), + "INTEGER", + ), + ([datetime.date(2023, 1, 1)], pa.date32(), "DATE"), + ( + [datetime.datetime(2023, 1, 1, 12, 0, 0, tzinfo=datetime.timezone.utc)], + pa.timestamp("s", tz="UTC"), + "TIMESTAMP", + ), + ( + [datetime.datetime(2023, 1, 1, 12, 0, 0, tzinfo=datetime.timezone.utc)], + pa.timestamp("ms", tz="UTC"), + "TIMESTAMP", + ), + ( + [datetime.datetime(2023, 1, 1, 12, 0, 0, tzinfo=datetime.timezone.utc)], + pa.timestamp("us", tz="UTC"), + "TIMESTAMP", + ), + ([datetime.time(12, 34, 56, 789000)], pa.time64("us"), "TIME"), + ], +) +def test_read_arrow_type_mappings(session, data, arrow_type, expected_bq_type_kind): + """ + Tests that various arrow types are mapped to the expected BigQuery types. + This is an indirect check via the resulting DataFrame's schema. + """ + pa_table = pa.Table.from_arrays([pa.array(data, type=arrow_type)], names=["col"]) + df = session.read_arrow(pa_table) + + bigquery_schema = df._block.expr.schema.to_bigquery() + assert len(bigquery_schema) == 2 # offsets + value + field = bigquery_schema[-1] + assert field.field_type.upper() == expected_bq_type_kind + + # Also check pandas dtype after conversion for good measure + pd_df = df.to_pandas() + assert pd_df["col"].shape == (len(data),) + + +def test_read_arrow_list_type(session): + pa_table = pa.Table.from_arrays( + [pa.array([[1, 2], [3, 4, 5]], type=pa.list_(pa.int64()))], names=["list_col"] + ) + df = session.read_arrow(pa_table) + + bigquery_schema = df._block.expr.schema.to_bigquery() + assert len(bigquery_schema) == 2 # offsets + value + field = bigquery_schema[-1] + assert field.mode.upper() == "REPEATED" + assert field.field_type.upper() == "INTEGER" + + +def test_read_arrow_struct_type(session): + struct_type = pa.struct([("a", pa.int64()), ("b", pa.string())]) + pa_table = pa.Table.from_arrays( + [pa.array([{"a": 1, "b": "x"}, {"a": 2, "b": "y"}], type=struct_type)], + names=["struct_col"], + ) + df = session.read_arrow(pa_table) + + bigquery_schema = df._block.expr.schema.to_bigquery() + assert len(bigquery_schema) == 2 # offsets + value + field = bigquery_schema[-1] + assert field.field_type.upper() == "RECORD" + assert field.fields[0].name == "a" + assert field.fields[1].name == "b" From 1c45ccb133091aa85bc34450704fc8cab3d9296b Mon Sep 17 00:00:00 2001 From: TrevorBergeron Date: Mon, 30 Jun 2025 09:52:09 -0700 Subject: [PATCH 25/28] feat: Support local execution of comparison ops (#1849) --- bigframes/core/compile/polars/lowering.py | 55 ++++++++++++++- bigframes/core/compile/scalar_op_compiler.py | 18 +++++ bigframes/session/polars_executor.py | 11 ++- .../small/engines/test_comparison_ops.py | 70 +++++++++++++++++++ 4 files changed, 151 insertions(+), 3 deletions(-) create mode 100644 tests/system/small/engines/test_comparison_ops.py diff --git a/bigframes/core/compile/polars/lowering.py b/bigframes/core/compile/polars/lowering.py index 88e2d6e599..48d63e9ed9 100644 --- a/bigframes/core/compile/polars/lowering.py +++ b/bigframes/core/compile/polars/lowering.py @@ -12,15 +12,31 @@ # See the License for the specific language governing permissions and # limitations under the License. +import dataclasses + from bigframes import dtypes from bigframes.core import bigframe_node, expression from bigframes.core.rewrite import op_lowering -from bigframes.operations import numeric_ops +from bigframes.operations import comparison_ops, numeric_ops import bigframes.operations as ops # TODO: Would be more precise to actually have separate op set for polars ops (where they diverge from the original ops) +@dataclasses.dataclass +class CoerceArgsRule(op_lowering.OpLoweringRule): + op_type: type[ops.BinaryOp] + + @property + def op(self) -> type[ops.ScalarOp]: + return self.op_type + + def lower(self, expr: expression.OpExpression) -> expression.Expression: + assert isinstance(expr.op, self.op_type) + larg, rarg = _coerce_comparables(expr.children[0], expr.children[1]) + return expr.op.as_expr(larg, rarg) + + class LowerFloorDivRule(op_lowering.OpLoweringRule): @property def op(self) -> type[ops.ScalarOp]: @@ -40,7 +56,42 @@ def lower(self, expr: expression.OpExpression) -> expression.Expression: return ops.where_op.as_expr(zero_result, divisor_is_zero, expr) -POLARS_LOWERING_RULES = (LowerFloorDivRule(),) +def _coerce_comparables(expr1: expression.Expression, expr2: expression.Expression): + + target_type = dtypes.coerce_to_common(expr1.output_type, expr2.output_type) + if expr1.output_type != target_type: + expr1 = _lower_cast(ops.AsTypeOp(target_type), expr1) + if expr2.output_type != target_type: + expr2 = _lower_cast(ops.AsTypeOp(target_type), expr2) + return expr1, expr2 + + +# TODO: Need to handle bool->string cast to get capitalization correct +def _lower_cast(cast_op: ops.AsTypeOp, arg: expression.Expression): + if arg.output_type == dtypes.BOOL_DTYPE and dtypes.is_numeric(cast_op.to_type): + # bool -> decimal needs two-step cast + new_arg = ops.AsTypeOp(to_type=dtypes.INT_DTYPE).as_expr(arg) + return cast_op.as_expr(new_arg) + return cast_op.as_expr(arg) + + +LOWER_COMPARISONS = tuple( + CoerceArgsRule(op) + for op in ( + comparison_ops.EqOp, + comparison_ops.EqNullsMatchOp, + comparison_ops.NeOp, + comparison_ops.LtOp, + comparison_ops.GtOp, + comparison_ops.LeOp, + comparison_ops.GeOp, + ) +) + +POLARS_LOWERING_RULES = ( + *LOWER_COMPARISONS, + LowerFloorDivRule(), +) def lower_ops_to_polars(root: bigframe_node.BigFrameNode) -> bigframe_node.BigFrameNode: diff --git a/bigframes/core/compile/scalar_op_compiler.py b/bigframes/core/compile/scalar_op_compiler.py index 075089bb7a..30da6b2cb2 100644 --- a/bigframes/core/compile/scalar_op_compiler.py +++ b/bigframes/core/compile/scalar_op_compiler.py @@ -1498,6 +1498,7 @@ def eq_op( x: ibis_types.Value, y: ibis_types.Value, ): + x, y = _coerce_comparables(x, y) return x == y @@ -1507,6 +1508,7 @@ def eq_nulls_match_op( y: ibis_types.Value, ): """Variant of eq_op where nulls match each other. Only use where dtypes are known to be same.""" + x, y = _coerce_comparables(x, y) literal = ibis_types.literal("$NULL_SENTINEL$") if hasattr(x, "fill_null"): left = x.cast(ibis_dtypes.str).fill_null(literal) @@ -1523,6 +1525,7 @@ def ne_op( x: ibis_types.Value, y: ibis_types.Value, ): + x, y = _coerce_comparables(x, y) return x != y @@ -1534,6 +1537,17 @@ def _null_or_value(value: ibis_types.Value, where_value: ibis_types.BooleanValue ) +def _coerce_comparables( + x: ibis_types.Value, + y: ibis_types.Value, +): + if x.type().is_boolean() and not y.type().is_boolean(): + x = x.cast(ibis_dtypes.int64) + elif y.type().is_boolean() and not x.type().is_boolean(): + y = y.cast(ibis_dtypes.int64) + return x, y + + @scalar_op_compiler.register_binary_op(ops.and_op) def and_op( x: ibis_types.Value, @@ -1735,6 +1749,7 @@ def lt_op( x: ibis_types.Value, y: ibis_types.Value, ): + x, y = _coerce_comparables(x, y) return x < y @@ -1744,6 +1759,7 @@ def le_op( x: ibis_types.Value, y: ibis_types.Value, ): + x, y = _coerce_comparables(x, y) return x <= y @@ -1753,6 +1769,7 @@ def gt_op( x: ibis_types.Value, y: ibis_types.Value, ): + x, y = _coerce_comparables(x, y) return x > y @@ -1762,6 +1779,7 @@ def ge_op( x: ibis_types.Value, y: ibis_types.Value, ): + x, y = _coerce_comparables(x, y) return x >= y diff --git a/bigframes/session/polars_executor.py b/bigframes/session/polars_executor.py index 24acda35dc..ec00e38606 100644 --- a/bigframes/session/polars_executor.py +++ b/bigframes/session/polars_executor.py @@ -32,11 +32,20 @@ nodes.OrderByNode, nodes.ReversedNode, nodes.SelectionNode, + nodes.ProjectionNode, nodes.SliceNode, nodes.AggregateNode, ) -_COMPATIBLE_SCALAR_OPS = () +_COMPATIBLE_SCALAR_OPS = ( + bigframes.operations.eq_op, + bigframes.operations.eq_null_match_op, + bigframes.operations.ne_op, + bigframes.operations.gt_op, + bigframes.operations.lt_op, + bigframes.operations.ge_op, + bigframes.operations.le_op, +) _COMPATIBLE_AGG_OPS = (agg_ops.SizeOp, agg_ops.SizeUnaryOp) diff --git a/tests/system/small/engines/test_comparison_ops.py b/tests/system/small/engines/test_comparison_ops.py new file mode 100644 index 0000000000..fefff93f58 --- /dev/null +++ b/tests/system/small/engines/test_comparison_ops.py @@ -0,0 +1,70 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import itertools + +import pytest + +from bigframes.core import array_value +import bigframes.operations as ops +from bigframes.session import polars_executor +from bigframes.testing.engine_utils import assert_equivalence_execution + +pytest.importorskip("polars") + +# Polars used as reference as its fast and local. Generally though, prefer gbq engine where they disagree. +REFERENCE_ENGINE = polars_executor.PolarsExecutor() + +# numeric domain + + +def apply_op_pairwise( + array: array_value.ArrayValue, op: ops.BinaryOp, excluded_cols=[] +) -> array_value.ArrayValue: + exprs = [] + for l_arg, r_arg in itertools.permutations(array.column_ids, 2): + if (l_arg in excluded_cols) or (r_arg in excluded_cols): + continue + try: + _ = op.output_type( + array.get_column_type(l_arg), array.get_column_type(r_arg) + ) + exprs.append(op.as_expr(l_arg, r_arg)) + except TypeError: + continue + assert len(exprs) > 0 + new_arr, _ = array.compute_values(exprs) + return new_arr + + +@pytest.mark.parametrize("engine", ["polars", "bq"], indirect=True) +@pytest.mark.parametrize( + "op", + [ + ops.eq_op, + ops.eq_null_match_op, + ops.ne_op, + ops.gt_op, + ops.lt_op, + ops.le_op, + ops.ge_op, + ], +) +def test_engines_project_comparison_op( + scalars_array_value: array_value.ArrayValue, engine, op +): + # exclude string cols as does not contain dates + # bool col actually doesn't work properly for bq engine + arr = apply_op_pairwise(scalars_array_value, op, excluded_cols=["string_col"]) + assert_equivalence_execution(arr.node, REFERENCE_ENGINE, engine) From 81e4d64c5a3bd8d30edaf909d0bef2d1d1a51c01 Mon Sep 17 00:00:00 2001 From: TrevorBergeron Date: Mon, 30 Jun 2025 09:52:28 -0700 Subject: [PATCH 26/28] fix: Fix bug with DataFrame.agg for string values (#1870) --- bigframes/core/blocks.py | 19 +++++++----- bigframes/dataframe.py | 46 +++++++++++++++++++++++----- tests/system/small/test_dataframe.py | 34 +++++++++++++++++++- 3 files changed, 82 insertions(+), 17 deletions(-) diff --git a/bigframes/core/blocks.py b/bigframes/core/blocks.py index 312035c7e0..6d476cc795 100644 --- a/bigframes/core/blocks.py +++ b/bigframes/core/blocks.py @@ -2030,7 +2030,7 @@ def _generate_resample_label( return block.set_index([resample_label_id]) def _create_stack_column(self, col_label: typing.Tuple, stack_labels: pd.Index): - dtype = None + input_dtypes = [] input_columns: list[Optional[str]] = [] for uvalue in utils.index_as_tuples(stack_labels): label_to_match = (*col_label, *uvalue) @@ -2040,15 +2040,18 @@ def _create_stack_column(self, col_label: typing.Tuple, stack_labels: pd.Index): matching_ids = self.label_to_col_id.get(label_to_match, []) input_id = matching_ids[0] if len(matching_ids) > 0 else None if input_id: - if dtype and dtype != self._column_type(input_id): - raise NotImplementedError( - "Cannot stack columns with non-matching dtypes." - ) - else: - dtype = self._column_type(input_id) + input_dtypes.append(self._column_type(input_id)) input_columns.append(input_id) # Input column i is the first one that - return tuple(input_columns), dtype or pd.Float64Dtype() + if len(input_dtypes) > 0: + output_dtype = bigframes.dtypes.lcd_type(*input_dtypes) + if output_dtype is None: + raise NotImplementedError( + "Cannot stack columns with non-matching dtypes." + ) + else: + output_dtype = pd.Float64Dtype() + return tuple(input_columns), output_dtype def _column_type(self, col_id: str) -> bigframes.dtypes.Dtype: col_offset = self.value_columns.index(col_id) diff --git a/bigframes/dataframe.py b/bigframes/dataframe.py index 495e242f43..1ca5b8b035 100644 --- a/bigframes/dataframe.py +++ b/bigframes/dataframe.py @@ -3004,14 +3004,44 @@ def agg( if utils.is_dict_like(func): # Must check dict-like first because dictionaries are list-like # according to Pandas. - agg_cols = [] - for col_label, agg_func in func.items(): - agg_cols.append(self[col_label].agg(agg_func)) - - from bigframes.core.reshape import api as reshape - - return reshape.concat(agg_cols, axis=1) + aggs = [] + labels = [] + funcnames = [] + for col_label, agg_func in func.items(): + agg_func_list = agg_func if utils.is_list_like(agg_func) else [agg_func] + col_id = self._block.resolve_label_exact(col_label) + if col_id is None: + raise KeyError(f"Column {col_label} does not exist") + for agg_func in agg_func_list: + agg_op = agg_ops.lookup_agg_func(typing.cast(str, agg_func)) + agg_expr = ( + ex.UnaryAggregation(agg_op, ex.deref(col_id)) + if isinstance(agg_op, agg_ops.UnaryAggregateOp) + else ex.NullaryAggregation(agg_op) + ) + aggs.append(agg_expr) + labels.append(col_label) + funcnames.append(agg_func) + + # if any list in dict values, format output differently + if any(utils.is_list_like(v) for v in func.values()): + new_index, _ = self.columns.reindex(labels) + new_index = utils.combine_indices(new_index, pandas.Index(funcnames)) + agg_block, _ = self._block.aggregate( + aggregations=aggs, column_labels=new_index + ) + return DataFrame(agg_block).stack().droplevel(0, axis="index") + else: + new_index, _ = self.columns.reindex(labels) + agg_block, _ = self._block.aggregate( + aggregations=aggs, column_labels=new_index + ) + return bigframes.series.Series( + agg_block.transpose( + single_row_mode=True, original_row_index=pandas.Index([None]) + ) + ) elif utils.is_list_like(func): aggregations = [agg_ops.lookup_agg_func(f) for f in func] @@ -3027,7 +3057,7 @@ def agg( ) ) - else: + else: # function name string return bigframes.series.Series( self._block.aggregate_all_and_stack( agg_ops.lookup_agg_func(typing.cast(str, func)) diff --git a/tests/system/small/test_dataframe.py b/tests/system/small/test_dataframe.py index d5446efcd0..e8d156538f 100644 --- a/tests/system/small/test_dataframe.py +++ b/tests/system/small/test_dataframe.py @@ -5538,7 +5538,7 @@ def test_astype_invalid_type_fail(scalars_dfs): bf_df.astype(123) -def test_agg_with_dict(scalars_dfs): +def test_agg_with_dict_lists(scalars_dfs): bf_df, pd_df = scalars_dfs agg_funcs = { "int64_too": ["min", "max"], @@ -5553,6 +5553,38 @@ def test_agg_with_dict(scalars_dfs): ) +def test_agg_with_dict_list_and_str(scalars_dfs): + bf_df, pd_df = scalars_dfs + agg_funcs = { + "int64_too": ["min", "max"], + "int64_col": "sum", + } + + bf_result = bf_df.agg(agg_funcs).to_pandas() + pd_result = pd_df.agg(agg_funcs) + + pd.testing.assert_frame_equal( + bf_result, pd_result, check_dtype=False, check_index_type=False + ) + + +def test_agg_with_dict_strs(scalars_dfs): + bf_df, pd_df = scalars_dfs + agg_funcs = { + "int64_too": "min", + "int64_col": "sum", + "float64_col": "max", + } + + bf_result = bf_df.agg(agg_funcs).to_pandas() + pd_result = pd_df.agg(agg_funcs) + pd_result.index = pd_result.index.astype("string[pyarrow]") + + pd.testing.assert_series_equal( + bf_result, pd_result, check_dtype=False, check_index_type=False + ) + + def test_agg_with_dict_containing_non_existing_col_raise_key_error(scalars_dfs): bf_df, _ = scalars_dfs agg_funcs = { From bb981783d5124f0e1e0c80f7eb6416165264c0dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Swe=C3=B1a=20=28Swast=29?= Date: Mon, 30 Jun 2025 17:02:28 -0500 Subject: [PATCH 27/28] chore: fix `read_gbq_colab` benchmark (#1872) * chore: fix `read_gbq_colab` benchmark * Correct the table size to match actual percentiles. * Only do sum() on numeric columns. * fix for filter bench --- .../create_read_gbq_colab_benchmark_tables.py | 17 +++-------------- .../read_gbq_colab/aggregate_output.py | 2 +- tests/benchmark/read_gbq_colab/filter_output.py | 12 ++++++++++-- 3 files changed, 14 insertions(+), 17 deletions(-) diff --git a/scripts/create_read_gbq_colab_benchmark_tables.py b/scripts/create_read_gbq_colab_benchmark_tables.py index 703c946360..63419bc660 100644 --- a/scripts/create_read_gbq_colab_benchmark_tables.py +++ b/scripts/create_read_gbq_colab_benchmark_tables.py @@ -42,18 +42,6 @@ 17486432.0, 1919625975.0, ], - "num_materialized_or_scanned_rows": [ - 0.0, - 6.0, - 100.0, - 4955.0, - 23108.0, - 139504.0, - 616341.0, - 3855698.0, - 83725698.0, - 5991998082.0, - ], "avg_row_bytes": [ 0.00014346299635435792, 0.005370969708923197, @@ -524,10 +512,11 @@ def main(): for i in range(num_percentiles): percentile = TABLE_STATS["percentile"][i] avg_row_bytes_raw = TABLE_STATS["avg_row_bytes"][i] - num_rows_raw = TABLE_STATS["num_materialized_or_scanned_rows"][i] + table_bytes_raw = TABLE_STATS["materialized_or_scanned_bytes"][i] + target_table_bytes = max(1, int(math.ceil(table_bytes_raw))) target_row_bytes = max(1, int(math.ceil(avg_row_bytes_raw))) - num_rows = max(1, int(math.ceil(num_rows_raw))) + num_rows = max(1, int(math.ceil(target_table_bytes / target_row_bytes))) table_name = f"percentile_{percentile:02d}" print(f"\n--- Processing Table: {table_name} ---") diff --git a/tests/benchmark/read_gbq_colab/aggregate_output.py b/tests/benchmark/read_gbq_colab/aggregate_output.py index b612e2998c..dda4bf95a4 100644 --- a/tests/benchmark/read_gbq_colab/aggregate_output.py +++ b/tests/benchmark/read_gbq_colab/aggregate_output.py @@ -44,7 +44,7 @@ def aggregate_output( df_aggregated = ( df.assign(rounded=df[group_column].astype("Int64").round(-9)) .groupby("rounded") - .sum() + .sum(numeric_only=True) ) df_aggregated.shape diff --git a/tests/benchmark/read_gbq_colab/filter_output.py b/tests/benchmark/read_gbq_colab/filter_output.py index 7945d9f0c6..5e872bb727 100644 --- a/tests/benchmark/read_gbq_colab/filter_output.py +++ b/tests/benchmark/read_gbq_colab/filter_output.py @@ -14,6 +14,7 @@ import pathlib import benchmark.utils as utils +import pytest import bigframes.session @@ -35,8 +36,15 @@ def filter_output( # Simulate the user filtering by a column and visualizing those results df_filtered = df[df["col_bool_0"]] - df_filtered.shape - next(iter(df_filtered.to_pandas_batches(page_size=PAGE_SIZE))) + rows, _ = df_filtered.shape + + # It's possible we don't have any pages at all, since we filtered out all + # matching rows. + if rows == 0: + with pytest.raises(StopIteration): + next(iter(df_filtered.to_pandas_batches(page_size=PAGE_SIZE))) + else: + next(iter(df_filtered.to_pandas_batches(page_size=PAGE_SIZE))) if __name__ == "__main__": From a4682e919e00c50e75ad57d273edfbc8b3bb78bd Mon Sep 17 00:00:00 2001 From: "release-please[bot]" <55107282+release-please[bot]@users.noreply.github.com> Date: Tue, 1 Jul 2025 09:23:25 -0700 Subject: [PATCH 28/28] chore(main): release 2.9.0 (#1845) Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com> --- CHANGELOG.md | 28 +++++++++++++++++++++++ bigframes/version.py | 4 ++-- third_party/bigframes_vendored/version.py | 4 ++-- 3 files changed, 32 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f649f2f8a4..313064241d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,34 @@ [1]: https://pypi.org/project/bigframes/#history +## [2.9.0](https://github.com/googleapis/python-bigquery-dataframes/compare/v2.8.0...v2.9.0) (2025-06-30) + + +### Features + +* Add `bpd.read_arrow` to convert an Arrow object into a bigframes DataFrame ([#1855](https://github.com/googleapis/python-bigquery-dataframes/issues/1855)) ([633bf98](https://github.com/googleapis/python-bigquery-dataframes/commit/633bf98fde33264be4fc9d7454e541c560589152)) +* Add experimental polars execution ([#1747](https://github.com/googleapis/python-bigquery-dataframes/issues/1747)) ([daf0c3b](https://github.com/googleapis/python-bigquery-dataframes/commit/daf0c3b349fb1e85e7070c54a2d3f5460f5e40c9)) +* Add size op support in local engine ([#1865](https://github.com/googleapis/python-bigquery-dataframes/issues/1865)) ([942e66c](https://github.com/googleapis/python-bigquery-dataframes/commit/942e66c483c9afbb680a7af56c9e9a76172a33e1)) +* Create `deploy_remote_function` and `deploy_udf` functions to immediately deploy functions to BigQuery ([#1832](https://github.com/googleapis/python-bigquery-dataframes/issues/1832)) ([c706759](https://github.com/googleapis/python-bigquery-dataframes/commit/c706759b85359b6d23ce3449f6ab138ad2d22f9d)) +* Support index item assign in Series ([#1868](https://github.com/googleapis/python-bigquery-dataframes/issues/1868)) ([c5d251a](https://github.com/googleapis/python-bigquery-dataframes/commit/c5d251a1d454bb4ef55ea9905faeadd646a23b14)) +* Support item assignment in series ([#1859](https://github.com/googleapis/python-bigquery-dataframes/issues/1859)) ([25684ff](https://github.com/googleapis/python-bigquery-dataframes/commit/25684ff60367f49dd318d4677a7438abdc98bff9)) +* Support local execution of comparison ops ([#1849](https://github.com/googleapis/python-bigquery-dataframes/issues/1849)) ([1c45ccb](https://github.com/googleapis/python-bigquery-dataframes/commit/1c45ccb133091aa85bc34450704fc8cab3d9296b)) + + +### Bug Fixes + +* Fix bug selecting column repeatedly ([#1858](https://github.com/googleapis/python-bigquery-dataframes/issues/1858)) ([cc339e9](https://github.com/googleapis/python-bigquery-dataframes/commit/cc339e9938129cac896460e3a794b3ec8479fa4a)) +* Fix bug with DataFrame.agg for string values ([#1870](https://github.com/googleapis/python-bigquery-dataframes/issues/1870)) ([81e4d64](https://github.com/googleapis/python-bigquery-dataframes/commit/81e4d64c5a3bd8d30edaf909d0bef2d1d1a51c01)) +* Generate GoogleSQL instead of legacy SQL data types for `dry_run=True` from `bpd._read_gbq_colab` with local pandas DataFrame ([#1867](https://github.com/googleapis/python-bigquery-dataframes/issues/1867)) ([fab3c38](https://github.com/googleapis/python-bigquery-dataframes/commit/fab3c387b2ad66043244fa813a366e613b41c60f)) +* Revert dict back to protobuf in the iam binding update ([#1838](https://github.com/googleapis/python-bigquery-dataframes/issues/1838)) ([9fb3cb4](https://github.com/googleapis/python-bigquery-dataframes/commit/9fb3cb444607df6736d383a2807059bca470c453)) + + +### Documentation + +* Add data visualization samples for public doc ([#1847](https://github.com/googleapis/python-bigquery-dataframes/issues/1847)) ([15e1277](https://github.com/googleapis/python-bigquery-dataframes/commit/15e1277b1413de18a5e36f72959a99701d6df08b)) +* Changed broken logo ([#1866](https://github.com/googleapis/python-bigquery-dataframes/issues/1866)) ([e3c06b4](https://github.com/googleapis/python-bigquery-dataframes/commit/e3c06b4a07d0669a42460d081f1582b681ae3dd5)) +* Update ai.forecast notebook ([#1844](https://github.com/googleapis/python-bigquery-dataframes/issues/1844)) ([1863538](https://github.com/googleapis/python-bigquery-dataframes/commit/186353888db537b561ee994256f998df361b4071)) + ## [2.8.0](https://github.com/googleapis/python-bigquery-dataframes/compare/v2.7.0...v2.8.0) (2025-06-23) diff --git a/bigframes/version.py b/bigframes/version.py index 5d2de2f97f..4f3c9a5124 100644 --- a/bigframes/version.py +++ b/bigframes/version.py @@ -12,8 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "2.8.0" +__version__ = "2.9.0" # {x-release-please-start-date} -__release_date__ = "2025-06-23" +__release_date__ = "2025-06-30" # {x-release-please-end} diff --git a/third_party/bigframes_vendored/version.py b/third_party/bigframes_vendored/version.py index 5d2de2f97f..4f3c9a5124 100644 --- a/third_party/bigframes_vendored/version.py +++ b/third_party/bigframes_vendored/version.py @@ -12,8 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "2.8.0" +__version__ = "2.9.0" # {x-release-please-start-date} -__release_date__ = "2025-06-23" +__release_date__ = "2025-06-30" # {x-release-please-end}
\n", " \n", - " \"Colab Run in Colab\n", + " \"Colab Run in Colab\n", " \n", " \n", " \n", - " \"GitHub\n", + " \"GitHub\n", " View on GitHub\n", " \n", " \n", " \n", - " \"Colab Run in Colab\n", + " \"Colab Run in Colab\n", " \n", " \n", " \n", - " \"GitHub\n", + " \"GitHub\n", " View on GitHub\n", " \n", " \n", " \n", - " \"Colab Run in Colab\n", + " \"Colab Run in Colab\n", " \n", " \n", " \n", - " \"GitHub\n", + " \"GitHub\n", " View on GitHub\n", " \n", " \n", " \n", - " \"Colab Run in Colab\n", + " \"Colab Run in Colab\n", " \n", " \n", " \n", - " \"GitHub\n", + " \"GitHub\n", " View on GitHub\n", " \n", " \n", " \n", - " \"Colab Run in Colab\n", + " \"Colab Run in Colab\n", " \n", " \n", " \n", - " \"GitHub\n", + " \"GitHub\n", " View on GitHub\n", " \n", " \n", " \n", - " \"Colab Run in Colab\n", + " \"Colab Run in Colab\n", " \n", " \n", " \n", - " \"GitHub\n", + " \"GitHub\n", " View on GitHub\n", " \n", " \n", " \n", - " \"Colab Run in Colab\n", + " \"Colab Run in Colab\n", " \n", " \n", " \n", - " \"GitHub\n", + " \"GitHub\n", " View on GitHub\n", " \n", " \n", " \n", - " \"Colab Run in Colab\n", + " \"Colab Run in Colab\n", " \n", " \n", " \n", - " \"GitHub\n", + " \"GitHub\n", " View on GitHub\n", " \n", " \n", " \n", - " \"Colab Run in Colab\n", + " \"Colab Run in Colab\n", " \n", " \n", " \n", - " \"GitHub\n", + " \"GitHub\n", " View on GitHub\n", " \n", " \n", " \n", - " \"Colab Run in Colab\n", + " \"Colab Run in Colab\n", " \n", " \n", " \n", - " \"GitHub\n", + " \"GitHub\n", " View on GitHub\n", " \n", "