From f27962149836e9b23b26851f181bb1088fe117db Mon Sep 17 00:00:00 2001 From: RinChanNOW Date: Tue, 10 Oct 2023 14:03:47 +0800 Subject: [PATCH 01/25] chore: reduce duplicate column bindings for aggregate functions. (#13136) * chore: reduce duplicate column bindings for aggregate functions. * fix clippy. * Fix explain results. * Fix explain results of cluster tests. --------- Co-authored-by: Jk Xu <54522439+Dousir9@users.noreply.github.com> Co-authored-by: BohuTANG --- src/query/sql/src/planner/binder/aggregate.rs | 30 ++ src/query/sql/src/planner/binder/project.rs | 26 +- .../mode/cluster/04_0002_explain_v2.test | 48 ++- .../suites/mode/cluster/exchange.test | 100 +++---- .../suites/mode/standalone/ee/explain | 277 ++++++++---------- .../mode/standalone/explain/aggregate.test | 260 ++++++++++++---- .../mode/standalone/explain/explain.test | 92 +++--- .../mode/standalone/explain/fold_count.test | 62 ++-- .../suites/mode/standalone/explain/limit.test | 148 +++++----- .../mode/standalone/explain/project_set.test | 40 ++- .../mode/standalone/explain/prune_column.test | 144 +++++---- .../standalone/explain/push_down_filter.test | 72 +++-- .../mode/standalone/explain/subquery.test | 68 ++--- .../standalone/explain_native/aggregate.test | 156 +++++----- .../standalone/explain_native/explain.test | 92 +++--- .../standalone/explain_native/fold_count.test | 54 ++-- .../mode/standalone/explain_native/limit.test | 148 +++++----- .../explain_native/project_set.test | 40 ++- .../explain_native/prune_column.test | 138 ++++----- .../explain_native/push_down_filter.test | 68 ++--- .../standalone/explain_native/subquery.test | 68 ++--- 21 files changed, 1083 insertions(+), 1048 deletions(-) diff --git a/src/query/sql/src/planner/binder/aggregate.rs b/src/query/sql/src/planner/binder/aggregate.rs index e2fe06c5c657..df7cfadfd3cd 100644 --- a/src/query/sql/src/planner/binder/aggregate.rs +++ b/src/query/sql/src/planner/binder/aggregate.rs @@ -319,6 +319,13 @@ impl<'a> AggregateRewriter<'a> { /// add the replaced aggregate function and the arguments into `AggregateInfo`. fn replace_aggregate_function(&mut self, aggregate: &AggregateFunction) -> Result { let agg_info = &mut self.bind_context.aggregate_info; + + if let Some(column) = + find_replaced_aggregate_function(agg_info, aggregate, &aggregate.display_name) + { + return Ok(BoundColumnRef { span: None, column }.into()); + } + let mut replaced_args: Vec = Vec::with_capacity(aggregate.args.len()); for (i, arg) in aggregate.args.iter().enumerate() { @@ -922,3 +929,26 @@ impl Binder { } } } + +/// Replace [`AggregateFunction`] with a [`ColumnBinding`] if the function is already replaced. +pub fn find_replaced_aggregate_function( + agg_info: &AggregateInfo, + agg: &AggregateFunction, + new_name: &str, +) -> Option { + agg_info + .aggregate_functions_map + .get(&agg.display_name) + .map(|i| { + // This expression is already replaced. + let scalar_item = &agg_info.aggregate_functions[*i]; + debug_assert_eq!(scalar_item.scalar.data_type().unwrap(), *agg.return_type); + ColumnBindingBuilder::new( + new_name.to_string(), + scalar_item.index, + agg.return_type.clone(), + Visibility::Visible, + ) + .build() + }) +} diff --git a/src/query/sql/src/planner/binder/project.rs b/src/query/sql/src/planner/binder/project.rs index b193efa761d3..55e4e64d43ff 100644 --- a/src/query/sql/src/planner/binder/project.rs +++ b/src/query/sql/src/planner/binder/project.rs @@ -28,6 +28,7 @@ use common_exception::Result; use common_exception::Span; use super::AggregateInfo; +use crate::binder::aggregate::find_replaced_aggregate_function; use crate::binder::select::SelectItem; use crate::binder::select::SelectList; use crate::binder::ExprContext; @@ -61,15 +62,24 @@ impl Binder { // This item is a grouping sets item, its data type should be nullable. let is_grouping_sets_item = agg_info.grouping_sets.is_some() && agg_info.group_items_map.contains_key(&item.scalar); - let mut column_binding = if let ScalarExpr::BoundColumnRef(ref column_ref) = item.scalar - { - let mut column_binding = column_ref.column.clone(); - // We should apply alias for the ColumnBinding, since it comes from table - column_binding.column_name = item.alias.clone(); - column_binding - } else { - self.create_derived_column_binding(item.alias.clone(), item.scalar.data_type()?) + + let mut column_binding = match &item.scalar { + ScalarExpr::BoundColumnRef(column_ref) => { + let mut column_binding = column_ref.column.clone(); + // We should apply alias for the ColumnBinding, since it comes from table + column_binding.column_name = item.alias.clone(); + column_binding + } + ScalarExpr::AggregateFunction(agg) => { + // Replace to bound column to reduce duplicate derived column bindings. + debug_assert!(!is_grouping_sets_item); + find_replaced_aggregate_function(agg_info, agg, &item.alias).unwrap() + } + _ => { + self.create_derived_column_binding(item.alias.clone(), item.scalar.data_type()?) + } }; + if is_grouping_sets_item { column_binding.data_type = Box::new(column_binding.data_type.wrap_nullable()); } diff --git a/tests/sqllogictests/suites/mode/cluster/04_0002_explain_v2.test b/tests/sqllogictests/suites/mode/cluster/04_0002_explain_v2.test index a96df63e25e0..010f89a1c506 100644 --- a/tests/sqllogictests/suites/mode/cluster/04_0002_explain_v2.test +++ b/tests/sqllogictests/suites/mode/cluster/04_0002_explain_v2.test @@ -167,40 +167,36 @@ query T explain select count(1) as c, count(b) as d, max(a) as e from t1 order by c, e, d limit 10; ---- Limit -├── output columns: [c (#5), e (#7), d (#6)] +├── output columns: [count(1) (#2), count(b) (#3), max(a) (#4)] ├── limit: 10 ├── offset: 0 ├── estimated rows: 1.00 └── Sort - ├── output columns: [c (#5), e (#7), d (#6)] - ├── sort keys: [c ASC NULLS LAST, e ASC NULLS LAST, d ASC NULLS LAST] + ├── output columns: [count(1) (#2), count(b) (#3), max(a) (#4)] + ├── sort keys: [count(1) ASC NULLS LAST, max(a) ASC NULLS LAST, count(b) ASC NULLS LAST] ├── estimated rows: 1.00 - └── EvalScalar - ├── output columns: [c (#5), e (#7), d (#6)] - ├── expressions: [count(1) (#2), max(a) (#4), count(b) (#3)] + └── AggregateFinal + ├── output columns: [count(1) (#2), count(b) (#3), max(a) (#4)] + ├── group by: [] + ├── aggregate functions: [count(), count(b), max(a)] ├── estimated rows: 1.00 - └── AggregateFinal + └── Exchange ├── output columns: [count(1) (#2), count(b) (#3), max(a) (#4)] - ├── group by: [] - ├── aggregate functions: [count(), count(b), max(a)] - ├── estimated rows: 1.00 - └── Exchange + ├── exchange type: Merge + └── AggregatePartial ├── output columns: [count(1) (#2), count(b) (#3), max(a) (#4)] - ├── exchange type: Merge - └── AggregatePartial - ├── output columns: [count(1) (#2), count(b) (#3), max(a) (#4)] - ├── group by: [] - ├── aggregate functions: [count(), count(b), max(a)] - ├── estimated rows: 1.00 - └── TableScan - ├── table: default.default.t1 - ├── output columns: [a (#0), b (#1)] - ├── read rows: 0 - ├── read bytes: 0 - ├── partitions total: 0 - ├── partitions scanned: 0 - ├── push downs: [filters: [], limit: NONE] - └── estimated rows: 0.00 + ├── group by: [] + ├── aggregate functions: [count(), count(b), max(a)] + ├── estimated rows: 1.00 + └── TableScan + ├── table: default.default.t1 + ├── output columns: [a (#0), b (#1)] + ├── read rows: 0 + ├── read bytes: 0 + ├── partitions total: 0 + ├── partitions scanned: 0 + ├── push downs: [filters: [], limit: NONE] + └── estimated rows: 0.00 query T explain select (t1.a + 1) as c,(t1.b+1) as d, (t2.a+1) as e from t1 join t2 on t1.a = t2.a order by c, d, e limit 10; diff --git a/tests/sqllogictests/suites/mode/cluster/exchange.test b/tests/sqllogictests/suites/mode/cluster/exchange.test index 41d157fd4f5b..afc4c368aba9 100644 --- a/tests/sqllogictests/suites/mode/cluster/exchange.test +++ b/tests/sqllogictests/suites/mode/cluster/exchange.test @@ -167,50 +167,46 @@ query T explain select * from (select sum(number) as number from numbers(1) group by number) t, numbers(2) t1 where t.number = t1.number ---- Exchange -├── output columns: [t1.number (#4), number (#3)] +├── output columns: [t1.number (#3), sum(number) (#2)] ├── exchange type: Merge └── HashJoin - ├── output columns: [t1.number (#4), number (#3)] + ├── output columns: [t1.number (#3), sum(number) (#2)] ├── join type: INNER - ├── build keys: [t.number (#3)] - ├── probe keys: [CAST(t1.number (#4) AS UInt64 NULL)] + ├── build keys: [t.number (#2)] + ├── probe keys: [CAST(t1.number (#3) AS UInt64 NULL)] ├── filters: [] ├── estimated rows: 2.00 ├── Exchange(Build) - │ ├── output columns: [number (#3)] - │ ├── exchange type: Hash(t.number (#3)) - │ └── EvalScalar - │ ├── output columns: [number (#3)] - │ ├── expressions: [sum(number) (#2)] + │ ├── output columns: [sum(number) (#2), numbers.number (#0)] + │ ├── exchange type: Hash(t.number (#2)) + │ └── AggregateFinal + │ ├── output columns: [sum(number) (#2), numbers.number (#0)] + │ ├── group by: [number] + │ ├── aggregate functions: [sum(number)] │ ├── estimated rows: 1.00 - │ └── AggregateFinal - │ ├── output columns: [sum(number) (#2), numbers.number (#0)] - │ ├── group by: [number] - │ ├── aggregate functions: [sum(number)] - │ ├── estimated rows: 1.00 - │ └── Exchange + │ └── Exchange + │ ├── output columns: [sum(number) (#2), #_group_by_key] + │ ├── exchange type: Hash(_group_by_key) + │ └── AggregatePartial │ ├── output columns: [sum(number) (#2), #_group_by_key] - │ ├── exchange type: Hash(_group_by_key) - │ └── AggregatePartial - │ ├── output columns: [sum(number) (#2), #_group_by_key] - │ ├── group by: [number] - │ ├── aggregate functions: [sum(number)] - │ ├── estimated rows: 1.00 - │ └── TableScan - │ ├── table: default.system.numbers - │ ├── output columns: [number (#0)] - │ ├── read rows: 1 - │ ├── read bytes: 8 - │ ├── partitions total: 1 - │ ├── partitions scanned: 1 - │ ├── push downs: [filters: [], limit: NONE] - │ └── estimated rows: 1.00 + │ ├── group by: [number] + │ ├── aggregate functions: [sum(number)] + │ ├── estimated rows: 1.00 + │ └── TableScan + │ ├── table: default.system.numbers + │ ├── output columns: [number (#0)] + │ ├── read rows: 1 + │ ├── read bytes: 8 + │ ├── partitions total: 1 + │ ├── partitions scanned: 1 + │ ├── push downs: [filters: [], limit: NONE] + │ └── estimated rows: 1.00 └── Exchange(Probe) - ├── output columns: [t1.number (#4)] - ├── exchange type: Hash(CAST(t1.number (#4) AS UInt64 NULL)) + ├── output columns: [t1.number (#3)] + ├── exchange type: Hash(CAST(t1.number (#3) AS UInt64 NULL)) └── TableScan ├── table: default.system.numbers - ├── output columns: [number (#4)] + ├── output columns: [number (#3)] ├── read rows: 2 ├── read bytes: 16 ├── partitions total: 1 @@ -224,11 +220,11 @@ explain fragments select * from (select sum(number) as number from numbers(1) gr Fragment 0: DataExchange: Shuffle ExchangeSink - ├── output columns: [t1.number (#4)] + ├── output columns: [t1.number (#3)] ├── destination fragment: [3] └── TableScan ├── table: default.system.numbers - ├── output columns: [number (#4)] + ├── output columns: [number (#3)] ├── read rows: 2 ├── read bytes: 16 ├── partitions total: 1 @@ -261,45 +257,41 @@ Fragment 1: Fragment 2: DataExchange: Shuffle ExchangeSink - ├── output columns: [number (#3)] + ├── output columns: [sum(number) (#2), numbers.number (#0)] ├── destination fragment: [3] - └── EvalScalar - ├── output columns: [number (#3)] - ├── expressions: [sum(number) (#2)] + └── AggregateFinal + ├── output columns: [sum(number) (#2), numbers.number (#0)] + ├── group by: [number] + ├── aggregate functions: [sum(number)] ├── estimated rows: 1.00 - └── AggregateFinal - ├── output columns: [sum(number) (#2), numbers.number (#0)] - ├── group by: [number] - ├── aggregate functions: [sum(number)] - ├── estimated rows: 1.00 - └── ExchangeSource - ├── output columns: [sum(number) (#2), #_group_by_key] - └── source fragment: [1] + └── ExchangeSource + ├── output columns: [sum(number) (#2), #_group_by_key] + └── source fragment: [1] (empty) (empty) Fragment 3: DataExchange: Merge ExchangeSink - ├── output columns: [t1.number (#4), number (#3)] + ├── output columns: [t1.number (#3), sum(number) (#2)] ├── destination fragment: [4] └── HashJoin - ├── output columns: [t1.number (#4), number (#3)] + ├── output columns: [t1.number (#3), sum(number) (#2)] ├── join type: INNER - ├── build keys: [t.number (#3)] - ├── probe keys: [CAST(t1.number (#4) AS UInt64 NULL)] + ├── build keys: [t.number (#2)] + ├── probe keys: [CAST(t1.number (#3) AS UInt64 NULL)] ├── filters: [] ├── estimated rows: 2.00 ├── ExchangeSource(Build) - │ ├── output columns: [number (#3)] + │ ├── output columns: [sum(number) (#2), numbers.number (#0)] │ └── source fragment: [2] └── ExchangeSource(Probe) - ├── output columns: [t1.number (#4)] + ├── output columns: [t1.number (#3)] └── source fragment: [0] (empty) (empty) Fragment 4: ExchangeSource - ├── output columns: [t1.number (#4), number (#3)] + ├── output columns: [t1.number (#3), sum(number) (#2)] └── source fragment: [3] (empty) diff --git a/tests/sqllogictests/suites/mode/standalone/ee/explain b/tests/sqllogictests/suites/mode/standalone/ee/explain index 125ae617643a..635f968a0aba 100644 --- a/tests/sqllogictests/suites/mode/standalone/ee/explain +++ b/tests/sqllogictests/suites/mode/standalone/ee/explain @@ -36,41 +36,37 @@ CREATE AGGREGATING INDEX idx1 AS SELECT b, SUM(a) FROM t1 WHERE b > 3 GROUP BY b query T EXPLAIN SELECT SUM(a), b FROM t1 WHERE b > 3 GROUP BY b ---- -EvalScalar -├── output columns: [t1.b (#5), sum(a) (#7)] -├── expressions: [SUM(a) (#6)] +AggregateFinal +├── output columns: [SUM(a) (#5), t1.b (#4)] +├── group by: [b] +├── aggregate functions: [sum(a)] ├── estimated rows: 0.00 -└── AggregateFinal - ├── output columns: [SUM(a) (#6), t1.b (#5)] +└── AggregatePartial + ├── output columns: [SUM(a) (#5), #_group_by_key] ├── group by: [b] ├── aggregate functions: [sum(a)] ├── estimated rows: 0.00 - └── AggregatePartial - ├── output columns: [SUM(a) (#6), #_group_by_key] - ├── group by: [b] - ├── aggregate functions: [sum(a)] + └── Filter + ├── output columns: [t1.a (#3), t1.b (#4)] + ├── filters: [t1.b (#4) > 3] ├── estimated rows: 0.00 - └── Filter - ├── output columns: [t1.a (#4), t1.b (#5)] - ├── filters: [t1.b (#5) > 3] - ├── estimated rows: 0.00 - └── TableScan - ├── table: default.test_index_db.t1 - ├── output columns: [a (#4), b (#5)] - ├── read rows: 0 - ├── read bytes: 0 - ├── partitions total: 0 - ├── partitions scanned: 0 - ├── push downs: [filters: [t1.b (#5) > 3], limit: NONE] - ├── aggregating index: [SELECT b, SUM(a) FROM test_index_db.t1 WHERE (b > 3) GROUP BY b] - ├── rewritten query: [selection: [index_col_0 (#0), index_col_1 (#1)]] - └── estimated rows: 0.00 + └── TableScan + ├── table: default.test_index_db.t1 + ├── output columns: [a (#3), b (#4)] + ├── read rows: 0 + ├── read bytes: 0 + ├── partitions total: 0 + ├── partitions scanned: 0 + ├── push downs: [filters: [t1.b (#4) > 3], limit: NONE] + ├── aggregating index: [SELECT b, SUM(a) FROM test_index_db.t1 WHERE (b > 3) GROUP BY b] + ├── rewritten query: [selection: [index_col_0 (#0), index_col_1 (#1)]] + └── estimated rows: 0.00 query T EXPLAIN SELECT b FROM t1 WHERE b > 3 GROUP BY b ---- AggregateFinal -├── output columns: [t1.b (#5)] +├── output columns: [t1.b (#4)] ├── group by: [b] ├── aggregate functions: [] ├── estimated rows: 0.00 @@ -80,17 +76,17 @@ AggregateFinal ├── aggregate functions: [] ├── estimated rows: 0.00 └── Filter - ├── output columns: [t1.b (#5)] - ├── filters: [t1.b (#5) > 3] + ├── output columns: [t1.b (#4)] + ├── filters: [t1.b (#4) > 3] ├── estimated rows: 0.00 └── TableScan ├── table: default.test_index_db.t1 - ├── output columns: [b (#5)] + ├── output columns: [b (#4)] ├── read rows: 0 ├── read bytes: 0 ├── partitions total: 0 ├── partitions scanned: 0 - ├── push downs: [filters: [t1.b (#5) > 3], limit: NONE] + ├── push downs: [filters: [t1.b (#4) > 3], limit: NONE] ├── aggregating index: [SELECT b, SUM(a) FROM test_index_db.t1 WHERE (b > 3) GROUP BY b] ├── rewritten query: [selection: [index_col_0 (#0)]] └── estimated rows: 0.00 @@ -99,31 +95,31 @@ query T EXPLAIN SELECT SUM(a) + 1 FROM t1 WHERE b > 3 GROUP BY b ---- EvalScalar -├── output columns: [(sum(a) + 1) (#7)] -├── expressions: [SUM(a) (#6) + 1] +├── output columns: [(sum(a) + 1) (#6)] +├── expressions: [SUM(a) (#5) + 1] ├── estimated rows: 0.00 └── AggregateFinal - ├── output columns: [SUM(a) (#6), t1.b (#5)] + ├── output columns: [SUM(a) (#5), t1.b (#4)] ├── group by: [b] ├── aggregate functions: [sum(a)] ├── estimated rows: 0.00 └── AggregatePartial - ├── output columns: [SUM(a) (#6), #_group_by_key] + ├── output columns: [SUM(a) (#5), #_group_by_key] ├── group by: [b] ├── aggregate functions: [sum(a)] ├── estimated rows: 0.00 └── Filter - ├── output columns: [t1.a (#4), t1.b (#5)] - ├── filters: [t1.b (#5) > 3] + ├── output columns: [t1.a (#3), t1.b (#4)] + ├── filters: [t1.b (#4) > 3] ├── estimated rows: 0.00 └── TableScan ├── table: default.test_index_db.t1 - ├── output columns: [a (#4), b (#5)] + ├── output columns: [a (#3), b (#4)] ├── read rows: 0 ├── read bytes: 0 ├── partitions total: 0 ├── partitions scanned: 0 - ├── push downs: [filters: [t1.b (#5) > 3], limit: NONE] + ├── push downs: [filters: [t1.b (#4) > 3], limit: NONE] ├── aggregating index: [SELECT b, SUM(a) FROM test_index_db.t1 WHERE (b > 3) GROUP BY b] ├── rewritten query: [selection: [index_col_0 (#0), index_col_1 (#1)]] └── estimated rows: 0.00 @@ -132,31 +128,31 @@ query T EXPLAIN SELECT SUM(a) + 1 FROM t1 WHERE b > 5 GROUP BY b ---- EvalScalar -├── output columns: [(sum(a) + 1) (#7)] -├── expressions: [SUM(a) (#6) + 1] +├── output columns: [(sum(a) + 1) (#6)] +├── expressions: [SUM(a) (#5) + 1] ├── estimated rows: 0.00 └── AggregateFinal - ├── output columns: [SUM(a) (#6), t1.b (#5)] + ├── output columns: [SUM(a) (#5), t1.b (#4)] ├── group by: [b] ├── aggregate functions: [sum(a)] ├── estimated rows: 0.00 └── AggregatePartial - ├── output columns: [SUM(a) (#6), #_group_by_key] + ├── output columns: [SUM(a) (#5), #_group_by_key] ├── group by: [b] ├── aggregate functions: [sum(a)] ├── estimated rows: 0.00 └── Filter - ├── output columns: [t1.a (#4), t1.b (#5)] - ├── filters: [t1.b (#5) > 5] + ├── output columns: [t1.a (#3), t1.b (#4)] + ├── filters: [t1.b (#4) > 5] ├── estimated rows: 0.00 └── TableScan ├── table: default.test_index_db.t1 - ├── output columns: [a (#4), b (#5)] + ├── output columns: [a (#3), b (#4)] ├── read rows: 0 ├── read bytes: 0 ├── partitions total: 0 ├── partitions scanned: 0 - ├── push downs: [filters: [t1.b (#5) > 5], limit: NONE] + ├── push downs: [filters: [t1.b (#4) > 5], limit: NONE] ├── aggregating index: [SELECT b, SUM(a) FROM test_index_db.t1 WHERE (b > 3) GROUP BY b] ├── rewritten query: [selection: [index_col_0 (#0), index_col_1 (#1)], filter: index_col_0 (#0) > to_int32(5)] └── estimated rows: 0.00 @@ -164,69 +160,61 @@ EvalScalar query T EXPLAIN SELECT t1.b, SUM(a) FROM t1 GROUP BY t1.b HAVING SUM(a)=(SELECT SUM(a) FROM t1 t WHERE t1.b=t.b and t.b > 3) ---- -EvalScalar -├── output columns: [t1.b (#5), sum(a) (#7)] -├── expressions: [SUM(a) (#16)] +Filter +├── output columns: [SUM(a) (#5), t1.b (#4)] +├── filters: [is_true(SUM(a) (#5) = scalar_subquery_11 (#11))] ├── estimated rows: 0.00 -└── Filter - ├── output columns: [SUM(a) (#16), t1.b (#5)] - ├── filters: [is_true(SUM(a) (#16) = scalar_subquery_15 (#15))] +└── HashJoin + ├── output columns: [SUM(a) (#5), t1.b (#4), SUM(a) (#11)] + ├── join type: LEFT SINGLE + ├── build keys: [b (#10)] + ├── probe keys: [CAST(b (#4) AS Int32 NULL)] + ├── filters: [] ├── estimated rows: 0.00 - └── HashJoin - ├── output columns: [SUM(a) (#16), t1.b (#5), sum(a) (#15)] - ├── join type: LEFT SINGLE - ├── build keys: [b (#13)] - ├── probe keys: [CAST(b (#5) AS Int32 NULL)] - ├── filters: [] + ├── AggregateFinal(Build) + │ ├── output columns: [SUM(a) (#11), t.b (#10)] + │ ├── group by: [b] + │ ├── aggregate functions: [sum(a)] + │ ├── estimated rows: 0.00 + │ └── AggregatePartial + │ ├── output columns: [SUM(a) (#11), #_group_by_key] + │ ├── group by: [b] + │ ├── aggregate functions: [sum(a)] + │ ├── estimated rows: 0.00 + │ └── Filter + │ ├── output columns: [t.a (#9), t.b (#10)] + │ ├── filters: [t.b (#10) > 3] + │ ├── estimated rows: 0.00 + │ └── TableScan + │ ├── table: default.test_index_db.t1 + │ ├── output columns: [a (#9), b (#10)] + │ ├── read rows: 0 + │ ├── read bytes: 0 + │ ├── partitions total: 0 + │ ├── partitions scanned: 0 + │ ├── push downs: [filters: [t.b (#10) > 3], limit: NONE] + │ ├── aggregating index: [SELECT b, SUM(a) FROM test_index_db.t1 WHERE (b > 3) GROUP BY b] + │ ├── rewritten query: [selection: [index_col_0 (#0), index_col_1 (#1)]] + │ └── estimated rows: 0.00 + └── AggregateFinal(Probe) + ├── output columns: [SUM(a) (#5), t1.b (#4)] + ├── group by: [b] + ├── aggregate functions: [sum(a)] ├── estimated rows: 0.00 - ├── EvalScalar(Build) - │ ├── output columns: [t.b (#13), sum(a) (#15)] - │ ├── expressions: [SUM(a) (#14)] - │ ├── estimated rows: 0.00 - │ └── AggregateFinal - │ ├── output columns: [SUM(a) (#14), t.b (#13)] - │ ├── group by: [b] - │ ├── aggregate functions: [sum(a)] - │ ├── estimated rows: 0.00 - │ └── AggregatePartial - │ ├── output columns: [SUM(a) (#14), #_group_by_key] - │ ├── group by: [b] - │ ├── aggregate functions: [sum(a)] - │ ├── estimated rows: 0.00 - │ └── Filter - │ ├── output columns: [t.a (#12), t.b (#13)] - │ ├── filters: [t.b (#13) > 3] - │ ├── estimated rows: 0.00 - │ └── TableScan - │ ├── table: default.test_index_db.t1 - │ ├── output columns: [a (#12), b (#13)] - │ ├── read rows: 0 - │ ├── read bytes: 0 - │ ├── partitions total: 0 - │ ├── partitions scanned: 0 - │ ├── push downs: [filters: [t.b (#13) > 3], limit: NONE] - │ ├── aggregating index: [SELECT b, SUM(a) FROM test_index_db.t1 WHERE (b > 3) GROUP BY b] - │ ├── rewritten query: [selection: [index_col_0 (#0), index_col_1 (#1)]] - │ └── estimated rows: 0.00 - └── AggregateFinal(Probe) - ├── output columns: [SUM(a) (#16), t1.b (#5)] + └── AggregatePartial + ├── output columns: [SUM(a) (#5), #_group_by_key] ├── group by: [b] ├── aggregate functions: [sum(a)] ├── estimated rows: 0.00 - └── AggregatePartial - ├── output columns: [SUM(a) (#16), #_group_by_key] - ├── group by: [b] - ├── aggregate functions: [sum(a)] - ├── estimated rows: 0.00 - └── TableScan - ├── table: default.test_index_db.t1 - ├── output columns: [a (#4), b (#5)] - ├── read rows: 0 - ├── read bytes: 0 - ├── partitions total: 0 - ├── partitions scanned: 0 - ├── push downs: [filters: [], limit: NONE] - └── estimated rows: 0.00 + └── TableScan + ├── table: default.test_index_db.t1 + ├── output columns: [a (#3), b (#4)] + ├── read rows: 0 + ├── read bytes: 0 + ├── partitions total: 0 + ├── partitions scanned: 0 + ├── push downs: [filters: [], limit: NONE] + └── estimated rows: 0.00 # Disable aggregating index scan statement ok @@ -235,33 +223,29 @@ SET enable_aggregating_index_scan = 0 query T EXPLAIN SELECT SUM(a), b FROM t1 WHERE b > 3 GROUP BY b ---- -EvalScalar -├── output columns: [t1.b (#1), sum(a) (#3)] -├── expressions: [SUM(a) (#2)] +AggregateFinal +├── output columns: [SUM(a) (#2), t1.b (#1)] +├── group by: [b] +├── aggregate functions: [sum(a)] ├── estimated rows: 0.00 -└── AggregateFinal - ├── output columns: [SUM(a) (#2), t1.b (#1)] +└── AggregatePartial + ├── output columns: [SUM(a) (#2), #_group_by_key] ├── group by: [b] ├── aggregate functions: [sum(a)] ├── estimated rows: 0.00 - └── AggregatePartial - ├── output columns: [SUM(a) (#2), #_group_by_key] - ├── group by: [b] - ├── aggregate functions: [sum(a)] + └── Filter + ├── output columns: [t1.a (#0), t1.b (#1)] + ├── filters: [t1.b (#1) > 3] ├── estimated rows: 0.00 - └── Filter - ├── output columns: [t1.a (#0), t1.b (#1)] - ├── filters: [t1.b (#1) > 3] - ├── estimated rows: 0.00 - └── TableScan - ├── table: default.test_index_db.t1 - ├── output columns: [a (#0), b (#1)] - ├── read rows: 0 - ├── read bytes: 0 - ├── partitions total: 0 - ├── partitions scanned: 0 - ├── push downs: [filters: [t1.b (#1) > 3], limit: NONE] - └── estimated rows: 0.00 + └── TableScan + ├── table: default.test_index_db.t1 + ├── output columns: [a (#0), b (#1)] + ├── read rows: 0 + ├── read bytes: 0 + ├── partitions total: 0 + ├── partitions scanned: 0 + ├── push downs: [filters: [t1.b (#1) > 3], limit: NONE] + └── estimated rows: 0.00 statement ok SET enable_aggregating_index_scan = 1 @@ -275,35 +259,31 @@ CREATE AGGREGATING INDEX idx1 AS SELECT a + 1 from t1 query T EXPLAIN SELECT avg(a + 1) from t1 ---- -EvalScalar -├── output columns: [avg((a + 1)) (#7)] -├── expressions: [avg((a + 1)) (#6)] +AggregateFinal +├── output columns: [avg((a + 1)) (#6)] +├── group by: [] +├── aggregate functions: [avg(avg_arg_0)] ├── estimated rows: 1.00 -└── AggregateFinal +└── AggregatePartial ├── output columns: [avg((a + 1)) (#6)] ├── group by: [] ├── aggregate functions: [avg(avg_arg_0)] ├── estimated rows: 1.00 - └── AggregatePartial - ├── output columns: [avg((a + 1)) (#6)] - ├── group by: [] - ├── aggregate functions: [avg(avg_arg_0)] - ├── estimated rows: 1.00 - └── EvalScalar - ├── output columns: [avg_arg_0 (#5)] - ├── expressions: [t1.a (#3) + 1] - ├── estimated rows: 0.00 - └── TableScan - ├── table: default.test_index_db.t1 - ├── output columns: [a (#3)] - ├── read rows: 0 - ├── read bytes: 0 - ├── partitions total: 0 - ├── partitions scanned: 0 - ├── push downs: [filters: [], limit: NONE] - ├── aggregating index: [SELECT (a + 1) FROM test_index_db.t1] - ├── rewritten query: [selection: [index_col_0 (#0)]] - └── estimated rows: 0.00 + └── EvalScalar + ├── output columns: [avg_arg_0 (#5)] + ├── expressions: [t1.a (#3) + 1] + ├── estimated rows: 0.00 + └── TableScan + ├── table: default.test_index_db.t1 + ├── output columns: [a (#3)] + ├── read rows: 0 + ├── read bytes: 0 + ├── partitions total: 0 + ├── partitions scanned: 0 + ├── push downs: [filters: [], limit: NONE] + ├── aggregating index: [SELECT (a + 1) FROM test_index_db.t1] + ├── rewritten query: [selection: [index_col_0 (#0)]] + └── estimated rows: 0.00 # Should not be rewritten @@ -432,4 +412,3 @@ USE default statement ok DROP DATABASE IF EXISTS test_virtual_db - diff --git a/tests/sqllogictests/suites/mode/standalone/explain/aggregate.test b/tests/sqllogictests/suites/mode/standalone/explain/aggregate.test index 4a90f86a18e1..bdbbadab869d 100644 --- a/tests/sqllogictests/suites/mode/standalone/explain/aggregate.test +++ b/tests/sqllogictests/suites/mode/standalone/explain/aggregate.test @@ -97,8 +97,8 @@ query T explain select count(3), type, name, trim(name) as a from system.columns group by name, type, a, concat(name, trim(name)), concat(type, name), length(name); ---- EvalScalar -├── output columns: [columns.name (#0), columns.type (#3), count(3) (#14), a (#15)] -├── expressions: [count(3) (#13), trim_both(columns.name (#0), ' ')] +├── output columns: [count(3) (#13), columns.name (#0), columns.type (#3), a (#14)] +├── expressions: [trim_both(columns.name (#0), ' ')] ├── estimated rows: 0.00 └── AggregateFinal ├── output columns: [count(3) (#13), columns.name (#0), columns.type (#3)] @@ -181,19 +181,46 @@ AggregateFinal query T explain select a, max(b) from explain_agg_t1 group by a having a > 1; ---- -EvalScalar -├── output columns: [explain_agg_t1.a (#0), max(b) (#3)] -├── expressions: [max(b) (#2)] +AggregateFinal +├── output columns: [max(b) (#2), explain_agg_t1.a (#0)] +├── group by: [a] +├── aggregate functions: [max(b)] ├── estimated rows: 0.00 -└── AggregateFinal - ├── output columns: [max(b) (#2), explain_agg_t1.a (#0)] +└── AggregatePartial + ├── output columns: [max(b) (#2), #_group_by_key] ├── group by: [a] ├── aggregate functions: [max(b)] ├── estimated rows: 0.00 + └── Filter + ├── output columns: [explain_agg_t1.a (#0), explain_agg_t1.b (#1)] + ├── filters: [explain_agg_t1.a (#0) > 1] + ├── estimated rows: 0.00 + └── TableScan + ├── table: default.default.explain_agg_t1 + ├── output columns: [a (#0), b (#1)] + ├── read rows: 0 + ├── read bytes: 0 + ├── partitions total: 0 + ├── partitions scanned: 0 + ├── push downs: [filters: [explain_agg_t1.a (#0) > 1], limit: NONE] + └── estimated rows: 0.00 + +query T +explain select a, avg(b) from explain_agg_t1 group by a having a > 1 and max(b) > 10; +---- +Filter +├── output columns: [avg(b) (#2), explain_agg_t1.a (#0)] +├── filters: [is_true(max(b) (#3) > 10)] +├── estimated rows: 0.00 +└── AggregateFinal + ├── output columns: [avg(b) (#2), max(b) (#3), explain_agg_t1.a (#0)] + ├── group by: [a] + ├── aggregate functions: [avg(b), max(b)] + ├── estimated rows: 0.00 └── AggregatePartial - ├── output columns: [max(b) (#2), #_group_by_key] + ├── output columns: [avg(b) (#2), max(b) (#3), #_group_by_key] ├── group by: [a] - ├── aggregate functions: [max(b)] + ├── aggregate functions: [avg(b), max(b)] ├── estimated rows: 0.00 └── Filter ├── output columns: [explain_agg_t1.a (#0), explain_agg_t1.b (#1)] @@ -210,29 +237,150 @@ EvalScalar └── estimated rows: 0.00 query T -explain select a, avg(b) from explain_agg_t1 group by a having a > 1 and max(b) > 10; +explain select avg(b) from explain_agg_t1 group by a order by avg(b); ---- -EvalScalar -├── output columns: [explain_agg_t1.a (#0), avg(b) (#3)] -├── expressions: [avg(b) (#2)] +Sort +├── output columns: [avg(b) (#2)] +├── sort keys: [avg(b) ASC NULLS LAST] ├── estimated rows: 0.00 -└── Filter +└── AggregateFinal ├── output columns: [avg(b) (#2), explain_agg_t1.a (#0)] - ├── filters: [is_true(max(b) (#4) > 10)] + ├── group by: [a] + ├── aggregate functions: [avg(b)] + ├── estimated rows: 0.00 + └── AggregatePartial + ├── output columns: [avg(b) (#2), #_group_by_key] + ├── group by: [a] + ├── aggregate functions: [avg(b)] + ├── estimated rows: 0.00 + └── TableScan + ├── table: default.default.explain_agg_t1 + ├── output columns: [a (#0), b (#1)] + ├── read rows: 0 + ├── read bytes: 0 + ├── partitions total: 0 + ├── partitions scanned: 0 + ├── push downs: [filters: [], limit: NONE] + └── estimated rows: 0.00 + + +query T +explain select avg(b) + 1 from explain_agg_t1 group by a order by avg(b); +---- +EvalScalar +├── output columns: [(avg(b) + 1) (#3)] +├── expressions: [avg(b) (#2) + 1] +├── estimated rows: 0.00 +└── Sort + ├── output columns: [avg(b) (#2)] + ├── sort keys: [avg(b) ASC NULLS LAST] ├── estimated rows: 0.00 └── AggregateFinal - ├── output columns: [avg(b) (#2), max(b) (#4), explain_agg_t1.a (#0)] + ├── output columns: [avg(b) (#2), explain_agg_t1.a (#0)] ├── group by: [a] - ├── aggregate functions: [avg(b), max(b)] + ├── aggregate functions: [avg(b)] + ├── estimated rows: 0.00 + └── AggregatePartial + ├── output columns: [avg(b) (#2), #_group_by_key] + ├── group by: [a] + ├── aggregate functions: [avg(b)] + ├── estimated rows: 0.00 + └── TableScan + ├── table: default.default.explain_agg_t1 + ├── output columns: [a (#0), b (#1)] + ├── read rows: 0 + ├── read bytes: 0 + ├── partitions total: 0 + ├── partitions scanned: 0 + ├── push downs: [filters: [], limit: NONE] + └── estimated rows: 0.00 + +query T +explain select avg(b), avg(b) + 1 from explain_agg_t1 group by a order by avg(b); +---- +EvalScalar +├── output columns: [avg(b) (#2), (avg(b) + 1) (#3)] +├── expressions: [avg(b) (#2) + 1] +├── estimated rows: 0.00 +└── Sort + ├── output columns: [avg(b) (#2)] + ├── sort keys: [avg(b) ASC NULLS LAST] + ├── estimated rows: 0.00 + └── AggregateFinal + ├── output columns: [avg(b) (#2), explain_agg_t1.a (#0)] + ├── group by: [a] + ├── aggregate functions: [avg(b)] + ├── estimated rows: 0.00 + └── AggregatePartial + ├── output columns: [avg(b) (#2), #_group_by_key] + ├── group by: [a] + ├── aggregate functions: [avg(b)] + ├── estimated rows: 0.00 + └── TableScan + ├── table: default.default.explain_agg_t1 + ├── output columns: [a (#0), b (#1)] + ├── read rows: 0 + ├── read bytes: 0 + ├── partitions total: 0 + ├── partitions scanned: 0 + ├── push downs: [filters: [], limit: NONE] + └── estimated rows: 0.00 + +query T +explain select avg(b) + 1, avg(b) from explain_agg_t1 group by a order by avg(b); +---- +EvalScalar +├── output columns: [avg(b) (#2), (avg(b) + 1) (#3)] +├── expressions: [avg(b) (#2) + 1] +├── estimated rows: 0.00 +└── Sort + ├── output columns: [avg(b) (#2)] + ├── sort keys: [avg(b) ASC NULLS LAST] + ├── estimated rows: 0.00 + └── AggregateFinal + ├── output columns: [avg(b) (#2), explain_agg_t1.a (#0)] + ├── group by: [a] + ├── aggregate functions: [avg(b)] ├── estimated rows: 0.00 └── AggregatePartial - ├── output columns: [avg(b) (#2), max(b) (#4), #_group_by_key] + ├── output columns: [avg(b) (#2), #_group_by_key] ├── group by: [a] - ├── aggregate functions: [avg(b), max(b)] + ├── aggregate functions: [avg(b)] ├── estimated rows: 0.00 - └── Filter - ├── output columns: [explain_agg_t1.a (#0), explain_agg_t1.b (#1)] - ├── filters: [explain_agg_t1.a (#0) > 1] + └── TableScan + ├── table: default.default.explain_agg_t1 + ├── output columns: [a (#0), b (#1)] + ├── read rows: 0 + ├── read bytes: 0 + ├── partitions total: 0 + ├── partitions scanned: 0 + ├── push downs: [filters: [], limit: NONE] + └── estimated rows: 0.00 + +query T +explain select avg(b), avg(b) + 1 from explain_agg_t1 group by a order by avg(b) + 1; +---- +EvalScalar +├── output columns: [avg(b) (#2), (avg(b) + 1) (#3)] +├── expressions: [avg(b) (#2) + 1] +├── estimated rows: 0.00 +└── Sort + ├── output columns: [avg(b) (#2), (avg(b) + 1) (#4)] + ├── sort keys: [(avg(b) + 1) ASC NULLS LAST] + ├── estimated rows: 0.00 + └── EvalScalar + ├── output columns: [avg(b) (#2), (avg(b) + 1) (#4)] + ├── expressions: [avg(b) (#2) + 1] + ├── estimated rows: 0.00 + └── AggregateFinal + ├── output columns: [avg(b) (#2), explain_agg_t1.a (#0)] + ├── group by: [a] + ├── aggregate functions: [avg(b)] + ├── estimated rows: 0.00 + └── AggregatePartial + ├── output columns: [avg(b) (#2), #_group_by_key] + ├── group by: [a] + ├── aggregate functions: [avg(b)] ├── estimated rows: 0.00 └── TableScan ├── table: default.default.explain_agg_t1 @@ -241,7 +389,7 @@ EvalScalar ├── read bytes: 0 ├── partitions total: 0 ├── partitions scanned: 0 - ├── push downs: [filters: [explain_agg_t1.a (#0) > 1], limit: NONE] + ├── push downs: [filters: [], limit: NONE] └── estimated rows: 0.00 statement ok @@ -262,46 +410,42 @@ create table t2 as select number as a from numbers(100) query T explain select count() from t1, t2 where t1.a > t2.a; ---- -EvalScalar -├── output columns: [count() (#3)] -├── expressions: [count() (#2)] +AggregateFinal +├── output columns: [count() (#2)] +├── group by: [] +├── aggregate functions: [count()] ├── estimated rows: 1.00 -└── AggregateFinal +└── AggregatePartial ├── output columns: [count() (#2)] ├── group by: [] ├── aggregate functions: [count()] ├── estimated rows: 1.00 - └── AggregatePartial - ├── output columns: [count() (#2)] - ├── group by: [] - ├── aggregate functions: [count()] - ├── estimated rows: 1.00 - └── MergeJoin - ├── output columns: [t1.a (#0), t2.a (#1)] - ├── join type: INNER - ├── range join conditions: [t1.a (#0) "gt" t2.a (#1)] - ├── other conditions: [] - ├── estimated rows: 1000.00 - ├── TableScan(Left) - │ ├── table: default.default.t1 - │ ├── output columns: [a (#0)] - │ ├── read rows: 10 - │ ├── read bytes: 65 - │ ├── partitions total: 1 - │ ├── partitions scanned: 1 - │ ├── pruning stats: [segments: , blocks: ] - │ ├── push downs: [filters: [], limit: NONE] - │ └── estimated rows: 10.00 - └── TableScan(Right) - ├── table: default.default.t2 - ├── output columns: [a (#1)] - ├── read rows: 100 - ├── read bytes: 172 - ├── partitions total: 1 - ├── partitions scanned: 1 - ├── pruning stats: [segments: , blocks: ] - ├── push downs: [filters: [], limit: NONE] - └── estimated rows: 100.00 + └── MergeJoin + ├── output columns: [t1.a (#0), t2.a (#1)] + ├── join type: INNER + ├── range join conditions: [t1.a (#0) "gt" t2.a (#1)] + ├── other conditions: [] + ├── estimated rows: 1000.00 + ├── TableScan(Left) + │ ├── table: default.default.t1 + │ ├── output columns: [a (#0)] + │ ├── read rows: 10 + │ ├── read bytes: 65 + │ ├── partitions total: 1 + │ ├── partitions scanned: 1 + │ ├── pruning stats: [segments: , blocks: ] + │ ├── push downs: [filters: [], limit: NONE] + │ └── estimated rows: 10.00 + └── TableScan(Right) + ├── table: default.default.t2 + ├── output columns: [a (#1)] + ├── read rows: 100 + ├── read bytes: 172 + ├── partitions total: 1 + ├── partitions scanned: 1 + ├── pruning stats: [segments: , blocks: ] + ├── push downs: [filters: [], limit: NONE] + └── estimated rows: 100.00 statement ok drop table t1; diff --git a/tests/sqllogictests/suites/mode/standalone/explain/explain.test b/tests/sqllogictests/suites/mode/standalone/explain/explain.test index 132b69430e93..99e0116e013b 100644 --- a/tests/sqllogictests/suites/mode/standalone/explain/explain.test +++ b/tests/sqllogictests/suites/mode/standalone/explain/explain.test @@ -908,78 +908,70 @@ HashJoin query explain select count(distinct a) from t1; ---- -EvalScalar -├── output columns: [count() (#3)] -├── expressions: [count() (#2)] +AggregateFinal +├── output columns: [count() (#2)] +├── group by: [] +├── aggregate functions: [count()] ├── estimated rows: 1.00 -└── AggregateFinal +└── AggregatePartial ├── output columns: [count() (#2)] ├── group by: [] ├── aggregate functions: [count()] ├── estimated rows: 1.00 - └── AggregatePartial - ├── output columns: [count() (#2)] - ├── group by: [] - ├── aggregate functions: [count()] + └── AggregateFinal + ├── output columns: [t1.a (#0)] + ├── group by: [a] + ├── aggregate functions: [] ├── estimated rows: 1.00 - └── AggregateFinal - ├── output columns: [t1.a (#0)] + └── AggregatePartial + ├── output columns: [#_group_by_key] ├── group by: [a] ├── aggregate functions: [] ├── estimated rows: 1.00 - └── AggregatePartial - ├── output columns: [#_group_by_key] - ├── group by: [a] - ├── aggregate functions: [] - ├── estimated rows: 1.00 - └── TableScan - ├── table: default.default.t1 - ├── output columns: [a (#0)] - ├── read rows: 1 - ├── read bytes: 39 - ├── partitions total: 1 - ├── partitions scanned: 1 - ├── pruning stats: [segments: , blocks: ] - ├── push downs: [filters: [], limit: NONE] - └── estimated rows: 1.00 + └── TableScan + ├── table: default.default.t1 + ├── output columns: [a (#0)] + ├── read rows: 1 + ├── read bytes: 39 + ├── partitions total: 1 + ├── partitions scanned: 1 + ├── pruning stats: [segments: , blocks: ] + ├── push downs: [filters: [], limit: NONE] + └── estimated rows: 1.00 query explain select count_distinct(a) from t1; ---- -EvalScalar -├── output columns: [count() (#3)] -├── expressions: [count() (#2)] +AggregateFinal +├── output columns: [count() (#2)] +├── group by: [] +├── aggregate functions: [count()] ├── estimated rows: 1.00 -└── AggregateFinal +└── AggregatePartial ├── output columns: [count() (#2)] ├── group by: [] ├── aggregate functions: [count()] ├── estimated rows: 1.00 - └── AggregatePartial - ├── output columns: [count() (#2)] - ├── group by: [] - ├── aggregate functions: [count()] + └── AggregateFinal + ├── output columns: [t1.a (#0)] + ├── group by: [a] + ├── aggregate functions: [] ├── estimated rows: 1.00 - └── AggregateFinal - ├── output columns: [t1.a (#0)] + └── AggregatePartial + ├── output columns: [#_group_by_key] ├── group by: [a] ├── aggregate functions: [] ├── estimated rows: 1.00 - └── AggregatePartial - ├── output columns: [#_group_by_key] - ├── group by: [a] - ├── aggregate functions: [] - ├── estimated rows: 1.00 - └── TableScan - ├── table: default.default.t1 - ├── output columns: [a (#0)] - ├── read rows: 1 - ├── read bytes: 39 - ├── partitions total: 1 - ├── partitions scanned: 1 - ├── pruning stats: [segments: , blocks: ] - ├── push downs: [filters: [], limit: NONE] - └── estimated rows: 1.00 + └── TableScan + ├── table: default.default.t1 + ├── output columns: [a (#0)] + ├── read rows: 1 + ├── read bytes: 39 + ├── partitions total: 1 + ├── partitions scanned: 1 + ├── pruning stats: [segments: , blocks: ] + ├── push downs: [filters: [], limit: NONE] + └── estimated rows: 1.00 query explain select * from (values(1, 'a'),(2, 'b')) t(c1,c2) diff --git a/tests/sqllogictests/suites/mode/standalone/explain/fold_count.test b/tests/sqllogictests/suites/mode/standalone/explain/fold_count.test index b11c2be7f1f9..0feabd7dcd66 100644 --- a/tests/sqllogictests/suites/mode/standalone/explain/fold_count.test +++ b/tests/sqllogictests/suites/mode/standalone/explain/fold_count.test @@ -8,14 +8,10 @@ query T explain select count(*) from t ---- EvalScalar -├── output columns: [count(*) (#2)] -├── expressions: [COUNT(*) (#1)] +├── output columns: [COUNT(*) (#1)] +├── expressions: [1000] ├── estimated rows: 1.00 -└── EvalScalar - ├── output columns: [COUNT(*) (#1)] - ├── expressions: [1000] - ├── estimated rows: 1.00 - └── DummyTableScan +└── DummyTableScan statement ok insert into t values(1) @@ -24,46 +20,38 @@ query T explain select count(*) from t ---- EvalScalar -├── output columns: [count(*) (#2)] -├── expressions: [COUNT(*) (#1)] +├── output columns: [COUNT(*) (#1)] +├── expressions: [1001] ├── estimated rows: 1.00 -└── EvalScalar - ├── output columns: [COUNT(*) (#1)] - ├── expressions: [1001] - ├── estimated rows: 1.00 - └── DummyTableScan +└── DummyTableScan query T explain select count(*) from t where number > 10 ---- -EvalScalar -├── output columns: [count(*) (#2)] -├── expressions: [COUNT(*) (#1)] +AggregateFinal +├── output columns: [COUNT(*) (#1)] +├── group by: [] +├── aggregate functions: [count()] ├── estimated rows: 1.00 -└── AggregateFinal +└── AggregatePartial ├── output columns: [COUNT(*) (#1)] ├── group by: [] ├── aggregate functions: [count()] ├── estimated rows: 1.00 - └── AggregatePartial - ├── output columns: [COUNT(*) (#1)] - ├── group by: [] - ├── aggregate functions: [count()] - ├── estimated rows: 1.00 - └── Filter - ├── output columns: [] - ├── filters: [t.number (#0) > 10] - ├── estimated rows: 990.00 - └── TableScan - ├── table: default.default.t - ├── output columns: [number (#0)] - ├── read rows: 1000 - ├── read bytes: 1598 - ├── partitions total: 2 - ├── partitions scanned: 1 - ├── pruning stats: [segments: , blocks: ] - ├── push downs: [filters: [t.number (#0) > 10], limit: NONE] - └── estimated rows: 1001.00 + └── Filter + ├── output columns: [] + ├── filters: [t.number (#0) > 10] + ├── estimated rows: 990.00 + └── TableScan + ├── table: default.default.t + ├── output columns: [number (#0)] + ├── read rows: 1000 + ├── read bytes: 1598 + ├── partitions total: 2 + ├── partitions scanned: 1 + ├── pruning stats: [segments: , blocks: ] + ├── push downs: [filters: [t.number (#0) > 10], limit: NONE] + └── estimated rows: 1001.00 statement ok drop table t diff --git a/tests/sqllogictests/suites/mode/standalone/explain/limit.test b/tests/sqllogictests/suites/mode/standalone/explain/limit.test index b0d605083b6f..64f86ea1fc03 100644 --- a/tests/sqllogictests/suites/mode/standalone/explain/limit.test +++ b/tests/sqllogictests/suites/mode/standalone/explain/limit.test @@ -97,54 +97,50 @@ Limit ├── estimated rows: 0.20 └── Filter ├── output columns: [t.number (#0)] - ├── filters: [is_true(CAST(t.number (#0) AS UInt64 NULL) = if(CAST(is_not_null(scalar_subquery_5 (#5)) AS Boolean NULL), scalar_subquery_5 (#5), 0))] + ├── filters: [is_true(CAST(t.number (#0) AS UInt64 NULL) = if(CAST(is_not_null(scalar_subquery_4 (#4)) AS Boolean NULL), scalar_subquery_4 (#4), 0))] ├── estimated rows: 0.20 └── HashJoin - ├── output columns: [t.number (#0), count(*) (#5)] + ├── output columns: [t.number (#0), COUNT(*) (#4)] ├── join type: LEFT SINGLE ├── build keys: [number (#2)] ├── probe keys: [CAST(number (#0) AS UInt64 NULL)] ├── filters: [] ├── estimated rows: 1.00 - ├── EvalScalar(Build) - │ ├── output columns: [t2.number (#2), count(*) (#5)] - │ ├── expressions: [COUNT(*) (#4)] + ├── AggregateFinal(Build) + │ ├── output columns: [COUNT(*) (#4), t2.number (#2)] + │ ├── group by: [number] + │ ├── aggregate functions: [count()] │ ├── estimated rows: 1.00 - │ └── AggregateFinal - │ ├── output columns: [COUNT(*) (#4), t2.number (#2)] + │ └── AggregatePartial + │ ├── output columns: [COUNT(*) (#4), #_group_by_key] │ ├── group by: [number] │ ├── aggregate functions: [count()] │ ├── estimated rows: 1.00 - │ └── AggregatePartial - │ ├── output columns: [COUNT(*) (#4), #_group_by_key] - │ ├── group by: [number] - │ ├── aggregate functions: [count()] + │ └── HashJoin + │ ├── output columns: [t2.number (#2)] + │ ├── join type: CROSS + │ ├── build keys: [] + │ ├── probe keys: [] + │ ├── filters: [] │ ├── estimated rows: 1.00 - │ └── HashJoin - │ ├── output columns: [t2.number (#2)] - │ ├── join type: CROSS - │ ├── build keys: [] - │ ├── probe keys: [] - │ ├── filters: [] - │ ├── estimated rows: 1.00 - │ ├── TableScan(Build) - │ │ ├── table: default.system.numbers - │ │ ├── output columns: [] - │ │ ├── read rows: 1 - │ │ ├── read bytes: 8 - │ │ ├── partitions total: 1 - │ │ ├── partitions scanned: 1 - │ │ ├── push downs: [filters: [], limit: NONE] - │ │ └── estimated rows: 1.00 - │ └── TableScan(Probe) - │ ├── table: default.system.numbers - │ ├── output columns: [number (#2)] - │ ├── read rows: 1 - │ ├── read bytes: 8 - │ ├── partitions total: 1 - │ ├── partitions scanned: 1 - │ ├── push downs: [filters: [], limit: NONE] - │ └── estimated rows: 1.00 + │ ├── TableScan(Build) + │ │ ├── table: default.system.numbers + │ │ ├── output columns: [] + │ │ ├── read rows: 1 + │ │ ├── read bytes: 8 + │ │ ├── partitions total: 1 + │ │ ├── partitions scanned: 1 + │ │ ├── push downs: [filters: [], limit: NONE] + │ │ └── estimated rows: 1.00 + │ └── TableScan(Probe) + │ ├── table: default.system.numbers + │ ├── output columns: [number (#2)] + │ ├── read rows: 1 + │ ├── read bytes: 8 + │ ├── partitions total: 1 + │ ├── partitions scanned: 1 + │ ├── push downs: [filters: [], limit: NONE] + │ └── estimated rows: 1.00 └── HashJoin(Probe) ├── output columns: [t.number (#0)] ├── join type: CROSS @@ -175,64 +171,56 @@ query T explain select * from (select count(t1.number) as c1 from numbers(1) as t1 group by number) as t3 left join (select count(t.number) as c from numbers(2) as t group by number) as t4 on t3.c1=t4.c order by t3.c1 limit 1 ---- Limit -├── output columns: [c (#7), c1 (#3)] +├── output columns: [count(t.number) (#5), count(t1.number) (#2)] ├── limit: 1 ├── offset: 0 ├── estimated rows: 1.00 └── Sort - ├── output columns: [c (#7), c1 (#3)] - ├── sort keys: [c1 ASC NULLS LAST] + ├── output columns: [count(t.number) (#5), count(t1.number) (#2)] + ├── sort keys: [count(t1.number) ASC NULLS LAST] ├── estimated rows: 2.00 └── HashJoin - ├── output columns: [c (#7), c1 (#3)] + ├── output columns: [count(t.number) (#5), count(t1.number) (#2)] ├── join type: RIGHT OUTER - ├── build keys: [CAST(t3.c1 (#3) AS UInt64 NULL)] - ├── probe keys: [t4.c (#7)] + ├── build keys: [CAST(t3.c1 (#2) AS UInt64 NULL)] + ├── probe keys: [t4.c (#5)] ├── filters: [] ├── estimated rows: 2.00 - ├── EvalScalar(Build) - │ ├── output columns: [c1 (#3)] - │ ├── expressions: [count(t1.number) (#2)] + ├── AggregateFinal(Build) + │ ├── output columns: [count(t1.number) (#2), t1.number (#0)] + │ ├── group by: [number] + │ ├── aggregate functions: [count(number)] │ ├── estimated rows: 1.00 - │ └── AggregateFinal - │ ├── output columns: [count(t1.number) (#2), t1.number (#0)] + │ └── AggregatePartial + │ ├── output columns: [count(t1.number) (#2), #_group_by_key] │ ├── group by: [number] │ ├── aggregate functions: [count(number)] │ ├── estimated rows: 1.00 - │ └── AggregatePartial - │ ├── output columns: [count(t1.number) (#2), #_group_by_key] - │ ├── group by: [number] - │ ├── aggregate functions: [count(number)] - │ ├── estimated rows: 1.00 - │ └── TableScan - │ ├── table: default.system.numbers - │ ├── output columns: [number (#0)] - │ ├── read rows: 1 - │ ├── read bytes: 8 - │ ├── partitions total: 1 - │ ├── partitions scanned: 1 - │ ├── push downs: [filters: [], limit: NONE] - │ └── estimated rows: 1.00 - └── EvalScalar(Probe) - ├── output columns: [c (#7)] - ├── expressions: [count(t.number) (#6)] + │ └── TableScan + │ ├── table: default.system.numbers + │ ├── output columns: [number (#0)] + │ ├── read rows: 1 + │ ├── read bytes: 8 + │ ├── partitions total: 1 + │ ├── partitions scanned: 1 + │ ├── push downs: [filters: [], limit: NONE] + │ └── estimated rows: 1.00 + └── AggregateFinal(Probe) + ├── output columns: [count(t.number) (#5), t.number (#3)] + ├── group by: [number] + ├── aggregate functions: [count(number)] ├── estimated rows: 2.00 - └── AggregateFinal - ├── output columns: [count(t.number) (#6), t.number (#4)] + └── AggregatePartial + ├── output columns: [count(t.number) (#5), #_group_by_key] ├── group by: [number] ├── aggregate functions: [count(number)] ├── estimated rows: 2.00 - └── AggregatePartial - ├── output columns: [count(t.number) (#6), #_group_by_key] - ├── group by: [number] - ├── aggregate functions: [count(number)] - ├── estimated rows: 2.00 - └── TableScan - ├── table: default.system.numbers - ├── output columns: [number (#4)] - ├── read rows: 2 - ├── read bytes: 16 - ├── partitions total: 1 - ├── partitions scanned: 1 - ├── push downs: [filters: [], limit: NONE] - └── estimated rows: 2.00 + └── TableScan + ├── table: default.system.numbers + ├── output columns: [number (#3)] + ├── read rows: 2 + ├── read bytes: 16 + ├── partitions total: 1 + ├── partitions scanned: 1 + ├── push downs: [filters: [], limit: NONE] + └── estimated rows: 2.00 diff --git a/tests/sqllogictests/suites/mode/standalone/explain/project_set.test b/tests/sqllogictests/suites/mode/standalone/explain/project_set.test index d793d7c1e745..223b01ed0e07 100644 --- a/tests/sqllogictests/suites/mode/standalone/explain/project_set.test +++ b/tests/sqllogictests/suites/mode/standalone/explain/project_set.test @@ -13,34 +13,30 @@ insert into fold_count values([1,2,3,4], 'x'); query T explain select count() from (select unnest(id), c1 from fold_count) ---- -EvalScalar -├── output columns: [count() (#5)] -├── expressions: [count() (#4)] +AggregateFinal +├── output columns: [count() (#4)] +├── group by: [] +├── aggregate functions: [count()] ├── estimated rows: 1.00 -└── AggregateFinal +└── AggregatePartial ├── output columns: [count() (#4)] ├── group by: [] ├── aggregate functions: [count()] ├── estimated rows: 1.00 - └── AggregatePartial - ├── output columns: [count() (#4)] - ├── group by: [] - ├── aggregate functions: [count()] + └── ProjectSet + ├── output columns: [unnest (#2)] ├── estimated rows: 1.00 - └── ProjectSet - ├── output columns: [unnest (#2)] - ├── estimated rows: 1.00 - ├── set returning functions: unnest(CAST(fold_count.id (#0) AS Array(Int32 NULL) NULL)) - └── TableScan - ├── table: default.default.fold_count - ├── output columns: [id (#0)] - ├── read rows: 1 - ├── read bytes: 51 - ├── partitions total: 1 - ├── partitions scanned: 1 - ├── pruning stats: [segments: , blocks: ] - ├── push downs: [filters: [], limit: NONE] - └── estimated rows: 1.00 + ├── set returning functions: unnest(CAST(fold_count.id (#0) AS Array(Int32 NULL) NULL)) + └── TableScan + ├── table: default.default.fold_count + ├── output columns: [id (#0)] + ├── read rows: 1 + ├── read bytes: 51 + ├── partitions total: 1 + ├── partitions scanned: 1 + ├── pruning stats: [segments: , blocks: ] + ├── push downs: [filters: [], limit: NONE] + └── estimated rows: 1.00 statement ok drop table fold_count; diff --git a/tests/sqllogictests/suites/mode/standalone/explain/prune_column.test b/tests/sqllogictests/suites/mode/standalone/explain/prune_column.test index 5bdd98baf468..1791804fada2 100644 --- a/tests/sqllogictests/suites/mode/standalone/explain/prune_column.test +++ b/tests/sqllogictests/suites/mode/standalone/explain/prune_column.test @@ -117,81 +117,77 @@ explain select t1.a from (select number + 1 as a, number + 1 as b from numbers(1 HashJoin ├── output columns: [a (#1)] ├── join type: INNER -├── build keys: [_if_scalar_subquery (#16)] +├── build keys: [_if_scalar_subquery (#15)] ├── probe keys: [CAST(t1.a (#1) AS UInt64 NULL)] ├── filters: [] ├── estimated rows: 1.00 ├── EvalScalar(Build) -│ ├── output columns: [_if_scalar_subquery (#16)] -│ ├── expressions: [if(CAST(_count_scalar_subquery (#14) = 0 AS Boolean NULL), NULL, _any_scalar_subquery (#15))] +│ ├── output columns: [_if_scalar_subquery (#15)] +│ ├── expressions: [if(CAST(_count_scalar_subquery (#13) = 0 AS Boolean NULL), NULL, _any_scalar_subquery (#14))] │ ├── estimated rows: 1.00 │ └── Limit -│ ├── output columns: [_count_scalar_subquery (#14), _any_scalar_subquery (#15)] +│ ├── output columns: [_count_scalar_subquery (#13), _any_scalar_subquery (#14)] │ ├── limit: 1 │ ├── offset: 0 │ ├── estimated rows: 1.00 │ └── AggregateFinal -│ ├── output columns: [_count_scalar_subquery (#14), _any_scalar_subquery (#15)] +│ ├── output columns: [_count_scalar_subquery (#13), _any_scalar_subquery (#14)] │ ├── group by: [] -│ ├── aggregate functions: [count(count(*)), any(count(*))] +│ ├── aggregate functions: [count(COUNT(*)), any(COUNT(*))] │ ├── limit: 1 │ ├── estimated rows: 1.00 │ └── AggregatePartial -│ ├── output columns: [_count_scalar_subquery (#14), _any_scalar_subquery (#15)] +│ ├── output columns: [_count_scalar_subquery (#13), _any_scalar_subquery (#14)] │ ├── group by: [] -│ ├── aggregate functions: [count(count(*)), any(count(*))] +│ ├── aggregate functions: [count(COUNT(*)), any(COUNT(*))] │ ├── estimated rows: 1.00 -│ └── EvalScalar -│ ├── output columns: [count(*) (#13)] -│ ├── expressions: [COUNT(*) (#12)] +│ └── AggregateFinal +│ ├── output columns: [COUNT(*) (#12)] +│ ├── group by: [] +│ ├── aggregate functions: [count()] │ ├── estimated rows: 1.00 -│ └── AggregateFinal +│ └── AggregatePartial │ ├── output columns: [COUNT(*) (#12)] │ ├── group by: [] │ ├── aggregate functions: [count()] │ ├── estimated rows: 1.00 -│ └── AggregatePartial -│ ├── output columns: [COUNT(*) (#12)] -│ ├── group by: [] -│ ├── aggregate functions: [count()] -│ ├── estimated rows: 1.00 -│ └── HashJoin -│ ├── output columns: [] -│ ├── join type: INNER -│ ├── build keys: [t2.b (#5)] -│ ├── probe keys: [t3.b (#10)] -│ ├── filters: [] -│ ├── estimated rows: 0.20 -│ ├── EvalScalar(Build) -│ │ ├── output columns: [b (#5)] -│ │ ├── expressions: [numbers.number (#3) + 1] -│ │ ├── estimated rows: 0.20 -│ │ └── Filter -│ │ ├── output columns: [numbers.number (#3)] -│ │ ├── filters: [numbers.number (#3) + 1 = 1] -│ │ ├── estimated rows: 0.20 -│ │ └── TableScan -│ │ ├── table: default.system.numbers -│ │ ├── output columns: [number (#3)] -│ │ ├── read rows: 1 -│ │ ├── read bytes: 8 -│ │ ├── partitions total: 1 -│ │ ├── partitions scanned: 1 -│ │ ├── push downs: [filters: [numbers.number (#3) + 1 = 1], limit: NONE] -│ │ └── estimated rows: 1.00 -│ └── EvalScalar(Probe) -│ ├── output columns: [b (#10)] -│ ├── expressions: [numbers.number (#8) + 1] -│ ├── estimated rows: 1.00 -│ └── TableScan -│ ├── table: default.system.numbers -│ ├── output columns: [number (#8)] -│ ├── read rows: 1 -│ ├── read bytes: 8 -│ ├── partitions total: 1 -│ ├── partitions scanned: 1 -│ ├── push downs: [filters: [], limit: NONE] -│ └── estimated rows: 1.00 +│ └── HashJoin +│ ├── output columns: [] +│ ├── join type: INNER +│ ├── build keys: [t2.b (#5)] +│ ├── probe keys: [t3.b (#10)] +│ ├── filters: [] +│ ├── estimated rows: 0.20 +│ ├── EvalScalar(Build) +│ │ ├── output columns: [b (#5)] +│ │ ├── expressions: [numbers.number (#3) + 1] +│ │ ├── estimated rows: 0.20 +│ │ └── Filter +│ │ ├── output columns: [numbers.number (#3)] +│ │ ├── filters: [numbers.number (#3) + 1 = 1] +│ │ ├── estimated rows: 0.20 +│ │ └── TableScan +│ │ ├── table: default.system.numbers +│ │ ├── output columns: [number (#3)] +│ │ ├── read rows: 1 +│ │ ├── read bytes: 8 +│ │ ├── partitions total: 1 +│ │ ├── partitions scanned: 1 +│ │ ├── push downs: [filters: [numbers.number (#3) + 1 = 1], limit: NONE] +│ │ └── estimated rows: 1.00 +│ └── EvalScalar(Probe) +│ ├── output columns: [b (#10)] +│ ├── expressions: [numbers.number (#8) + 1] +│ ├── estimated rows: 1.00 +│ └── TableScan +│ ├── table: default.system.numbers +│ ├── output columns: [number (#8)] +│ ├── read rows: 1 +│ ├── read bytes: 8 +│ ├── partitions total: 1 +│ ├── partitions scanned: 1 +│ ├── push downs: [filters: [], limit: NONE] +│ └── estimated rows: 1.00 └── EvalScalar(Probe) ├── output columns: [a (#1)] ├── expressions: [numbers.number (#0) + 1] @@ -284,34 +280,30 @@ insert into t values(1, 2), (2, 3) query T explain select count(*) from t where t.b = 2 ---- -EvalScalar -├── output columns: [count(*) (#3)] -├── expressions: [COUNT(*) (#2)] +AggregateFinal +├── output columns: [COUNT(*) (#2)] +├── group by: [] +├── aggregate functions: [count()] ├── estimated rows: 1.00 -└── AggregateFinal +└── AggregatePartial ├── output columns: [COUNT(*) (#2)] ├── group by: [] ├── aggregate functions: [count()] ├── estimated rows: 1.00 - └── AggregatePartial - ├── output columns: [COUNT(*) (#2)] - ├── group by: [] - ├── aggregate functions: [count()] + └── Filter + ├── output columns: [] + ├── filters: [is_true(t.b (#1) = 2)] ├── estimated rows: 1.00 - └── Filter - ├── output columns: [] - ├── filters: [is_true(t.b (#1) = 2)] - ├── estimated rows: 1.00 - └── TableScan - ├── table: default.default.t - ├── output columns: [b (#1)] - ├── read rows: 2 - ├── read bytes: 41 - ├── partitions total: 1 - ├── partitions scanned: 1 - ├── pruning stats: [segments: , blocks: ] - ├── push downs: [filters: [is_true(t.b (#1) = 2)], limit: NONE] - └── estimated rows: 2.00 + └── TableScan + ├── table: default.default.t + ├── output columns: [b (#1)] + ├── read rows: 2 + ├── read bytes: 41 + ├── partitions total: 1 + ├── partitions scanned: 1 + ├── pruning stats: [segments: , blocks: ] + ├── push downs: [filters: [is_true(t.b (#1) = 2)], limit: NONE] + └── estimated rows: 2.00 statement ok drop table t diff --git a/tests/sqllogictests/suites/mode/standalone/explain/push_down_filter.test b/tests/sqllogictests/suites/mode/standalone/explain/push_down_filter.test index 7a6b28bdaa4a..e83d0174f440 100644 --- a/tests/sqllogictests/suites/mode/standalone/explain/push_down_filter.test +++ b/tests/sqllogictests/suites/mode/standalone/explain/push_down_filter.test @@ -142,56 +142,52 @@ AggregateFinal ├── aggregate functions: [] ├── estimated rows: 0.00 └── UnionAll - ├── output columns: [t.id (#0), de (#10)] + ├── output columns: [t.id (#0), de (#8)] ├── estimated rows: 0.00 ├── EvalScalar - │ ├── output columns: [t.id (#0), de (#10)] - │ ├── expressions: [if(CAST(is_not_null(sum(tb.de) (#9)) AS Boolean NULL), CAST(assume_not_null(sum(tb.de) (#9)) AS Int64 NULL), true, 0, NULL)] + │ ├── output columns: [t.id (#0), de (#8)] + │ ├── expressions: [if(CAST(is_not_null(sum(tb.de) (#7)) AS Boolean NULL), CAST(assume_not_null(sum(tb.de) (#7)) AS Int64 NULL), true, 0, NULL)] │ ├── estimated rows: 0.00 │ └── AggregateFinal - │ ├── output columns: [sum(tb.de) (#9), t.id (#0)] + │ ├── output columns: [sum(tb.de) (#7), t.id (#0)] │ ├── group by: [id] - │ ├── aggregate functions: [sum(de)] + │ ├── aggregate functions: [sum(sum(coalesce(t3.val, 0)))] │ ├── estimated rows: 0.00 │ └── AggregatePartial - │ ├── output columns: [sum(tb.de) (#9), #_group_by_key] + │ ├── output columns: [sum(tb.de) (#7), #_group_by_key] │ ├── group by: [id] - │ ├── aggregate functions: [sum(de)] + │ ├── aggregate functions: [sum(sum(coalesce(t3.val, 0)))] │ ├── estimated rows: 0.00 │ └── HashJoin - │ ├── output columns: [t.id (#0), de (#6)] + │ ├── output columns: [t.id (#0), sum(coalesce(t3.val, 0)) (#5)] │ ├── join type: LEFT OUTER │ ├── build keys: [tb.sid (#1)] │ ├── probe keys: [t.id (#0)] │ ├── filters: [] │ ├── estimated rows: 0.00 - │ ├── EvalScalar(Build) - │ │ ├── output columns: [t2.sid (#1), de (#6)] - │ │ ├── expressions: [sum(coalesce(t3.val, 0)) (#5)] + │ ├── AggregateFinal(Build) + │ │ ├── output columns: [sum(coalesce(t3.val, 0)) (#5), t2.sid (#1)] + │ │ ├── group by: [sid] + │ │ ├── aggregate functions: [sum(sum_arg_0)] │ │ ├── estimated rows: 0.00 - │ │ └── AggregateFinal - │ │ ├── output columns: [sum(coalesce(t3.val, 0)) (#5), t2.sid (#1)] + │ │ └── AggregatePartial + │ │ ├── output columns: [sum(coalesce(t3.val, 0)) (#5), #_group_by_key] │ │ ├── group by: [sid] │ │ ├── aggregate functions: [sum(sum_arg_0)] │ │ ├── estimated rows: 0.00 - │ │ └── AggregatePartial - │ │ ├── output columns: [sum(coalesce(t3.val, 0)) (#5), #_group_by_key] - │ │ ├── group by: [sid] - │ │ ├── aggregate functions: [sum(sum_arg_0)] + │ │ └── EvalScalar + │ │ ├── output columns: [t2.sid (#1), sum_arg_0 (#4)] + │ │ ├── expressions: [if(CAST(is_not_null(t3.val (#2)) AS Boolean NULL), CAST(assume_not_null(t3.val (#2)) AS Int32 NULL), true, 0, NULL)] │ │ ├── estimated rows: 0.00 - │ │ └── EvalScalar - │ │ ├── output columns: [t2.sid (#1), sum_arg_0 (#4)] - │ │ ├── expressions: [if(CAST(is_not_null(t3.val (#2)) AS Boolean NULL), CAST(assume_not_null(t3.val (#2)) AS Int32 NULL), true, 0, NULL)] - │ │ ├── estimated rows: 0.00 - │ │ └── TableScan - │ │ ├── table: default.default.t2 - │ │ ├── output columns: [sid (#1), val (#2)] - │ │ ├── read rows: 0 - │ │ ├── read bytes: 0 - │ │ ├── partitions total: 0 - │ │ ├── partitions scanned: 0 - │ │ ├── push downs: [filters: [], limit: NONE] - │ │ └── estimated rows: 0.00 + │ │ └── TableScan + │ │ ├── table: default.default.t2 + │ │ ├── output columns: [sid (#1), val (#2)] + │ │ ├── read rows: 0 + │ │ ├── read bytes: 0 + │ │ ├── partitions total: 0 + │ │ ├── partitions scanned: 0 + │ │ ├── push downs: [filters: [], limit: NONE] + │ │ └── estimated rows: 0.00 │ └── Filter(Probe) │ ├── output columns: [t.id (#0)] │ ├── filters: [is_true(t.id (#0) = 1)] @@ -206,15 +202,15 @@ AggregateFinal │ ├── push downs: [filters: [is_true(t1.id (#0) = 1)], limit: NONE] │ └── estimated rows: 0.00 └── EvalScalar - ├── output columns: [t.id (#11), de (#14)] - ├── expressions: [CAST(de (#13) AS Int64 NULL)] + ├── output columns: [t.id (#9), de (#12)] + ├── expressions: [CAST(de (#11) AS Int64 NULL)] ├── estimated rows: 0.00 └── EvalScalar - ├── output columns: [t.id (#11), de (#13)] + ├── output columns: [t.id (#9), de (#11)] ├── expressions: [0] ├── estimated rows: 0.00 └── AggregateFinal - ├── output columns: [t.id (#11)] + ├── output columns: [t.id (#9)] ├── group by: [id] ├── aggregate functions: [] ├── estimated rows: 0.00 @@ -224,17 +220,17 @@ AggregateFinal ├── aggregate functions: [] ├── estimated rows: 0.00 └── Filter - ├── output columns: [t.id (#11)] - ├── filters: [is_true(t.id (#11) = 1)] + ├── output columns: [t.id (#9)] + ├── filters: [is_true(t.id (#9) = 1)] ├── estimated rows: 0.00 └── TableScan ├── table: default.default.t1 - ├── output columns: [id (#11)] + ├── output columns: [id (#9)] ├── read rows: 0 ├── read bytes: 0 ├── partitions total: 0 ├── partitions scanned: 0 - ├── push downs: [filters: [is_true(t1.id (#11) = 1)], limit: NONE] + ├── push downs: [filters: [is_true(t1.id (#9) = 1)], limit: NONE] └── estimated rows: 0.00 statement ok diff --git a/tests/sqllogictests/suites/mode/standalone/explain/subquery.test b/tests/sqllogictests/suites/mode/standalone/explain/subquery.test index d6e2ac98d191..306e22b06d5d 100644 --- a/tests/sqllogictests/suites/mode/standalone/explain/subquery.test +++ b/tests/sqllogictests/suites/mode/standalone/explain/subquery.test @@ -3,54 +3,50 @@ explain select t.number from numbers(1) as t, numbers(1) as t1 where t.number = ---- Filter ├── output columns: [t.number (#0)] -├── filters: [is_true(CAST(t.number (#0) AS UInt64 NULL) = if(CAST(is_not_null(scalar_subquery_5 (#5)) AS Boolean NULL), scalar_subquery_5 (#5), 0))] +├── filters: [is_true(CAST(t.number (#0) AS UInt64 NULL) = if(CAST(is_not_null(scalar_subquery_4 (#4)) AS Boolean NULL), scalar_subquery_4 (#4), 0))] ├── estimated rows: 0.20 └── HashJoin - ├── output columns: [t.number (#0), count(*) (#5)] + ├── output columns: [t.number (#0), COUNT(*) (#4)] ├── join type: LEFT SINGLE ├── build keys: [number (#2)] ├── probe keys: [CAST(number (#0) AS UInt64 NULL)] ├── filters: [] ├── estimated rows: 1.00 - ├── EvalScalar(Build) - │ ├── output columns: [t2.number (#2), count(*) (#5)] - │ ├── expressions: [COUNT(*) (#4)] + ├── AggregateFinal(Build) + │ ├── output columns: [COUNT(*) (#4), t2.number (#2)] + │ ├── group by: [number] + │ ├── aggregate functions: [count()] │ ├── estimated rows: 1.00 - │ └── AggregateFinal - │ ├── output columns: [COUNT(*) (#4), t2.number (#2)] + │ └── AggregatePartial + │ ├── output columns: [COUNT(*) (#4), #_group_by_key] │ ├── group by: [number] │ ├── aggregate functions: [count()] │ ├── estimated rows: 1.00 - │ └── AggregatePartial - │ ├── output columns: [COUNT(*) (#4), #_group_by_key] - │ ├── group by: [number] - │ ├── aggregate functions: [count()] + │ └── HashJoin + │ ├── output columns: [t2.number (#2)] + │ ├── join type: CROSS + │ ├── build keys: [] + │ ├── probe keys: [] + │ ├── filters: [] │ ├── estimated rows: 1.00 - │ └── HashJoin - │ ├── output columns: [t2.number (#2)] - │ ├── join type: CROSS - │ ├── build keys: [] - │ ├── probe keys: [] - │ ├── filters: [] - │ ├── estimated rows: 1.00 - │ ├── TableScan(Build) - │ │ ├── table: default.system.numbers - │ │ ├── output columns: [] - │ │ ├── read rows: 1 - │ │ ├── read bytes: 8 - │ │ ├── partitions total: 1 - │ │ ├── partitions scanned: 1 - │ │ ├── push downs: [filters: [], limit: NONE] - │ │ └── estimated rows: 1.00 - │ └── TableScan(Probe) - │ ├── table: default.system.numbers - │ ├── output columns: [number (#2)] - │ ├── read rows: 1 - │ ├── read bytes: 8 - │ ├── partitions total: 1 - │ ├── partitions scanned: 1 - │ ├── push downs: [filters: [], limit: NONE] - │ └── estimated rows: 1.00 + │ ├── TableScan(Build) + │ │ ├── table: default.system.numbers + │ │ ├── output columns: [] + │ │ ├── read rows: 1 + │ │ ├── read bytes: 8 + │ │ ├── partitions total: 1 + │ │ ├── partitions scanned: 1 + │ │ ├── push downs: [filters: [], limit: NONE] + │ │ └── estimated rows: 1.00 + │ └── TableScan(Probe) + │ ├── table: default.system.numbers + │ ├── output columns: [number (#2)] + │ ├── read rows: 1 + │ ├── read bytes: 8 + │ ├── partitions total: 1 + │ ├── partitions scanned: 1 + │ ├── push downs: [filters: [], limit: NONE] + │ └── estimated rows: 1.00 └── HashJoin(Probe) ├── output columns: [t.number (#0)] ├── join type: CROSS diff --git a/tests/sqllogictests/suites/mode/standalone/explain_native/aggregate.test b/tests/sqllogictests/suites/mode/standalone/explain_native/aggregate.test index 5150186e371d..3c5cd04914ba 100644 --- a/tests/sqllogictests/suites/mode/standalone/explain_native/aggregate.test +++ b/tests/sqllogictests/suites/mode/standalone/explain_native/aggregate.test @@ -97,8 +97,8 @@ query T explain select count(3), type, name, trim(name) as a from system.columns group by name, type, a, concat(name, trim(name)), concat(type, name), length(name); ---- EvalScalar -├── output columns: [columns.name (#0), columns.type (#3), count(3) (#14), a (#15)] -├── expressions: [count(3) (#13), trim_both(columns.name (#0), ' ')] +├── output columns: [count(3) (#13), columns.name (#0), columns.type (#3), a (#14)] +├── expressions: [trim_both(columns.name (#0), ' ')] ├── estimated rows: 0.00 └── AggregateFinal ├── output columns: [count(3) (#13), columns.name (#0), columns.type (#3)] @@ -132,6 +132,29 @@ AggregateFinal ├── group by: [a] ├── aggregate functions: [] ├── estimated rows: 0.00 +└── AggregatePartial + ├── output columns: [#_group_by_key] + ├── group by: [a] + ├── aggregate functions: [] + ├── estimated rows: 0.00 + └── TableScan + ├── table: default.default.explain_agg_t1 + ├── output columns: [a (#0)] + ├── read rows: 0 + ├── read bytes: 0 + ├── partitions total: 0 + ├── partitions scanned: 0 + ├── push downs: [filters: [false], limit: NONE] + └── estimated rows: 0.00 + +query T +explain select a from explain_agg_t1 group by a having a > 3; +---- +AggregateFinal +├── output columns: [explain_agg_t1.a (#0)] +├── group by: [a] +├── aggregate functions: [] +├── estimated rows: 0.00 └── AggregatePartial ├── output columns: [#_group_by_key] ├── group by: [a] @@ -144,48 +167,48 @@ AggregateFinal ├── read bytes: 0 ├── partitions total: 0 ├── partitions scanned: 0 - ├── push downs: [filters: [false], limit: NONE] + ├── push downs: [filters: [explain_agg_t1.a (#0) > 3], limit: NONE] └── estimated rows: 0.00 query T -explain select a from explain_agg_t1 group by a having a > 3; +explain select a, max(b) from explain_agg_t1 group by a having a > 1; ---- AggregateFinal -├── output columns: [explain_agg_t1.a (#0)] +├── output columns: [max(b) (#2), explain_agg_t1.a (#0)] ├── group by: [a] -├── aggregate functions: [] +├── aggregate functions: [max(b)] ├── estimated rows: 0.00 └── AggregatePartial - ├── output columns: [#_group_by_key] + ├── output columns: [max(b) (#2), #_group_by_key] ├── group by: [a] - ├── aggregate functions: [] + ├── aggregate functions: [max(b)] ├── estimated rows: 0.00 └── TableScan ├── table: default.default.explain_agg_t1 - ├── output columns: [a (#0)] + ├── output columns: [a (#0), b (#1)] ├── read rows: 0 ├── read bytes: 0 ├── partitions total: 0 ├── partitions scanned: 0 - ├── push downs: [filters: [explain_agg_t1.a (#0) > 3], limit: NONE] + ├── push downs: [filters: [explain_agg_t1.a (#0) > 1], limit: NONE] └── estimated rows: 0.00 query T -explain select a, max(b) from explain_agg_t1 group by a having a > 1; +explain select a, avg(b) from explain_agg_t1 group by a having a > 1 and max(b) > 10; ---- -EvalScalar -├── output columns: [explain_agg_t1.a (#0), max(b) (#3)] -├── expressions: [max(b) (#2)] +Filter +├── output columns: [avg(b) (#2), explain_agg_t1.a (#0)] +├── filters: [is_true(max(b) (#3) > 10)] ├── estimated rows: 0.00 └── AggregateFinal - ├── output columns: [max(b) (#2), explain_agg_t1.a (#0)] + ├── output columns: [avg(b) (#2), max(b) (#3), explain_agg_t1.a (#0)] ├── group by: [a] - ├── aggregate functions: [max(b)] + ├── aggregate functions: [avg(b), max(b)] ├── estimated rows: 0.00 └── AggregatePartial - ├── output columns: [max(b) (#2), #_group_by_key] + ├── output columns: [avg(b) (#2), max(b) (#3), #_group_by_key] ├── group by: [a] - ├── aggregate functions: [max(b)] + ├── aggregate functions: [avg(b), max(b)] ├── estimated rows: 0.00 └── TableScan ├── table: default.default.explain_agg_t1 @@ -197,37 +220,6 @@ EvalScalar ├── push downs: [filters: [explain_agg_t1.a (#0) > 1], limit: NONE] └── estimated rows: 0.00 -query T -explain select a, avg(b) from explain_agg_t1 group by a having a > 1 and max(b) > 10; ----- -EvalScalar -├── output columns: [explain_agg_t1.a (#0), avg(b) (#3)] -├── expressions: [avg(b) (#2)] -├── estimated rows: 0.00 -└── Filter - ├── output columns: [avg(b) (#2), explain_agg_t1.a (#0)] - ├── filters: [is_true(max(b) (#4) > 10)] - ├── estimated rows: 0.00 - └── AggregateFinal - ├── output columns: [avg(b) (#2), max(b) (#4), explain_agg_t1.a (#0)] - ├── group by: [a] - ├── aggregate functions: [avg(b), max(b)] - ├── estimated rows: 0.00 - └── AggregatePartial - ├── output columns: [avg(b) (#2), max(b) (#4), #_group_by_key] - ├── group by: [a] - ├── aggregate functions: [avg(b), max(b)] - ├── estimated rows: 0.00 - └── TableScan - ├── table: default.default.explain_agg_t1 - ├── output columns: [a (#0), b (#1)] - ├── read rows: 0 - ├── read bytes: 0 - ├── partitions total: 0 - ├── partitions scanned: 0 - ├── push downs: [filters: [explain_agg_t1.a (#0) > 1], limit: NONE] - └── estimated rows: 0.00 - statement ok drop table explain_agg_t1; @@ -246,46 +238,42 @@ create table t2 as select number as a from numbers(100) query T explain select count() from t1, t2 where t1.a > t2.a; ---- -EvalScalar -├── output columns: [count() (#3)] -├── expressions: [count() (#2)] +AggregateFinal +├── output columns: [count() (#2)] +├── group by: [] +├── aggregate functions: [count()] ├── estimated rows: 1.00 -└── AggregateFinal +└── AggregatePartial ├── output columns: [count() (#2)] ├── group by: [] ├── aggregate functions: [count()] ├── estimated rows: 1.00 - └── AggregatePartial - ├── output columns: [count() (#2)] - ├── group by: [] - ├── aggregate functions: [count()] - ├── estimated rows: 1.00 - └── MergeJoin - ├── output columns: [t1.a (#0), t2.a (#1)] - ├── join type: INNER - ├── range join conditions: [t1.a (#0) "gt" t2.a (#1)] - ├── other conditions: [] - ├── estimated rows: 1000.00 - ├── TableScan(Left) - │ ├── table: default.default.t1 - │ ├── output columns: [a (#0)] - │ ├── read rows: 10 - │ ├── read bytes: 54 - │ ├── partitions total: 1 - │ ├── partitions scanned: 1 - │ ├── pruning stats: [segments: , blocks: ] - │ ├── push downs: [filters: [], limit: NONE] - │ └── estimated rows: 10.00 - └── TableScan(Right) - ├── table: default.default.t2 - ├── output columns: [a (#1)] - ├── read rows: 100 - ├── read bytes: 414 - ├── partitions total: 1 - ├── partitions scanned: 1 - ├── pruning stats: [segments: , blocks: ] - ├── push downs: [filters: [], limit: NONE] - └── estimated rows: 100.00 + └── MergeJoin + ├── output columns: [t1.a (#0), t2.a (#1)] + ├── join type: INNER + ├── range join conditions: [t1.a (#0) "gt" t2.a (#1)] + ├── other conditions: [] + ├── estimated rows: 1000.00 + ├── TableScan(Left) + │ ├── table: default.default.t1 + │ ├── output columns: [a (#0)] + │ ├── read rows: 10 + │ ├── read bytes: 54 + │ ├── partitions total: 1 + │ ├── partitions scanned: 1 + │ ├── pruning stats: [segments: , blocks: ] + │ ├── push downs: [filters: [], limit: NONE] + │ └── estimated rows: 10.00 + └── TableScan(Right) + ├── table: default.default.t2 + ├── output columns: [a (#1)] + ├── read rows: 100 + ├── read bytes: 414 + ├── partitions total: 1 + ├── partitions scanned: 1 + ├── pruning stats: [segments: , blocks: ] + ├── push downs: [filters: [], limit: NONE] + └── estimated rows: 100.00 statement ok diff --git a/tests/sqllogictests/suites/mode/standalone/explain_native/explain.test b/tests/sqllogictests/suites/mode/standalone/explain_native/explain.test index b9f77ca809f8..76e85b3119d3 100644 --- a/tests/sqllogictests/suites/mode/standalone/explain_native/explain.test +++ b/tests/sqllogictests/suites/mode/standalone/explain_native/explain.test @@ -860,78 +860,70 @@ HashJoin query T explain select count(distinct a) from t1; ---- -EvalScalar -├── output columns: [count() (#3)] -├── expressions: [count() (#2)] +AggregateFinal +├── output columns: [count() (#2)] +├── group by: [] +├── aggregate functions: [count()] ├── estimated rows: 1.00 -└── AggregateFinal +└── AggregatePartial ├── output columns: [count() (#2)] ├── group by: [] ├── aggregate functions: [count()] ├── estimated rows: 1.00 - └── AggregatePartial - ├── output columns: [count() (#2)] - ├── group by: [] - ├── aggregate functions: [count()] + └── AggregateFinal + ├── output columns: [t1.a (#0)] + ├── group by: [a] + ├── aggregate functions: [] ├── estimated rows: 1.00 - └── AggregateFinal - ├── output columns: [t1.a (#0)] + └── AggregatePartial + ├── output columns: [#_group_by_key] ├── group by: [a] ├── aggregate functions: [] ├── estimated rows: 1.00 - └── AggregatePartial - ├── output columns: [#_group_by_key] - ├── group by: [a] - ├── aggregate functions: [] - ├── estimated rows: 1.00 - └── TableScan - ├── table: default.default.t1 - ├── output columns: [a (#0)] - ├── read rows: 1 - ├── read bytes: 18 - ├── partitions total: 1 - ├── partitions scanned: 1 - ├── pruning stats: [segments: , blocks: ] - ├── push downs: [filters: [], limit: NONE] - └── estimated rows: 1.00 + └── TableScan + ├── table: default.default.t1 + ├── output columns: [a (#0)] + ├── read rows: 1 + ├── read bytes: 18 + ├── partitions total: 1 + ├── partitions scanned: 1 + ├── pruning stats: [segments: , blocks: ] + ├── push downs: [filters: [], limit: NONE] + └── estimated rows: 1.00 query T explain select count_distinct(a) from t1; ---- -EvalScalar -├── output columns: [count() (#3)] -├── expressions: [count() (#2)] +AggregateFinal +├── output columns: [count() (#2)] +├── group by: [] +├── aggregate functions: [count()] ├── estimated rows: 1.00 -└── AggregateFinal +└── AggregatePartial ├── output columns: [count() (#2)] ├── group by: [] ├── aggregate functions: [count()] ├── estimated rows: 1.00 - └── AggregatePartial - ├── output columns: [count() (#2)] - ├── group by: [] - ├── aggregate functions: [count()] + └── AggregateFinal + ├── output columns: [t1.a (#0)] + ├── group by: [a] + ├── aggregate functions: [] ├── estimated rows: 1.00 - └── AggregateFinal - ├── output columns: [t1.a (#0)] + └── AggregatePartial + ├── output columns: [#_group_by_key] ├── group by: [a] ├── aggregate functions: [] ├── estimated rows: 1.00 - └── AggregatePartial - ├── output columns: [#_group_by_key] - ├── group by: [a] - ├── aggregate functions: [] - ├── estimated rows: 1.00 - └── TableScan - ├── table: default.default.t1 - ├── output columns: [a (#0)] - ├── read rows: 1 - ├── read bytes: 18 - ├── partitions total: 1 - ├── partitions scanned: 1 - ├── pruning stats: [segments: , blocks: ] - ├── push downs: [filters: [], limit: NONE] - └── estimated rows: 1.00 + └── TableScan + ├── table: default.default.t1 + ├── output columns: [a (#0)] + ├── read rows: 1 + ├── read bytes: 18 + ├── partitions total: 1 + ├── partitions scanned: 1 + ├── pruning stats: [segments: , blocks: ] + ├── push downs: [filters: [], limit: NONE] + └── estimated rows: 1.00 query explain select * from (values(1, 'a'),(2, 'b')) t(c1,c2) diff --git a/tests/sqllogictests/suites/mode/standalone/explain_native/fold_count.test b/tests/sqllogictests/suites/mode/standalone/explain_native/fold_count.test index 9e470db6b9b5..ed75e0538f98 100644 --- a/tests/sqllogictests/suites/mode/standalone/explain_native/fold_count.test +++ b/tests/sqllogictests/suites/mode/standalone/explain_native/fold_count.test @@ -8,14 +8,10 @@ query T explain select count(*) from t ---- EvalScalar -├── output columns: [count(*) (#2)] -├── expressions: [COUNT(*) (#1)] +├── output columns: [COUNT(*) (#1)] +├── expressions: [1000] ├── estimated rows: 1.00 -└── EvalScalar - ├── output columns: [COUNT(*) (#1)] - ├── expressions: [1000] - ├── estimated rows: 1.00 - └── DummyTableScan +└── DummyTableScan statement ok insert into t values(1) @@ -24,42 +20,34 @@ query T explain select count(*) from t ---- EvalScalar -├── output columns: [count(*) (#2)] -├── expressions: [COUNT(*) (#1)] +├── output columns: [COUNT(*) (#1)] +├── expressions: [1001] ├── estimated rows: 1.00 -└── EvalScalar - ├── output columns: [COUNT(*) (#1)] - ├── expressions: [1001] - ├── estimated rows: 1.00 - └── DummyTableScan +└── DummyTableScan query T explain select count(*) from t where number > 10 ---- -EvalScalar -├── output columns: [count(*) (#2)] -├── expressions: [COUNT(*) (#1)] +AggregateFinal +├── output columns: [COUNT(*) (#1)] +├── group by: [] +├── aggregate functions: [count()] ├── estimated rows: 1.00 -└── AggregateFinal +└── AggregatePartial ├── output columns: [COUNT(*) (#1)] ├── group by: [] ├── aggregate functions: [count()] ├── estimated rows: 1.00 - └── AggregatePartial - ├── output columns: [COUNT(*) (#1)] - ├── group by: [] - ├── aggregate functions: [count()] - ├── estimated rows: 1.00 - └── TableScan - ├── table: default.default.t - ├── output columns: [] - ├── read rows: 1000 - ├── read bytes: 4011 - ├── partitions total: 2 - ├── partitions scanned: 1 - ├── pruning stats: [segments: , blocks: ] - ├── push downs: [filters: [t.number (#0) > 10], limit: NONE] - └── estimated rows: 990.00 + └── TableScan + ├── table: default.default.t + ├── output columns: [] + ├── read rows: 1000 + ├── read bytes: 4011 + ├── partitions total: 2 + ├── partitions scanned: 1 + ├── pruning stats: [segments: , blocks: ] + ├── push downs: [filters: [t.number (#0) > 10], limit: NONE] + └── estimated rows: 990.00 statement ok drop table t diff --git a/tests/sqllogictests/suites/mode/standalone/explain_native/limit.test b/tests/sqllogictests/suites/mode/standalone/explain_native/limit.test index b0d605083b6f..64f86ea1fc03 100644 --- a/tests/sqllogictests/suites/mode/standalone/explain_native/limit.test +++ b/tests/sqllogictests/suites/mode/standalone/explain_native/limit.test @@ -97,54 +97,50 @@ Limit ├── estimated rows: 0.20 └── Filter ├── output columns: [t.number (#0)] - ├── filters: [is_true(CAST(t.number (#0) AS UInt64 NULL) = if(CAST(is_not_null(scalar_subquery_5 (#5)) AS Boolean NULL), scalar_subquery_5 (#5), 0))] + ├── filters: [is_true(CAST(t.number (#0) AS UInt64 NULL) = if(CAST(is_not_null(scalar_subquery_4 (#4)) AS Boolean NULL), scalar_subquery_4 (#4), 0))] ├── estimated rows: 0.20 └── HashJoin - ├── output columns: [t.number (#0), count(*) (#5)] + ├── output columns: [t.number (#0), COUNT(*) (#4)] ├── join type: LEFT SINGLE ├── build keys: [number (#2)] ├── probe keys: [CAST(number (#0) AS UInt64 NULL)] ├── filters: [] ├── estimated rows: 1.00 - ├── EvalScalar(Build) - │ ├── output columns: [t2.number (#2), count(*) (#5)] - │ ├── expressions: [COUNT(*) (#4)] + ├── AggregateFinal(Build) + │ ├── output columns: [COUNT(*) (#4), t2.number (#2)] + │ ├── group by: [number] + │ ├── aggregate functions: [count()] │ ├── estimated rows: 1.00 - │ └── AggregateFinal - │ ├── output columns: [COUNT(*) (#4), t2.number (#2)] + │ └── AggregatePartial + │ ├── output columns: [COUNT(*) (#4), #_group_by_key] │ ├── group by: [number] │ ├── aggregate functions: [count()] │ ├── estimated rows: 1.00 - │ └── AggregatePartial - │ ├── output columns: [COUNT(*) (#4), #_group_by_key] - │ ├── group by: [number] - │ ├── aggregate functions: [count()] + │ └── HashJoin + │ ├── output columns: [t2.number (#2)] + │ ├── join type: CROSS + │ ├── build keys: [] + │ ├── probe keys: [] + │ ├── filters: [] │ ├── estimated rows: 1.00 - │ └── HashJoin - │ ├── output columns: [t2.number (#2)] - │ ├── join type: CROSS - │ ├── build keys: [] - │ ├── probe keys: [] - │ ├── filters: [] - │ ├── estimated rows: 1.00 - │ ├── TableScan(Build) - │ │ ├── table: default.system.numbers - │ │ ├── output columns: [] - │ │ ├── read rows: 1 - │ │ ├── read bytes: 8 - │ │ ├── partitions total: 1 - │ │ ├── partitions scanned: 1 - │ │ ├── push downs: [filters: [], limit: NONE] - │ │ └── estimated rows: 1.00 - │ └── TableScan(Probe) - │ ├── table: default.system.numbers - │ ├── output columns: [number (#2)] - │ ├── read rows: 1 - │ ├── read bytes: 8 - │ ├── partitions total: 1 - │ ├── partitions scanned: 1 - │ ├── push downs: [filters: [], limit: NONE] - │ └── estimated rows: 1.00 + │ ├── TableScan(Build) + │ │ ├── table: default.system.numbers + │ │ ├── output columns: [] + │ │ ├── read rows: 1 + │ │ ├── read bytes: 8 + │ │ ├── partitions total: 1 + │ │ ├── partitions scanned: 1 + │ │ ├── push downs: [filters: [], limit: NONE] + │ │ └── estimated rows: 1.00 + │ └── TableScan(Probe) + │ ├── table: default.system.numbers + │ ├── output columns: [number (#2)] + │ ├── read rows: 1 + │ ├── read bytes: 8 + │ ├── partitions total: 1 + │ ├── partitions scanned: 1 + │ ├── push downs: [filters: [], limit: NONE] + │ └── estimated rows: 1.00 └── HashJoin(Probe) ├── output columns: [t.number (#0)] ├── join type: CROSS @@ -175,64 +171,56 @@ query T explain select * from (select count(t1.number) as c1 from numbers(1) as t1 group by number) as t3 left join (select count(t.number) as c from numbers(2) as t group by number) as t4 on t3.c1=t4.c order by t3.c1 limit 1 ---- Limit -├── output columns: [c (#7), c1 (#3)] +├── output columns: [count(t.number) (#5), count(t1.number) (#2)] ├── limit: 1 ├── offset: 0 ├── estimated rows: 1.00 └── Sort - ├── output columns: [c (#7), c1 (#3)] - ├── sort keys: [c1 ASC NULLS LAST] + ├── output columns: [count(t.number) (#5), count(t1.number) (#2)] + ├── sort keys: [count(t1.number) ASC NULLS LAST] ├── estimated rows: 2.00 └── HashJoin - ├── output columns: [c (#7), c1 (#3)] + ├── output columns: [count(t.number) (#5), count(t1.number) (#2)] ├── join type: RIGHT OUTER - ├── build keys: [CAST(t3.c1 (#3) AS UInt64 NULL)] - ├── probe keys: [t4.c (#7)] + ├── build keys: [CAST(t3.c1 (#2) AS UInt64 NULL)] + ├── probe keys: [t4.c (#5)] ├── filters: [] ├── estimated rows: 2.00 - ├── EvalScalar(Build) - │ ├── output columns: [c1 (#3)] - │ ├── expressions: [count(t1.number) (#2)] + ├── AggregateFinal(Build) + │ ├── output columns: [count(t1.number) (#2), t1.number (#0)] + │ ├── group by: [number] + │ ├── aggregate functions: [count(number)] │ ├── estimated rows: 1.00 - │ └── AggregateFinal - │ ├── output columns: [count(t1.number) (#2), t1.number (#0)] + │ └── AggregatePartial + │ ├── output columns: [count(t1.number) (#2), #_group_by_key] │ ├── group by: [number] │ ├── aggregate functions: [count(number)] │ ├── estimated rows: 1.00 - │ └── AggregatePartial - │ ├── output columns: [count(t1.number) (#2), #_group_by_key] - │ ├── group by: [number] - │ ├── aggregate functions: [count(number)] - │ ├── estimated rows: 1.00 - │ └── TableScan - │ ├── table: default.system.numbers - │ ├── output columns: [number (#0)] - │ ├── read rows: 1 - │ ├── read bytes: 8 - │ ├── partitions total: 1 - │ ├── partitions scanned: 1 - │ ├── push downs: [filters: [], limit: NONE] - │ └── estimated rows: 1.00 - └── EvalScalar(Probe) - ├── output columns: [c (#7)] - ├── expressions: [count(t.number) (#6)] + │ └── TableScan + │ ├── table: default.system.numbers + │ ├── output columns: [number (#0)] + │ ├── read rows: 1 + │ ├── read bytes: 8 + │ ├── partitions total: 1 + │ ├── partitions scanned: 1 + │ ├── push downs: [filters: [], limit: NONE] + │ └── estimated rows: 1.00 + └── AggregateFinal(Probe) + ├── output columns: [count(t.number) (#5), t.number (#3)] + ├── group by: [number] + ├── aggregate functions: [count(number)] ├── estimated rows: 2.00 - └── AggregateFinal - ├── output columns: [count(t.number) (#6), t.number (#4)] + └── AggregatePartial + ├── output columns: [count(t.number) (#5), #_group_by_key] ├── group by: [number] ├── aggregate functions: [count(number)] ├── estimated rows: 2.00 - └── AggregatePartial - ├── output columns: [count(t.number) (#6), #_group_by_key] - ├── group by: [number] - ├── aggregate functions: [count(number)] - ├── estimated rows: 2.00 - └── TableScan - ├── table: default.system.numbers - ├── output columns: [number (#4)] - ├── read rows: 2 - ├── read bytes: 16 - ├── partitions total: 1 - ├── partitions scanned: 1 - ├── push downs: [filters: [], limit: NONE] - └── estimated rows: 2.00 + └── TableScan + ├── table: default.system.numbers + ├── output columns: [number (#3)] + ├── read rows: 2 + ├── read bytes: 16 + ├── partitions total: 1 + ├── partitions scanned: 1 + ├── push downs: [filters: [], limit: NONE] + └── estimated rows: 2.00 diff --git a/tests/sqllogictests/suites/mode/standalone/explain_native/project_set.test b/tests/sqllogictests/suites/mode/standalone/explain_native/project_set.test index 6457c6602c23..432a9ff40f6c 100644 --- a/tests/sqllogictests/suites/mode/standalone/explain_native/project_set.test +++ b/tests/sqllogictests/suites/mode/standalone/explain_native/project_set.test @@ -13,34 +13,30 @@ insert into fold_count values([1,2,3,4], 'x'); query T explain select count() from (select unnest(id), c1 from fold_count) ---- -EvalScalar -├── output columns: [count() (#5)] -├── expressions: [count() (#4)] +AggregateFinal +├── output columns: [count() (#4)] +├── group by: [] +├── aggregate functions: [count()] ├── estimated rows: 1.00 -└── AggregateFinal +└── AggregatePartial ├── output columns: [count() (#4)] ├── group by: [] ├── aggregate functions: [count()] ├── estimated rows: 1.00 - └── AggregatePartial - ├── output columns: [count() (#4)] - ├── group by: [] - ├── aggregate functions: [count()] + └── ProjectSet + ├── output columns: [unnest (#2)] ├── estimated rows: 1.00 - └── ProjectSet - ├── output columns: [unnest (#2)] - ├── estimated rows: 1.00 - ├── set returning functions: unnest(CAST(fold_count.id (#0) AS Array(Int32 NULL) NULL)) - └── TableScan - ├── table: default.default.fold_count - ├── output columns: [id (#0)] - ├── read rows: 1 - ├── read bytes: 43 - ├── partitions total: 1 - ├── partitions scanned: 1 - ├── pruning stats: [segments: , blocks: ] - ├── push downs: [filters: [], limit: NONE] - └── estimated rows: 1.00 + ├── set returning functions: unnest(CAST(fold_count.id (#0) AS Array(Int32 NULL) NULL)) + └── TableScan + ├── table: default.default.fold_count + ├── output columns: [id (#0)] + ├── read rows: 1 + ├── read bytes: 43 + ├── partitions total: 1 + ├── partitions scanned: 1 + ├── pruning stats: [segments: , blocks: ] + ├── push downs: [filters: [], limit: NONE] + └── estimated rows: 1.00 statement ok drop table fold_count; diff --git a/tests/sqllogictests/suites/mode/standalone/explain_native/prune_column.test b/tests/sqllogictests/suites/mode/standalone/explain_native/prune_column.test index 7a34a0674c53..3f0219784215 100644 --- a/tests/sqllogictests/suites/mode/standalone/explain_native/prune_column.test +++ b/tests/sqllogictests/suites/mode/standalone/explain_native/prune_column.test @@ -117,81 +117,77 @@ explain select t1.a from (select number + 1 as a, number + 1 as b from numbers(1 HashJoin ├── output columns: [a (#1)] ├── join type: INNER -├── build keys: [_if_scalar_subquery (#16)] +├── build keys: [_if_scalar_subquery (#15)] ├── probe keys: [CAST(t1.a (#1) AS UInt64 NULL)] ├── filters: [] ├── estimated rows: 1.00 ├── EvalScalar(Build) -│ ├── output columns: [_if_scalar_subquery (#16)] -│ ├── expressions: [if(CAST(_count_scalar_subquery (#14) = 0 AS Boolean NULL), NULL, _any_scalar_subquery (#15))] +│ ├── output columns: [_if_scalar_subquery (#15)] +│ ├── expressions: [if(CAST(_count_scalar_subquery (#13) = 0 AS Boolean NULL), NULL, _any_scalar_subquery (#14))] │ ├── estimated rows: 1.00 │ └── Limit -│ ├── output columns: [_count_scalar_subquery (#14), _any_scalar_subquery (#15)] +│ ├── output columns: [_count_scalar_subquery (#13), _any_scalar_subquery (#14)] │ ├── limit: 1 │ ├── offset: 0 │ ├── estimated rows: 1.00 │ └── AggregateFinal -│ ├── output columns: [_count_scalar_subquery (#14), _any_scalar_subquery (#15)] +│ ├── output columns: [_count_scalar_subquery (#13), _any_scalar_subquery (#14)] │ ├── group by: [] -│ ├── aggregate functions: [count(count(*)), any(count(*))] +│ ├── aggregate functions: [count(COUNT(*)), any(COUNT(*))] │ ├── limit: 1 │ ├── estimated rows: 1.00 │ └── AggregatePartial -│ ├── output columns: [_count_scalar_subquery (#14), _any_scalar_subquery (#15)] +│ ├── output columns: [_count_scalar_subquery (#13), _any_scalar_subquery (#14)] │ ├── group by: [] -│ ├── aggregate functions: [count(count(*)), any(count(*))] +│ ├── aggregate functions: [count(COUNT(*)), any(COUNT(*))] │ ├── estimated rows: 1.00 -│ └── EvalScalar -│ ├── output columns: [count(*) (#13)] -│ ├── expressions: [COUNT(*) (#12)] +│ └── AggregateFinal +│ ├── output columns: [COUNT(*) (#12)] +│ ├── group by: [] +│ ├── aggregate functions: [count()] │ ├── estimated rows: 1.00 -│ └── AggregateFinal +│ └── AggregatePartial │ ├── output columns: [COUNT(*) (#12)] │ ├── group by: [] │ ├── aggregate functions: [count()] │ ├── estimated rows: 1.00 -│ └── AggregatePartial -│ ├── output columns: [COUNT(*) (#12)] -│ ├── group by: [] -│ ├── aggregate functions: [count()] -│ ├── estimated rows: 1.00 -│ └── HashJoin -│ ├── output columns: [] -│ ├── join type: INNER -│ ├── build keys: [t2.b (#5)] -│ ├── probe keys: [t3.b (#10)] -│ ├── filters: [] -│ ├── estimated rows: 0.20 -│ ├── EvalScalar(Build) -│ │ ├── output columns: [b (#5)] -│ │ ├── expressions: [numbers.number (#3) + 1] -│ │ ├── estimated rows: 0.20 -│ │ └── Filter -│ │ ├── output columns: [numbers.number (#3)] -│ │ ├── filters: [numbers.number (#3) + 1 = 1] -│ │ ├── estimated rows: 0.20 -│ │ └── TableScan -│ │ ├── table: default.system.numbers -│ │ ├── output columns: [number (#3)] -│ │ ├── read rows: 1 -│ │ ├── read bytes: 8 -│ │ ├── partitions total: 1 -│ │ ├── partitions scanned: 1 -│ │ ├── push downs: [filters: [numbers.number (#3) + 1 = 1], limit: NONE] -│ │ └── estimated rows: 1.00 -│ └── EvalScalar(Probe) -│ ├── output columns: [b (#10)] -│ ├── expressions: [numbers.number (#8) + 1] -│ ├── estimated rows: 1.00 -│ └── TableScan -│ ├── table: default.system.numbers -│ ├── output columns: [number (#8)] -│ ├── read rows: 1 -│ ├── read bytes: 8 -│ ├── partitions total: 1 -│ ├── partitions scanned: 1 -│ ├── push downs: [filters: [], limit: NONE] -│ └── estimated rows: 1.00 +│ └── HashJoin +│ ├── output columns: [] +│ ├── join type: INNER +│ ├── build keys: [t2.b (#5)] +│ ├── probe keys: [t3.b (#10)] +│ ├── filters: [] +│ ├── estimated rows: 0.20 +│ ├── EvalScalar(Build) +│ │ ├── output columns: [b (#5)] +│ │ ├── expressions: [numbers.number (#3) + 1] +│ │ ├── estimated rows: 0.20 +│ │ └── Filter +│ │ ├── output columns: [numbers.number (#3)] +│ │ ├── filters: [numbers.number (#3) + 1 = 1] +│ │ ├── estimated rows: 0.20 +│ │ └── TableScan +│ │ ├── table: default.system.numbers +│ │ ├── output columns: [number (#3)] +│ │ ├── read rows: 1 +│ │ ├── read bytes: 8 +│ │ ├── partitions total: 1 +│ │ ├── partitions scanned: 1 +│ │ ├── push downs: [filters: [numbers.number (#3) + 1 = 1], limit: NONE] +│ │ └── estimated rows: 1.00 +│ └── EvalScalar(Probe) +│ ├── output columns: [b (#10)] +│ ├── expressions: [numbers.number (#8) + 1] +│ ├── estimated rows: 1.00 +│ └── TableScan +│ ├── table: default.system.numbers +│ ├── output columns: [number (#8)] +│ ├── read rows: 1 +│ ├── read bytes: 8 +│ ├── partitions total: 1 +│ ├── partitions scanned: 1 +│ ├── push downs: [filters: [], limit: NONE] +│ └── estimated rows: 1.00 └── EvalScalar(Probe) ├── output columns: [a (#1)] ├── expressions: [numbers.number (#0) + 1] @@ -284,30 +280,26 @@ insert into t values(1, 2), (2, 3) query T explain select count(*) from t where t.b = 2 ---- -EvalScalar -├── output columns: [count(*) (#3)] -├── expressions: [COUNT(*) (#2)] +AggregateFinal +├── output columns: [COUNT(*) (#2)] +├── group by: [] +├── aggregate functions: [count()] ├── estimated rows: 1.00 -└── AggregateFinal +└── AggregatePartial ├── output columns: [COUNT(*) (#2)] ├── group by: [] ├── aggregate functions: [count()] ├── estimated rows: 1.00 - └── AggregatePartial - ├── output columns: [COUNT(*) (#2)] - ├── group by: [] - ├── aggregate functions: [count()] - ├── estimated rows: 1.00 - └── TableScan - ├── table: default.default.t - ├── output columns: [] - ├── read rows: 2 - ├── read bytes: 24 - ├── partitions total: 1 - ├── partitions scanned: 1 - ├── pruning stats: [segments: , blocks: ] - ├── push downs: [filters: [is_true(t.b (#1) = 2)], limit: NONE] - └── estimated rows: 1.00 + └── TableScan + ├── table: default.default.t + ├── output columns: [] + ├── read rows: 2 + ├── read bytes: 24 + ├── partitions total: 1 + ├── partitions scanned: 1 + ├── pruning stats: [segments: , blocks: ] + ├── push downs: [filters: [is_true(t.b (#1) = 2)], limit: NONE] + └── estimated rows: 1.00 statement ok drop table t diff --git a/tests/sqllogictests/suites/mode/standalone/explain_native/push_down_filter.test b/tests/sqllogictests/suites/mode/standalone/explain_native/push_down_filter.test index 7ffe27872eab..245fda6295bd 100644 --- a/tests/sqllogictests/suites/mode/standalone/explain_native/push_down_filter.test +++ b/tests/sqllogictests/suites/mode/standalone/explain_native/push_down_filter.test @@ -134,56 +134,52 @@ AggregateFinal ├── aggregate functions: [] ├── estimated rows: 0.00 └── UnionAll - ├── output columns: [t.id (#0), de (#10)] + ├── output columns: [t.id (#0), de (#8)] ├── estimated rows: 0.00 ├── EvalScalar - │ ├── output columns: [t.id (#0), de (#10)] - │ ├── expressions: [if(CAST(is_not_null(sum(tb.de) (#9)) AS Boolean NULL), CAST(assume_not_null(sum(tb.de) (#9)) AS Int64 NULL), true, 0, NULL)] + │ ├── output columns: [t.id (#0), de (#8)] + │ ├── expressions: [if(CAST(is_not_null(sum(tb.de) (#7)) AS Boolean NULL), CAST(assume_not_null(sum(tb.de) (#7)) AS Int64 NULL), true, 0, NULL)] │ ├── estimated rows: 0.00 │ └── AggregateFinal - │ ├── output columns: [sum(tb.de) (#9), t.id (#0)] + │ ├── output columns: [sum(tb.de) (#7), t.id (#0)] │ ├── group by: [id] - │ ├── aggregate functions: [sum(de)] + │ ├── aggregate functions: [sum(sum(coalesce(t3.val, 0)))] │ ├── estimated rows: 0.00 │ └── AggregatePartial - │ ├── output columns: [sum(tb.de) (#9), #_group_by_key] + │ ├── output columns: [sum(tb.de) (#7), #_group_by_key] │ ├── group by: [id] - │ ├── aggregate functions: [sum(de)] + │ ├── aggregate functions: [sum(sum(coalesce(t3.val, 0)))] │ ├── estimated rows: 0.00 │ └── HashJoin - │ ├── output columns: [t.id (#0), de (#6)] + │ ├── output columns: [t.id (#0), sum(coalesce(t3.val, 0)) (#5)] │ ├── join type: LEFT OUTER │ ├── build keys: [tb.sid (#1)] │ ├── probe keys: [t.id (#0)] │ ├── filters: [] │ ├── estimated rows: 0.00 - │ ├── EvalScalar(Build) - │ │ ├── output columns: [t2.sid (#1), de (#6)] - │ │ ├── expressions: [sum(coalesce(t3.val, 0)) (#5)] + │ ├── AggregateFinal(Build) + │ │ ├── output columns: [sum(coalesce(t3.val, 0)) (#5), t2.sid (#1)] + │ │ ├── group by: [sid] + │ │ ├── aggregate functions: [sum(sum_arg_0)] │ │ ├── estimated rows: 0.00 - │ │ └── AggregateFinal - │ │ ├── output columns: [sum(coalesce(t3.val, 0)) (#5), t2.sid (#1)] + │ │ └── AggregatePartial + │ │ ├── output columns: [sum(coalesce(t3.val, 0)) (#5), #_group_by_key] │ │ ├── group by: [sid] │ │ ├── aggregate functions: [sum(sum_arg_0)] │ │ ├── estimated rows: 0.00 - │ │ └── AggregatePartial - │ │ ├── output columns: [sum(coalesce(t3.val, 0)) (#5), #_group_by_key] - │ │ ├── group by: [sid] - │ │ ├── aggregate functions: [sum(sum_arg_0)] + │ │ └── EvalScalar + │ │ ├── output columns: [t2.sid (#1), sum_arg_0 (#4)] + │ │ ├── expressions: [if(CAST(is_not_null(t3.val (#2)) AS Boolean NULL), CAST(assume_not_null(t3.val (#2)) AS Int32 NULL), true, 0, NULL)] │ │ ├── estimated rows: 0.00 - │ │ └── EvalScalar - │ │ ├── output columns: [t2.sid (#1), sum_arg_0 (#4)] - │ │ ├── expressions: [if(CAST(is_not_null(t3.val (#2)) AS Boolean NULL), CAST(assume_not_null(t3.val (#2)) AS Int32 NULL), true, 0, NULL)] - │ │ ├── estimated rows: 0.00 - │ │ └── TableScan - │ │ ├── table: default.default.t2 - │ │ ├── output columns: [sid (#1), val (#2)] - │ │ ├── read rows: 0 - │ │ ├── read bytes: 0 - │ │ ├── partitions total: 0 - │ │ ├── partitions scanned: 0 - │ │ ├── push downs: [filters: [], limit: NONE] - │ │ └── estimated rows: 0.00 + │ │ └── TableScan + │ │ ├── table: default.default.t2 + │ │ ├── output columns: [sid (#1), val (#2)] + │ │ ├── read rows: 0 + │ │ ├── read bytes: 0 + │ │ ├── partitions total: 0 + │ │ ├── partitions scanned: 0 + │ │ ├── push downs: [filters: [], limit: NONE] + │ │ └── estimated rows: 0.00 │ └── TableScan(Probe) │ ├── table: default.default.t1 │ ├── output columns: [id (#0)] @@ -194,15 +190,15 @@ AggregateFinal │ ├── push downs: [filters: [is_true(t1.id (#0) = 1)], limit: NONE] │ └── estimated rows: 0.00 └── EvalScalar - ├── output columns: [t.id (#11), de (#14)] - ├── expressions: [CAST(de (#13) AS Int64 NULL)] + ├── output columns: [t.id (#9), de (#12)] + ├── expressions: [CAST(de (#11) AS Int64 NULL)] ├── estimated rows: 0.00 └── EvalScalar - ├── output columns: [t.id (#11), de (#13)] + ├── output columns: [t.id (#9), de (#11)] ├── expressions: [0] ├── estimated rows: 0.00 └── AggregateFinal - ├── output columns: [t.id (#11)] + ├── output columns: [t.id (#9)] ├── group by: [id] ├── aggregate functions: [] ├── estimated rows: 0.00 @@ -213,12 +209,12 @@ AggregateFinal ├── estimated rows: 0.00 └── TableScan ├── table: default.default.t1 - ├── output columns: [id (#11)] + ├── output columns: [id (#9)] ├── read rows: 0 ├── read bytes: 0 ├── partitions total: 0 ├── partitions scanned: 0 - ├── push downs: [filters: [is_true(t1.id (#11) = 1)], limit: NONE] + ├── push downs: [filters: [is_true(t1.id (#9) = 1)], limit: NONE] └── estimated rows: 0.00 statement ok diff --git a/tests/sqllogictests/suites/mode/standalone/explain_native/subquery.test b/tests/sqllogictests/suites/mode/standalone/explain_native/subquery.test index d6e2ac98d191..306e22b06d5d 100644 --- a/tests/sqllogictests/suites/mode/standalone/explain_native/subquery.test +++ b/tests/sqllogictests/suites/mode/standalone/explain_native/subquery.test @@ -3,54 +3,50 @@ explain select t.number from numbers(1) as t, numbers(1) as t1 where t.number = ---- Filter ├── output columns: [t.number (#0)] -├── filters: [is_true(CAST(t.number (#0) AS UInt64 NULL) = if(CAST(is_not_null(scalar_subquery_5 (#5)) AS Boolean NULL), scalar_subquery_5 (#5), 0))] +├── filters: [is_true(CAST(t.number (#0) AS UInt64 NULL) = if(CAST(is_not_null(scalar_subquery_4 (#4)) AS Boolean NULL), scalar_subquery_4 (#4), 0))] ├── estimated rows: 0.20 └── HashJoin - ├── output columns: [t.number (#0), count(*) (#5)] + ├── output columns: [t.number (#0), COUNT(*) (#4)] ├── join type: LEFT SINGLE ├── build keys: [number (#2)] ├── probe keys: [CAST(number (#0) AS UInt64 NULL)] ├── filters: [] ├── estimated rows: 1.00 - ├── EvalScalar(Build) - │ ├── output columns: [t2.number (#2), count(*) (#5)] - │ ├── expressions: [COUNT(*) (#4)] + ├── AggregateFinal(Build) + │ ├── output columns: [COUNT(*) (#4), t2.number (#2)] + │ ├── group by: [number] + │ ├── aggregate functions: [count()] │ ├── estimated rows: 1.00 - │ └── AggregateFinal - │ ├── output columns: [COUNT(*) (#4), t2.number (#2)] + │ └── AggregatePartial + │ ├── output columns: [COUNT(*) (#4), #_group_by_key] │ ├── group by: [number] │ ├── aggregate functions: [count()] │ ├── estimated rows: 1.00 - │ └── AggregatePartial - │ ├── output columns: [COUNT(*) (#4), #_group_by_key] - │ ├── group by: [number] - │ ├── aggregate functions: [count()] + │ └── HashJoin + │ ├── output columns: [t2.number (#2)] + │ ├── join type: CROSS + │ ├── build keys: [] + │ ├── probe keys: [] + │ ├── filters: [] │ ├── estimated rows: 1.00 - │ └── HashJoin - │ ├── output columns: [t2.number (#2)] - │ ├── join type: CROSS - │ ├── build keys: [] - │ ├── probe keys: [] - │ ├── filters: [] - │ ├── estimated rows: 1.00 - │ ├── TableScan(Build) - │ │ ├── table: default.system.numbers - │ │ ├── output columns: [] - │ │ ├── read rows: 1 - │ │ ├── read bytes: 8 - │ │ ├── partitions total: 1 - │ │ ├── partitions scanned: 1 - │ │ ├── push downs: [filters: [], limit: NONE] - │ │ └── estimated rows: 1.00 - │ └── TableScan(Probe) - │ ├── table: default.system.numbers - │ ├── output columns: [number (#2)] - │ ├── read rows: 1 - │ ├── read bytes: 8 - │ ├── partitions total: 1 - │ ├── partitions scanned: 1 - │ ├── push downs: [filters: [], limit: NONE] - │ └── estimated rows: 1.00 + │ ├── TableScan(Build) + │ │ ├── table: default.system.numbers + │ │ ├── output columns: [] + │ │ ├── read rows: 1 + │ │ ├── read bytes: 8 + │ │ ├── partitions total: 1 + │ │ ├── partitions scanned: 1 + │ │ ├── push downs: [filters: [], limit: NONE] + │ │ └── estimated rows: 1.00 + │ └── TableScan(Probe) + │ ├── table: default.system.numbers + │ ├── output columns: [number (#2)] + │ ├── read rows: 1 + │ ├── read bytes: 8 + │ ├── partitions total: 1 + │ ├── partitions scanned: 1 + │ ├── push downs: [filters: [], limit: NONE] + │ └── estimated rows: 1.00 └── HashJoin(Probe) ├── output columns: [t.number (#0)] ├── join type: CROSS From bd36a42b8cd0261186a834dad529bb0fbf783b30 Mon Sep 17 00:00:00 2001 From: TONG CHEN <52696678+ct20000901@users.noreply.github.com> Date: Tue, 10 Oct 2023 14:04:55 +0800 Subject: [PATCH 02/25] feat:draw_merge_into_pipeline (#13036) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit feat:draw_merge_into_pipeline feat:draw_merge_into_pipeline add some missing pipline add some missing pipline remove some comment remove some comment remove some comment remove some comment format Co-authored-by: 陈侗 Co-authored-by: JackTan25 <60096118+JackTan25@users.noreply.github.com> --- .../service/src/pipelines/pipeline_builder.rs | 18 ++++++++ .../storages/fuse/src/operations/merge.rs | 43 +++++++++++++++++++ 2 files changed, 61 insertions(+) diff --git a/src/query/service/src/pipelines/pipeline_builder.rs b/src/query/service/src/pipelines/pipeline_builder.rs index 9c6cb57468c8..afd3728fba48 100644 --- a/src/query/service/src/pipelines/pipeline_builder.rs +++ b/src/query/service/src/pipelines/pipeline_builder.rs @@ -471,6 +471,24 @@ impl PipelineBuilder { } output_len }; + // Handle matched and unmatched data separately. + + // +-----------------------------+-+ + // +-----------------------+ Matched | +-+ + // | +---+--------------->| MatchedSplitProcessor | + // | | | | +-+ + // +----------------------+ | +---+ +-----------------------------+-+ + // | MergeIntoSource +---------->|MergeIntoSplitProcessor| + // +----------------------+ | +---+ +-----------------------------+ + // | | | NotMatched | +-+ + // | +---+--------------->| MergeIntoNotMatchedProcessor| | + // +-----------------------+ | +-+ + // +-----------------------------+ + // Note: here the output_port of MatchedSplitProcessor are arranged in the following order + // (0) -> output_port_row_id + // (1) -> output_port_updated + + // Outputs from MatchedSplitProcessor's output_port_updated and MergeIntoNotMatchedProcessor's output_port are merged and processed uniformly by the subsequent ResizeProcessor // receive matched data and not matched data parallelly. let mut pipe_items = Vec::with_capacity(self.main_pipeline.output_len()); diff --git a/src/query/storages/fuse/src/operations/merge.rs b/src/query/storages/fuse/src/operations/merge.rs index e0cea8b952e2..0724a322df60 100644 --- a/src/query/storages/fuse/src/operations/merge.rs +++ b/src/query/storages/fuse/src/operations/merge.rs @@ -28,6 +28,49 @@ use crate::FuseTable; impl FuseTable { // todo: (JackTan25) add pipeline picture + // The big picture of the merge into pipeline: + // + // +-------------------+ + // +-----------------------------+ output_port_row_id | | + // +-----------------------+ Matched | +------------------------>-ResizeProcessor(1)+---------------+ + // | +---+--------------->| MatchedSplitProcessor | | | | + // | | | | +----------+ +-------------------+ | + // +----------------------+ | +---+ +-----------------------------+ | | + // | MergeIntoSource +---------->|MergeIntoSplitProcessor| output_port_updated | + // +----------------------+ | +---+ +-----------------------------+ | +-------------------+ | + // | | | NotMatched | | | | | | + // | +---+--------------->| MergeIntoNotMatchedProcessor+----------+------------->-ResizeProcessor(1)+-----------+ | + // +-----------------------+ | | | | | | + // +-----------------------------+ +-------------------+ | | + // | | + // | | + // | | + // | | + // | | + // +-------------------------------------------------+ | | + // | | | | + // | | | | + // +--------------------------+ +-------------------------+ | ++---------------------------+ | +--------------------------------------+ | | + // +---------+ TransformSerializeSegment<--------+ TransformSerializeBlock <-----+---------+|TransformAddComputedColumns|<---------+-----+TransformResortAddOnWithoutSourceSchema<-+ | + // | +--------------------------+ +-------------------------+ | ++---------------------------+ | +--------------------------------------+ | + // | | | | + // | | | | + // | | | | + // | | | | + // | +---------------+ +------------------------------+ | ++---------------+ | +---------------+ | + // +----------+ TransformDummy|<----------------+ AsyncAccumulatingTransformer <-+---------------+|TransformDummy |<---------------+---------------+TransformDummy <------------------+ + // | +---------------+ +------------------------------+ | ++---------------+ | +---------------+ + // | | | + // | | If it includes 'computed', this section | + // | | of code will be executed, otherwise it won't | + // | | | + // | -+-------------------------------------------------+ + // | + // | + // | + // | +------------------+ +-----------------------+ +-----------+ + // +------->|ResizeProcessor(1)+----------->|TableMutationAggregator+------->|CommitSink | + // +------------------+ +-----------------------+ +-----------+ pub fn rowid_aggregate_mutator( &self, ctx: Arc, From 907284d29a8184944659e558c46b1c688e0971df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E7=82=8E=E6=B3=BC?= Date: Tue, 10 Oct 2023 15:02:13 +0800 Subject: [PATCH 03/25] chore: move meta-service impl out of trait implementation (#13166) --- src/meta/service/src/api/grpc/grpc_service.rs | 142 +++++++++--------- 1 file changed, 74 insertions(+), 68 deletions(-) diff --git a/src/meta/service/src/api/grpc/grpc_service.rs b/src/meta/service/src/api/grpc/grpc_service.rs index 4849508c4764..330104a71384 100644 --- a/src/meta/service/src/api/grpc/grpc_service.rs +++ b/src/meta/service/src/api/grpc/grpc_service.rs @@ -90,6 +90,66 @@ impl MetaServiceImpl { })?; Ok(claim) } + + #[minitrace::trace] + async fn handle_kv_api(&self, request: Request) -> Result { + let req: MetaGrpcReq = request.try_into()?; + info!("{}: Received MetaGrpcReq: {:?}", func_name!(), req); + + let t0 = Instant::now(); + + let m = &self.meta_node; + let reply = match &req { + MetaGrpcReq::UpsertKV(a) => { + let res = m.upsert_kv(a.clone()).await; + RaftReply::from(res) + } + MetaGrpcReq::GetKV(a) => { + let res = m.get_kv(&a.key).await; + RaftReply::from(res) + } + MetaGrpcReq::MGetKV(a) => { + let res = m.mget_kv(&a.keys).await; + RaftReply::from(res) + } + MetaGrpcReq::ListKV(a) => { + let res = m.prefix_list_kv(&a.prefix).await; + RaftReply::from(res) + } + }; + let elapsed = t0.elapsed(); + info!("Handled(elapsed: {:?}) MetaGrpcReq: {:?}", elapsed, req); + + network_metrics::incr_request_result(reply.error.is_empty()); + + Ok(reply) + } + + #[minitrace::trace] + async fn handle_txn(&self, request: Request) -> Result { + let request = request.into_inner(); + + info!("{}: Receive txn_request: {}", func_name!(), request); + + let ret = self.meta_node.transaction(request).await; + + let body = match ret { + Ok(resp) => TxnReply { + success: resp.success, + error: "".to_string(), + responses: resp.responses, + }, + Err(err) => TxnReply { + success: false, + error: serde_json::to_string(&err).expect("fail to serialize"), + responses: vec![], + }, + }; + + network_metrics::incr_request_result(body.error.is_empty()); + + Ok(body) + } } impl NamedService for MetaServiceImpl { @@ -159,48 +219,15 @@ impl MetaService for MetaServiceImpl { } } - async fn kv_api(&self, r: Request) -> Result, Status> { - let root = common_tracing::start_trace_for_remote_request(func_name!(), &r); - - let reply = async { - self.check_token(r.metadata())?; - network_metrics::incr_recv_bytes(r.get_ref().encoded_len() as u64); - let _guard = RequestInFlight::guard(); - - let req: MetaGrpcReq = r.try_into()?; - info!("{}: Received MetaGrpcReq: {:?}", func_name!(), req); - - let t0 = Instant::now(); - - let m = &self.meta_node; - let reply = match &req { - MetaGrpcReq::UpsertKV(a) => { - let res = m.upsert_kv(a.clone()).await; - RaftReply::from(res) - } - MetaGrpcReq::GetKV(a) => { - let res = m.get_kv(&a.key).await; - RaftReply::from(res) - } - MetaGrpcReq::MGetKV(a) => { - let res = m.mget_kv(&a.keys).await; - RaftReply::from(res) - } - MetaGrpcReq::ListKV(a) => { - let res = m.prefix_list_kv(&a.prefix).await; - RaftReply::from(res) - } - }; + async fn kv_api(&self, request: Request) -> Result, Status> { + self.check_token(request.metadata())?; - let elapsed = t0.elapsed(); - info!("Handled(elapsed: {:?}) MetaGrpcReq: {:?}", elapsed, req); + network_metrics::incr_recv_bytes(request.get_ref().encoded_len() as u64); + let _guard = RequestInFlight::guard(); - Ok::<_, Status>(reply) - } - .in_span(root) - .await?; + let root = common_tracing::start_trace_for_remote_request(func_name!(), &request); + let reply = self.handle_kv_api(request).in_span(root).await?; - network_metrics::incr_request_result(reply.error.is_empty()); network_metrics::incr_sent_bytes(reply.encoded_len() as u64); Ok(Response::new(reply)) @@ -210,38 +237,17 @@ impl MetaService for MetaServiceImpl { &self, request: Request, ) -> Result, Status> { + self.check_token(request.metadata())?; + + network_metrics::incr_recv_bytes(request.get_ref().encoded_len() as u64); + let _guard = RequestInFlight::guard(); + let root = common_tracing::start_trace_for_remote_request(func_name!(), &request); - async { - self.check_token(request.metadata())?; - network_metrics::incr_recv_bytes(request.get_ref().encoded_len() as u64); - let _guard = RequestInFlight::guard(); - - let request = request.into_inner(); - - info!("{}: Receive txn_request: {}", func_name!(), request); - - let ret = self.meta_node.transaction(request).await; - network_metrics::incr_request_result(ret.is_ok()); - - let body = match ret { - Ok(resp) => TxnReply { - success: resp.success, - error: "".to_string(), - responses: resp.responses, - }, - Err(err) => TxnReply { - success: false, - error: serde_json::to_string(&err).expect("fail to serialize"), - responses: vec![], - }, - }; + let reply = self.handle_txn(request).in_span(root).await?; - network_metrics::incr_sent_bytes(body.encoded_len() as u64); + network_metrics::incr_sent_bytes(reply.encoded_len() as u64); - Ok(Response::new(body)) - } - .in_span(root) - .await + Ok(Response::new(reply)) } type ExportStream = Pin> + Send + 'static>>; From 2c70a33f60e77070bcc28db6a96d1467f0df57e0 Mon Sep 17 00:00:00 2001 From: Quan <787025321@qq.com> Date: Tue, 10 Oct 2023 15:48:59 +0800 Subject: [PATCH 04/25] fix: Image reduction bug (#13169) * chore: styles * chore: Replaced Twitter Logo * fix: styles * fix: tag styles * fix: Image reduction bug --- website/docusaurus.config.js | 4 +++- website/package.json | 2 +- website/yarn.lock | 8 ++++---- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/website/docusaurus.config.js b/website/docusaurus.config.js index 8fe9119e2dec..ddf61ab2049b 100644 --- a/website/docusaurus.config.js +++ b/website/docusaurus.config.js @@ -132,7 +132,9 @@ const config = { themeConfig: /** @type {import('@docusaurus/preset-classic').ThemeConfig} */ ({ - zoomSelector: 'article :not(a) > img', + imageZoom: { + selector: 'article :not(a) > img' + }, announcementBar: { id: 'announcementBar-2', // Increment on change content: `⭐️ If you like Databend, give it a star on GitHub and follow us on Twitter ${TwitterSvg}`, diff --git a/website/package.json b/website/package.json index 47fb22be1ef9..a769e691aa1c 100644 --- a/website/package.json +++ b/website/package.json @@ -39,7 +39,7 @@ "docusaurus-plugin-devserver": "^1.0.6", "docusaurus-plugin-sass": "^0.2.1", "node": "^17.7.2", - "plugin-image-zoom": "^1.1.0", + "plugin-image-zoom": "^1.2.0", "prism-react-renderer": "^1.2.1", "rc-table": "^7.27.1", "rc-texty": "^0.2.0", diff --git a/website/yarn.lock b/website/yarn.lock index 03eb981a132c..a30b4d7a1a3a 100644 --- a/website/yarn.lock +++ b/website/yarn.lock @@ -5945,10 +5945,10 @@ pkg-up@^3.1.0: dependencies: find-up "^3.0.0" -plugin-image-zoom@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/plugin-image-zoom/-/plugin-image-zoom-1.1.0.tgz#0d0e5950f9af327de894b3608011b4e113968712" - integrity sha512-1uUByLjDsqRb3HI5ffujoqPstHdGzKzEJHBG83qVt00t/gRNcVt6oaTS0wEcw5IoQGzPoRnqadv6ivxMfIjghA== +plugin-image-zoom@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/plugin-image-zoom/-/plugin-image-zoom-1.2.0.tgz#f2ab8a6a082e0c728b870dd8213078d449a67b6d" + integrity sha512-uVCjp4bXuli39gmBs+JQvXMtpfLL+5yWfRIKZyM41d3D9oxGBEHmRzDu9EgusIwmBrKJvF9QuOZENw/9s6G+Jw== dependencies: medium-zoom "^1.0.4" From 609e02bf70680e319b0ebedf3c19f63ac3f85d8d Mon Sep 17 00:00:00 2001 From: flaneur Date: Tue, 10 Oct 2023 15:56:50 +0800 Subject: [PATCH 05/25] ci: add sqlogictest about SHOW USERS (#13162) * fix rewrite about SHOW USERS * add SHOW USERS to sqllogictest * add show users * try fix sqlogictest --- .../suites/base/05_ddl/05_0004_ddl_create_user | 3 +++ .../suites/base/06_show/06_0016_show_users | 12 ++++++++++++ 2 files changed, 15 insertions(+) create mode 100644 tests/sqllogictests/suites/base/06_show/06_0016_show_users diff --git a/tests/sqllogictests/suites/base/05_ddl/05_0004_ddl_create_user b/tests/sqllogictests/suites/base/05_ddl/05_0004_ddl_create_user index b72a91800e89..66d2f9f28d5b 100644 --- a/tests/sqllogictests/suites/base/05_ddl/05_0004_ddl_create_user +++ b/tests/sqllogictests/suites/base/05_ddl/05_0004_ddl_create_user @@ -46,6 +46,9 @@ CREATE USER 'test-f'@'%' IDENTIFIED BY 'password' statement error 2202 CREATE USER 'test-f' IDENTIFIED BY 'password' +statement ok +SHOW USERS + statement ok DROP USER IF EXISTS 'test-a' diff --git a/tests/sqllogictests/suites/base/06_show/06_0016_show_users b/tests/sqllogictests/suites/base/06_show/06_0016_show_users new file mode 100644 index 000000000000..bdd2049b7b7c --- /dev/null +++ b/tests/sqllogictests/suites/base/06_show/06_0016_show_users @@ -0,0 +1,12 @@ +statement ok +CREATE USER IF NOT EXISTS 'showuser1' IDENTIFIED BY 'password' + +query TTTI +SHOW USERS +---- +default % no_password YES +root % no_password YES +showuser1 % double_sha1_password NO + +statement ok +DROP USER IF EXISTS 'showuser1' From 99cd128ed1112c998a3459de78cfbac889046f55 Mon Sep 17 00:00:00 2001 From: flaneur Date: Tue, 10 Oct 2023 19:12:07 +0800 Subject: [PATCH 06/25] enable users table again (#13171) --- src/query/service/src/databases/system/system_database.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/query/service/src/databases/system/system_database.rs b/src/query/service/src/databases/system/system_database.rs index 1fdd0f2c8a80..d36ec05ada04 100644 --- a/src/query/service/src/databases/system/system_database.rs +++ b/src/query/service/src/databases/system/system_database.rs @@ -70,8 +70,6 @@ impl SystemDatabase { let mut map = HashMap::new(); map.insert("configs".to_string(), true); map.insert("clusters".to_string(), true); - // Add 2023-08-01 by BohuTANG, due to it may leak the auth_string in the output. - map.insert("users".to_string(), true); map } From f113ca3ebe03d21419e1adacfa043253d3cbee7f Mon Sep 17 00:00:00 2001 From: "xudong.w" Date: Tue, 10 Oct 2023 20:53:59 +0800 Subject: [PATCH 07/25] fix: not only count aggregation function in subquery (#13179) --- .../optimizer/heuristic/decorrelate.rs | 7 +++++- .../src/planner/optimizer/hyper_dp/dphyp.rs | 1 + tests/sqllogictests/suites/tpch/queries.test | 22 +++++++++++++++++++ 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/query/sql/src/planner/optimizer/heuristic/decorrelate.rs b/src/query/sql/src/planner/optimizer/heuristic/decorrelate.rs index b33477d8471f..57700be978cb 100644 --- a/src/query/sql/src/planner/optimizer/heuristic/decorrelate.rs +++ b/src/query/sql/src/planner/optimizer/heuristic/decorrelate.rs @@ -682,7 +682,12 @@ impl SubqueryRewriter { if let ScalarExpr::AggregateFunction(AggregateFunction { func_name, .. }) = &scalar { - if func_name.eq_ignore_ascii_case("count") || func_name.eq("count_distinct") + // For scalar subquery, we'll convert it to single join. + // Single join is similar to left outer join, if there isn't matched row in the right side, we'll add NULL value for the right side. + // But for count aggregation function, NULL values should be 0. + if aggregate.aggregate_functions.len() == 1 + && (func_name.eq_ignore_ascii_case("count") + || func_name.eq("count_distinct")) { flatten_info.from_count_func = true; } diff --git a/src/query/sql/src/planner/optimizer/hyper_dp/dphyp.rs b/src/query/sql/src/planner/optimizer/hyper_dp/dphyp.rs index c2ea6f6c8751..49f905953cd3 100644 --- a/src/query/sql/src/planner/optimizer/hyper_dp/dphyp.rs +++ b/src/query/sql/src/planner/optimizer/hyper_dp/dphyp.rs @@ -50,6 +50,7 @@ pub struct DPhpy { dp_table: HashMap, JoinNode>, query_graph: QueryGraph, relation_set_tree: RelationSetTree, + // non-equi conditions filters: HashSet, } diff --git a/tests/sqllogictests/suites/tpch/queries.test b/tests/sqllogictests/suites/tpch/queries.test index 2b1b293b407c..0cfbb0a001c3 100644 --- a/tests/sqllogictests/suites/tpch/queries.test +++ b/tests/sqllogictests/suites/tpch/queries.test @@ -823,6 +823,28 @@ where ---- 23512.75195312 +#Q17 variant +query I +SELECT + TRUNCATE(sum(l_extendedprice) / 7.0, 8) AS avg_yearly +FROM + lineitem, + part +WHERE + p_partkey = l_partkey + AND p_brand = 'Brand#23' + AND p_container = 'MED BOX' + AND l_quantity < ( + SELECT + 0.2 * (sum(l_quantity) / count(l_quantity)) + FROM + lineitem + WHERE + l_partkey = p_partkey + ) +---- +23512.75195312 + #Q18 query I select From a7b0343cdd05a780aba45d8081483f4cdd161bb8 Mon Sep 17 00:00:00 2001 From: Yang Xiufeng Date: Tue, 10 Oct 2023 07:58:25 -0500 Subject: [PATCH 08/25] refactor: split code of CopyIntoTable and CopyIntoLocation. (#13170) --- src/query/ast/src/ast/format/ast_format.rs | 139 +++-- src/query/ast/src/ast/format/syntax/dml.rs | 117 ++-- src/query/ast/src/ast/format/syntax/mod.rs | 3 +- src/query/ast/src/ast/statements/copy.rs | 215 +++++--- src/query/ast/src/ast/statements/statement.rs | 21 +- src/query/ast/src/parser/copy.rs | 211 +++++++ src/query/ast/src/parser/mod.rs | 1 + src/query/ast/src/parser/statement.rs | 144 +---- src/query/ast/src/visitors/visitor.rs | 5 +- src/query/ast/src/visitors/visitor_mut.rs | 5 +- src/query/ast/src/visitors/walk.rs | 3 +- src/query/ast/src/visitors/walk_mut.rs | 3 +- src/query/ast/tests/it/parser.rs | 12 +- .../ast/tests/it/testdata/statement-error.txt | 107 ++-- src/query/ast/tests/it/testdata/statement.txt | 518 +++++++----------- src/query/catalog/src/query_kind.rs | 2 +- .../interpreters/access/privilege_access.rs | 44 +- .../interpreter_copy_into_location.rs | 147 +++++ ...copy.rs => interpreter_copy_into_table.rs} | 152 ++--- .../src/interpreters/interpreter_factory.rs | 10 +- .../src/interpreters/interpreter_replace.rs | 28 +- src/query/service/src/interpreters/mod.rs | 3 +- .../service/src/pipelines/builders/copy.rs | 4 +- .../service/src/pipelines/pipeline_builder.rs | 4 +- .../src/schedulers/fragments/fragmenter.rs | 23 +- .../src/schedulers/fragments/plan_fragment.rs | 31 +- src/query/service/src/sessions/query_ctx.rs | 2 +- src/query/sql/src/executor/format.rs | 4 +- src/query/sql/src/executor/physical_plan.rs | 6 +- .../sql/src/executor/physical_plan_display.rs | 4 +- .../sql/src/executor/physical_plan_visitor.rs | 23 +- src/query/sql/src/planner/binder/binder.rs | 13 +- .../src/planner/binder/copy_into_location.rs | 100 ++++ .../binder/{copy.rs => copy_into_table.rs} | 207 ++----- src/query/sql/src/planner/binder/ddl/stage.rs | 2 +- src/query/sql/src/planner/binder/mod.rs | 6 +- src/query/sql/src/planner/binder/table.rs | 2 +- .../sql/src/planner/format/display_plan.rs | 3 +- .../sql/src/planner/optimizer/optimizer.rs | 45 +- src/query/sql/src/planner/planner.rs | 5 +- .../src/planner/plans/copy_into_location.rs | 38 ++ .../plans/{copy.rs => copy_into_table.rs} | 60 +- src/query/sql/src/planner/plans/mod.rs | 7 +- src/query/sql/src/planner/plans/plan.rs | 21 +- .../src/parquet2/parquet_table/partition.rs | 2 +- .../processors/deserialize_transform.rs | 2 +- .../src/parquet_rs/parquet_table/partition.rs | 2 +- .../src/parquet_rs/parquet_table/table.rs | 2 +- .../storages/parquet/src/parquet_rs/source.rs | 2 +- .../mode/standalone/explain/explain.test | 16 +- .../standalone/explain_native/explain.test | 16 +- .../05_formats/05_00_00_named_format.sh | 2 +- 52 files changed, 1343 insertions(+), 1201 deletions(-) create mode 100644 src/query/ast/src/parser/copy.rs create mode 100644 src/query/service/src/interpreters/interpreter_copy_into_location.rs rename src/query/service/src/interpreters/{interpreter_copy.rs => interpreter_copy_into_table.rs} (71%) create mode 100644 src/query/sql/src/planner/binder/copy_into_location.rs rename src/query/sql/src/planner/binder/{copy.rs => copy_into_table.rs} (75%) create mode 100644 src/query/sql/src/planner/plans/copy_into_location.rs rename src/query/sql/src/planner/plans/{copy.rs => copy_into_table.rs} (84%) diff --git a/src/query/ast/src/ast/format/ast_format.rs b/src/query/ast/src/ast/format/ast_format.rs index da521cc648ad..3adc9fb8586c 100644 --- a/src/query/ast/src/ast/format/ast_format.rs +++ b/src/query/ast/src/ast/format/ast_format.rs @@ -701,12 +701,51 @@ impl<'ast> Visitor<'ast> for AstFormatVisitor { self.children.push(node); } - fn visit_copy(&mut self, copy: &'ast CopyStmt) { + fn visit_copy_into_table(&mut self, copy: &'ast CopyIntoTableStmt) { let mut children = Vec::new(); - self.visit_copy_unit(©.src); - children.push(self.children.pop().unwrap()); - self.visit_copy_unit(©.dst); - children.push(self.children.pop().unwrap()); + + // to + self.visit_table_ref(©.dst.catalog, ©.dst.database, ©.dst.table); + let to_node = self.children.pop().unwrap(); + let to_node = FormatTreeNode::with_children( + AstFormatContext::with_children("TO".to_string(), 1), + vec![to_node], + ); + children.push(to_node); + + // from + let from_node = match ©.src { + CopyIntoTableSource::Location(location) => { + FormatTreeNode::new(AstFormatContext::new(format!("Location {}", location))) + } + CopyIntoTableSource::Query(query) => { + self.visit_query(query); + FormatTreeNode::with_children( + AstFormatContext::with_children("Query".to_string(), 1), + vec![self.children.pop().unwrap()], + ) + } + }; + let from_node = FormatTreeNode::with_children( + AstFormatContext::with_children("FROM".to_string(), 1), + vec![from_node], + ); + children.push(from_node); + + // columns + if let Some(columns) = ©.dst_columns { + let mut columns_children = Vec::with_capacity(columns.len()); + for column in columns.iter() { + self.visit_identifier(column); + columns_children.push(self.children.pop().unwrap()); + } + let columns_name = "Columns".to_string(); + let columns_format_ctx = + AstFormatContext::with_children(columns_name, columns_children.len()); + let columns_node = FormatTreeNode::with_children(columns_format_ctx, columns_children); + children.push(columns_node); + } + if let Some(files) = ©.files { let mut files_children = Vec::with_capacity(files.len()); for file in files.iter() { @@ -770,46 +809,72 @@ impl<'ast> Visitor<'ast> for AstFormatVisitor { let disable_variant_check_node = FormatTreeNode::new(disable_variant_check_ctx); children.push(disable_variant_check_node); - let name = "Copy".to_string(); + let name = "CopyIntoTable".to_string(); let format_ctx = AstFormatContext::with_children(name, children.len()); let node = FormatTreeNode::with_children(format_ctx, children); self.children.push(node); } - fn visit_copy_unit(&mut self, copy_unit: &'ast CopyUnit) { - match copy_unit { - CopyUnit::Table { - catalog, - database, - table, - columns, - } => { - self.visit_table_ref(catalog, database, table); - if let Some(columns) = columns { - let mut columns_children = Vec::with_capacity(columns.len()); - for column in columns.iter() { - self.visit_identifier(column); - columns_children.push(self.children.pop().unwrap()); - } - let columns_name = "Columns".to_string(); - let columns_format_ctx = - AstFormatContext::with_children(columns_name, columns_children.len()); - let columns_node = - FormatTreeNode::with_children(columns_format_ctx, columns_children); - self.children.push(columns_node); - } + fn visit_copy_into_location(&mut self, copy: &'ast CopyIntoLocationStmt) { + let mut children = Vec::new(); + + // to + let to_node = FormatTreeNode::new(AstFormatContext::new(format!("Location {}", copy.dst))); + let to_node = FormatTreeNode::with_children( + AstFormatContext::with_children("TO".to_string(), 1), + vec![to_node], + ); + children.push(to_node); + + // from + let from_node = match ©.src { + CopyIntoLocationSource::Table(table) => { + self.visit_table_ref(&table.catalog, &table.database, &table.table); + let from_node = self.children.pop().unwrap(); + FormatTreeNode::with_children( + AstFormatContext::with_children("Table".to_string(), 1), + vec![from_node], + ) + } + CopyIntoLocationSource::Query(query) => { + self.visit_query(query); + FormatTreeNode::with_children( + AstFormatContext::with_children("Query".to_string(), 1), + vec![self.children.pop().unwrap()], + ) } - CopyUnit::Location(v) => { - let location_format_ctx = AstFormatContext::new(format!("Location {}", v)); - let location_node = FormatTreeNode::new(location_format_ctx); - self.children.push(location_node); + }; + let from_node = FormatTreeNode::with_children( + AstFormatContext::with_children("FROM".to_string(), 1), + vec![from_node], + ); + children.push(from_node); + + if !copy.file_format.is_empty() { + let mut file_formats_children = Vec::with_capacity(copy.file_format.len()); + for (k, v) in copy.file_format.iter() { + let file_format_name = format!("FileFormat {} = {:?}", k, v); + let file_format_format_ctx = AstFormatContext::new(file_format_name); + let file_format_node = FormatTreeNode::new(file_format_format_ctx); + file_formats_children.push(file_format_node); } - CopyUnit::Query(query) => self.visit_query(query), + let file_formats_format_name = "FileFormats".to_string(); + let files_formats_format_ctx = AstFormatContext::with_children( + file_formats_format_name, + file_formats_children.len(), + ); + let files_formats_node = + FormatTreeNode::with_children(files_formats_format_ctx, file_formats_children); + children.push(files_formats_node); } - let child = self.children.pop().unwrap(); - let name = "CopyUnit".to_string(); - let format_ctx = AstFormatContext::with_children(name, 1); - let node = FormatTreeNode::with_children(format_ctx, vec![child]); + children.push(FormatTreeNode::new(AstFormatContext::new(format!( + "Single {}", + copy.single + )))); + + let name = "CopyIntoLocation".to_string(); + let format_ctx = AstFormatContext::with_children(name, children.len()); + let node = FormatTreeNode::with_children(format_ctx, children); self.children.push(node); } diff --git a/src/query/ast/src/ast/format/syntax/dml.rs b/src/query/ast/src/ast/format/syntax/dml.rs index 28ac0d8220ff..3d58268b4558 100644 --- a/src/query/ast/src/ast/format/syntax/dml.rs +++ b/src/query/ast/src/ast/format/syntax/dml.rs @@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +use std::collections::BTreeMap; + use pretty::RcDoc; use super::expr::pretty_expr; @@ -21,8 +23,10 @@ use crate::ast::format::syntax::inline_comma; use crate::ast::format::syntax::interweave_comma; use crate::ast::format::syntax::parenthesized; use crate::ast::format::syntax::NEST_FACTOR; -use crate::ast::CopyStmt; -use crate::ast::CopyUnit; +use crate::ast::CopyIntoLocationSource; +use crate::ast::CopyIntoLocationStmt; +use crate::ast::CopyIntoTableSource; +use crate::ast::CopyIntoTableStmt; use crate::ast::Expr; use crate::ast::InsertSource; use crate::ast::InsertStmt; @@ -175,22 +179,26 @@ fn pretty_update_list(update_list: Vec) -> RcDoc<'static> { ) } -pub(crate) fn pretty_copy(copy_stmt: CopyStmt) -> RcDoc<'static> { +pub(crate) fn pretty_copy_into_table(copy_stmt: CopyIntoTableStmt) -> RcDoc<'static> { RcDoc::text("COPY") .append(RcDoc::line().append(RcDoc::text("INTO "))) - .append(pretty_copy_unit(copy_stmt.dst)) - .append(RcDoc::line().append(RcDoc::text("FROM "))) - .append(pretty_copy_unit(copy_stmt.src)) - .append(if let Some(files) = ©_stmt.files { - RcDoc::line() - .append(RcDoc::text("FILES = ")) - .append(parenthesized( - interweave_comma(files.iter().map(|file| RcDoc::text(format!("{:?}", file)))) - .group(), - )) + .append(RcDoc::text(format!("{}", copy_stmt.dst))) + .append(if let Some(cols) = ©_stmt.dst_columns { + parenthesized( + interweave_comma(cols.iter().map(|file| RcDoc::text(format!("{:?}", file)))) + .group(), + ) } else { RcDoc::nil() }) + .append(RcDoc::line().append(RcDoc::text("FROM "))) + .append(match copy_stmt.src { + CopyIntoTableSource::Location(v) => RcDoc::text(format!("{v}")), + CopyIntoTableSource::Query(query) => RcDoc::text("(") + .append(pretty_query(*query)) + .append(RcDoc::text(")")), + }) + .append(pretty_file_format(©_stmt.file_format)) .append(if let Some(pattern) = ©_stmt.pattern { RcDoc::line() .append(RcDoc::text("PATTERN = ")) @@ -198,18 +206,12 @@ pub(crate) fn pretty_copy(copy_stmt: CopyStmt) -> RcDoc<'static> { } else { RcDoc::nil() }) - .append(if !copy_stmt.file_format.is_empty() { + .append(if let Some(files) = ©_stmt.files { RcDoc::line() - .append(RcDoc::text("FILE_FORMAT = ")) + .append(RcDoc::text("FILES = ")) .append(parenthesized( - interweave_comma(copy_stmt.file_format.iter().map(|(k, v)| { - RcDoc::text(k.to_string()) - .append(RcDoc::space()) - .append(RcDoc::text("=")) - .append(RcDoc::space()) - .append(RcDoc::text(format!("{:?}", v))) - })) - .group(), + interweave_comma(files.iter().map(|file| RcDoc::text(format!("{:?}", file)))) + .group(), )) } else { RcDoc::nil() @@ -247,43 +249,40 @@ pub(crate) fn pretty_copy(copy_stmt: CopyStmt) -> RcDoc<'static> { ) } -fn pretty_copy_unit(copy_unit: CopyUnit) -> RcDoc<'static> { - match copy_unit { - CopyUnit::Table { - catalog, - database, - table, - columns, - } => if let Some(catalog) = catalog { - RcDoc::text(catalog.to_string()).append(RcDoc::text(".")) - } else { - RcDoc::nil() - } - .append(if let Some(database) = database { - RcDoc::text(database.to_string()).append(RcDoc::text(".")) - } else { - RcDoc::nil() +pub(crate) fn pretty_copy_into_location(copy_stmt: CopyIntoLocationStmt) -> RcDoc<'static> { + RcDoc::text("COPY") + .append(RcDoc::line().append(RcDoc::text("INTO "))) + .append(RcDoc::text(format!("{:?}", copy_stmt.dst))) + .append(RcDoc::line().append(RcDoc::text("FROM "))) + .append(match copy_stmt.src { + CopyIntoLocationSource::Table(v) => RcDoc::text(format!("{v}")), + CopyIntoLocationSource::Query(query) => RcDoc::text("(") + .append(pretty_query(*query)) + .append(RcDoc::text(")")), }) - .append(RcDoc::text(table.to_string())) - .append(if let Some(columns) = columns { + .append(pretty_file_format(©_stmt.file_format)) + .append( RcDoc::line() - .append(RcDoc::text("(")) - .append( - interweave_comma( - columns - .into_iter() - .map(|column| RcDoc::text(column.to_string())), - ) - .nest(NEST_FACTOR) - .group(), - ) - .append(RcDoc::text(")")) - } else { - RcDoc::nil() - }), - CopyUnit::Location(v) => RcDoc::text(v.to_string()), - CopyUnit::Query(query) => RcDoc::text("(") - .append(pretty_query(*query)) - .append(RcDoc::text(")")), + .append(RcDoc::text("SINGLE = ")) + .append(RcDoc::text(copy_stmt.single.to_string())), + ) +} + +fn pretty_file_format(file_format: &BTreeMap) -> RcDoc<'static> { + if !file_format.is_empty() { + RcDoc::line() + .append(RcDoc::text("FILE_FORMAT = ")) + .append(parenthesized( + interweave_comma(file_format.iter().map(|(k, v)| { + RcDoc::text(k.to_string()) + .append(RcDoc::space()) + .append(RcDoc::text("=")) + .append(RcDoc::space()) + .append(RcDoc::text(format!("{:?}", v))) + })) + .group(), + )) + } else { + RcDoc::nil() } } diff --git a/src/query/ast/src/ast/format/syntax/mod.rs b/src/query/ast/src/ast/format/syntax/mod.rs index 5a99ac67c2b1..d7d5b00c7184 100644 --- a/src/query/ast/src/ast/format/syntax/mod.rs +++ b/src/query/ast/src/ast/format/syntax/mod.rs @@ -35,7 +35,8 @@ pub fn pretty_statement(stmt: Statement, max_width: usize) -> Result { selection, .. } => pretty_delete(table_reference, selection), - Statement::Copy(copy_stmt) => pretty_copy(copy_stmt), + Statement::CopyIntoTable(copy_stmt) => pretty_copy_into_table(copy_stmt), + Statement::CopyIntoLocation(copy_stmt) => pretty_copy_into_location(copy_stmt), Statement::Update(update_stmt) => pretty_update(update_stmt), Statement::CreateTable(create_table_stmt) => pretty_create_table(create_table_stmt), Statement::AlterTable(alter_table_stmt) => pretty_alter_table(alter_table_stmt), diff --git a/src/query/ast/src/ast/statements/copy.rs b/src/query/ast/src/ast/statements/copy.rs index e08b3af6b10b..65817847b67c 100644 --- a/src/query/ast/src/ast/statements/copy.rs +++ b/src/query/ast/src/ast/statements/copy.rs @@ -29,7 +29,43 @@ use crate::ast::Hint; use crate::ast::Identifier; use crate::ast::Query; -/// CopyStmt is the parsed statement of `COPY`. +#[derive(Debug, Clone, PartialEq)] +pub struct TableIdentifier { + pub catalog: Option, + pub database: Option, + pub table: Identifier, +} + +impl TableIdentifier { + pub fn from_tuple(t: (Option, Option, Identifier)) -> Self { + let (catalog, database, table) = t; + Self { + catalog, + database, + table, + } + } +} + +impl Display for TableIdentifier { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + if let Some(catalog) = &self.catalog { + write!( + f, + "{catalog}.{}.{}", + self.database.as_ref().expect("database must be valid"), + self.table + )?; + } else if let Some(database) = &self.database { + write!(f, "{database}.{}", self.table)?; + } else { + write!(f, "{}", self.table)?; + }; + Ok(()) + } +} + +/// CopyIntoTableStmt is the parsed statement of `COPY into from `. /// /// ## Examples /// @@ -37,55 +73,61 @@ use crate::ast::Query; /// COPY INTO table from s3://bucket/path/to/x.csv /// ``` #[derive(Debug, Clone, PartialEq)] -pub struct CopyStmt { +pub struct CopyIntoTableStmt { + pub src: CopyIntoTableSource, + pub dst: TableIdentifier, + pub dst_columns: Option>, + pub hints: Option, - pub src: CopyUnit, - pub dst: CopyUnit, + + pub file_format: BTreeMap, + + // files to load pub files: Option>, pub pattern: Option, - pub file_format: BTreeMap, + pub force: bool, + + // copy options /// TODO(xuanwo): parse into validation_mode directly. pub validation_mode: String, pub size_limit: usize, pub max_files: usize, - pub max_file_size: usize, pub split_size: usize, - pub single: bool, pub purge: bool, - pub force: bool, pub disable_variant_check: bool, pub return_failed_only: bool, pub on_error: String, } -impl CopyStmt { - pub fn apply_option(&mut self, opt: CopyOption) { +impl CopyIntoTableStmt { + pub fn apply_option(&mut self, opt: CopyIntoTableOption) { match opt { - CopyOption::Files(v) => self.files = Some(v), - CopyOption::Pattern(v) => self.pattern = Some(v), - CopyOption::FileFormat(v) => self.file_format = v, - CopyOption::ValidationMode(v) => self.validation_mode = v, - CopyOption::SizeLimit(v) => self.size_limit = v, - CopyOption::MaxFiles(v) => self.max_files = v, - CopyOption::MaxFileSize(v) => self.max_file_size = v, - CopyOption::SplitSize(v) => self.split_size = v, - CopyOption::Single(v) => self.single = v, - CopyOption::Purge(v) => self.purge = v, - CopyOption::Force(v) => self.force = v, - CopyOption::DisableVariantCheck(v) => self.disable_variant_check = v, - CopyOption::ReturnFailedOnly(v) => self.return_failed_only = v, - CopyOption::OnError(v) => self.on_error = v, + CopyIntoTableOption::Files(v) => self.files = Some(v), + CopyIntoTableOption::Pattern(v) => self.pattern = Some(v), + CopyIntoTableOption::FileFormat(v) => self.file_format = v, + CopyIntoTableOption::ValidationMode(v) => self.validation_mode = v, + CopyIntoTableOption::SizeLimit(v) => self.size_limit = v, + CopyIntoTableOption::MaxFiles(v) => self.max_files = v, + CopyIntoTableOption::SplitSize(v) => self.split_size = v, + CopyIntoTableOption::Purge(v) => self.purge = v, + CopyIntoTableOption::Force(v) => self.force = v, + CopyIntoTableOption::DisableVariantCheck(v) => self.disable_variant_check = v, + CopyIntoTableOption::ReturnFailedOnly(v) => self.return_failed_only = v, + CopyIntoTableOption::OnError(v) => self.on_error = v, } } } -impl Display for CopyStmt { +impl Display for CopyIntoTableStmt { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "COPY")?; if let Some(hints) = &self.hints { write!(f, "{} ", hints)?; } write!(f, " INTO {}", self.dst)?; + if let Some(columns) = &self.dst_columns { + write!(f, "({})", columns.iter().map(|c| c.to_string()).join(","))?; + } write!(f, " FROM {}", self.src)?; if let Some(files) = &self.files { @@ -116,15 +158,10 @@ impl Display for CopyStmt { write!(f, " MAX_FILES = {}", self.max_files)?; } - if self.max_file_size != 0 { - write!(f, " MAX_FILE_SIZE = {}", self.max_file_size)?; - } - if self.split_size != 0 { write!(f, " SPLIT_SIZE = {}", self.split_size)?; } - write!(f, " SINGLE = {}", self.single)?; write!(f, " PURGE = {}", self.purge)?; write!(f, " FORCE = {}", self.force)?; write!(f, " DISABLE_VARIANT_CHECK = {}", self.disable_variant_check)?; @@ -134,65 +171,83 @@ impl Display for CopyStmt { } } -/// CopyUnit is the unit that can be used in `COPY`. +/// CopyIntoLocationStmt is the parsed statement of `COPY into from
...` #[derive(Debug, Clone, PartialEq)] -pub enum CopyUnit { - /// Table can be used in `INTO` or `FROM`. - /// - /// While table used as `FROM`, it will be rewrite as `(SELECT * FROM table)` - Table { - catalog: Option, - database: Option, - table: Identifier, - columns: Option>, - }, +pub struct CopyIntoLocationStmt { + pub hints: Option, + pub src: CopyIntoLocationSource, + pub dst: FileLocation, + pub file_format: BTreeMap, + pub single: bool, + pub max_file_size: usize, +} + +impl Display for CopyIntoLocationStmt { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "COPY")?; + if let Some(hints) = &self.hints { + write!(f, "{} ", hints)?; + } + write!(f, " INTO {}", self.dst)?; + write!(f, " FROM {}", self.src)?; + + if !self.file_format.is_empty() { + write!(f, " FILE_FORMAT = (")?; + write_comma_separated_map(f, &self.file_format)?; + write!(f, ")")?; + } + write!(f, " SINGLE = {}", self.single)?; + write!(f, " MAX_FILE_SIZE= {}", self.max_file_size)?; + + Ok(()) + } +} + +impl CopyIntoLocationStmt { + pub fn apply_option(&mut self, opt: CopyIntoLocationOption) { + match opt { + CopyIntoLocationOption::FileFormat(v) => self.file_format = v, + CopyIntoLocationOption::Single(v) => self.single = v, + CopyIntoLocationOption::MaxFileSize(v) => self.max_file_size = v, + } + } +} + +#[derive(Debug, Clone, PartialEq)] +pub enum CopyIntoTableSource { Location(FileLocation), - /// Query can only be used as `FROM`. - /// - /// For example:`(SELECT field_a,field_b FROM table)` + /// Load with Transform + /// limited to `(SELECT ... FROM )` Query(Box), } -impl CopyUnit { - pub fn target(&self) -> &'static str { +impl Display for CopyIntoTableSource { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { - CopyUnit::Table { .. } => "Table", - CopyUnit::Location { .. } => "Location", - CopyUnit::Query(_) => "Query", + CopyIntoTableSource::Location(v) => v.fmt(f), + CopyIntoTableSource::Query(query) => { + write!(f, "({query})") + } } } } -impl Display for CopyUnit { +#[derive(Debug, Clone, PartialEq)] +pub enum CopyIntoLocationSource { + Query(Box), + /// it will be rewrite as `(SELECT * FROM table)` + Table(TableIdentifier), +} + +impl Display for CopyIntoLocationSource { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { - CopyUnit::Table { - catalog, - database, - table, - columns, - } => { - let ret = if let Some(catalog) = catalog { - write!( - f, - "{catalog}.{}.{table}", - database.as_ref().expect("database must be valid") - ) - } else if let Some(database) = database { - write!(f, "{database}.{table}") - } else { - write!(f, "{table}") - }; - if let Some(columns) = columns { - write!(f, "({})", columns.iter().map(|c| c.to_string()).join(",")) - } else { - ret - } - } - CopyUnit::Location(v) => v.fmt(f), - CopyUnit::Query(query) => { + CopyIntoLocationSource::Query(query) => { write!(f, "({query})") } + CopyIntoLocationSource::Table(table) => { + write!(f, "{}", table) + } } } } @@ -380,19 +435,23 @@ impl Display for FileLocation { } } -pub enum CopyOption { +pub enum CopyIntoTableOption { Files(Vec), Pattern(String), FileFormat(BTreeMap), ValidationMode(String), SizeLimit(usize), MaxFiles(usize), - MaxFileSize(usize), SplitSize(usize), - Single(bool), Purge(bool), Force(bool), DisableVariantCheck(bool), ReturnFailedOnly(bool), OnError(String), } + +pub enum CopyIntoLocationOption { + FileFormat(BTreeMap), + MaxFileSize(usize), + Single(bool), +} diff --git a/src/query/ast/src/ast/statements/statement.rs b/src/query/ast/src/ast/statements/statement.rs index 8ae30da9c555..074c5f80dd04 100644 --- a/src/query/ast/src/ast/statements/statement.rs +++ b/src/query/ast/src/ast/statements/statement.rs @@ -39,7 +39,9 @@ pub enum Statement { query: Box, }, - Copy(CopyStmt), + CopyIntoTable(CopyIntoTableStmt), + CopyIntoLocation(CopyIntoLocationStmt), + Call(CallStmt), ShowSettings { @@ -238,17 +240,23 @@ pub struct StatementMsg { impl Statement { pub fn to_mask_sql(&self) -> String { match self { - Statement::Copy(copy) => { + Statement::CopyIntoTable(copy) => { let mut copy_clone = copy.clone(); - if let CopyUnit::Location(FileLocation::Uri(location)) = &mut copy_clone.src { + if let CopyIntoTableSource::Location(FileLocation::Uri(location)) = + &mut copy_clone.src + { location.connection = location.connection.mask() } + format!("{}", Statement::CopyIntoTable(copy_clone)) + } + Statement::CopyIntoLocation(copy) => { + let mut copy_clone = copy.clone(); - if let CopyUnit::Location(FileLocation::Uri(location)) = &mut copy_clone.dst { + if let FileLocation::Uri(location) = &mut copy_clone.dst { location.connection = location.connection.mask() } - format!("{}", Statement::Copy(copy_clone)) + format!("{}", Statement::CopyIntoLocation(copy_clone)) } Statement::CreateStage(stage) => { let mut stage_clone = stage.clone(); @@ -302,7 +310,8 @@ impl Display for Statement { } } Statement::Update(update) => write!(f, "{update}")?, - Statement::Copy(stmt) => write!(f, "{stmt}")?, + Statement::CopyIntoTable(stmt) => write!(f, "{stmt}")?, + Statement::CopyIntoLocation(stmt) => write!(f, "{stmt}")?, Statement::ShowSettings { like } => { write!(f, "SHOW SETTINGS")?; if like.is_some() { diff --git a/src/query/ast/src/parser/copy.rs b/src/query/ast/src/parser/copy.rs new file mode 100644 index 000000000000..a8c1b3cf6dab --- /dev/null +++ b/src/query/ast/src/parser/copy.rs @@ -0,0 +1,211 @@ +// Copyright 2021 Datafuse Labs +// +// 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. + +use std::cmp::min; + +use nom::branch::alt; +use nom::combinator::map; + +use crate::ast::CopyIntoLocationOption; +use crate::ast::CopyIntoLocationSource; +use crate::ast::CopyIntoLocationStmt; +use crate::ast::CopyIntoTableOption; +use crate::ast::CopyIntoTableSource; +use crate::ast::CopyIntoTableStmt; +use crate::ast::Statement; +use crate::ast::Statement::CopyIntoLocation; +use crate::ast::TableIdentifier; +use crate::parser::expr::literal_bool; +use crate::parser::expr::literal_string; +use crate::parser::expr::literal_u64; +use crate::parser::query::query; +use crate::parser::stage::file_format_clause; +use crate::parser::stage::file_location; +use crate::parser::statement::hint; +use crate::parser::token::TokenKind::COPY; +use crate::parser::token::TokenKind::*; +use crate::rule; +use crate::util::comma_separated_list0; +use crate::util::comma_separated_list1; +use crate::util::dot_separated_idents_1_to_3; +use crate::util::ident; +use crate::util::IResult; +use crate::Input; + +const MAX_COPIED_FILES_NUM: usize = 2000; + +fn table_triple(i: Input) -> IResult { + map(dot_separated_idents_1_to_3, TableIdentifier::from_tuple)(i) +} + +fn copy_into_table(i: Input) -> IResult { + let copy_into_table_source = alt(( + map(file_location, CopyIntoTableSource::Location), + map(rule! { "(" ~ #query ~ ")" }, |(_, query, _)| { + CopyIntoTableSource::Query(Box::new(query)) + }), + )); + + map( + rule! { + COPY + ~ #hint? + ~ INTO ~ #table_triple ~ ( "(" ~ #comma_separated_list1(ident) ~ ")" )? + ~ ^FROM ~ ^#copy_into_table_source + ~ #copy_into_table_option* + }, + |(_copy, opt_hints, _into, dst, dst_columns, _from, src, opts)| { + let mut copy_stmt = CopyIntoTableStmt { + hints: opt_hints, + src, + dst, + dst_columns: dst_columns.map(|(_, columns, _)| columns), + files: Default::default(), + pattern: Default::default(), + file_format: Default::default(), + validation_mode: Default::default(), + size_limit: Default::default(), + max_files: Default::default(), + split_size: Default::default(), + purge: Default::default(), + force: Default::default(), + disable_variant_check: Default::default(), + on_error: "abort".to_string(), + return_failed_only: Default::default(), + }; + for opt in opts { + copy_stmt.apply_option(opt); + } + Statement::CopyIntoTable(copy_stmt) + }, + )(i) +} + +fn copy_into_location(i: Input) -> IResult { + let copy_into_location_source = alt(( + map(table_triple, CopyIntoLocationSource::Table), + map(rule! { "(" ~ #query ~ ")" }, |(_, query, _)| { + CopyIntoLocationSource::Query(Box::new(query)) + }), + )); + + map( + rule! { + COPY + ~ #hint? + ~ INTO ~ #file_location + ~ ^FROM ~ ^#copy_into_location_source + ~ #copy_into_location_option* + }, + |(_copy, opt_hints, _into, dst, _from, src, opts)| { + let mut copy_stmt = CopyIntoLocationStmt { + hints: opt_hints, + src, + dst, + file_format: Default::default(), + single: Default::default(), + max_file_size: Default::default(), + }; + for opt in opts { + copy_stmt.apply_option(opt); + } + CopyIntoLocation(copy_stmt) + }, + )(i) +} +pub fn copy_into(i: Input) -> IResult { + rule!( + #copy_into_location:"`COPY + INTO { internalStage | externalStage | externalLocation } + FROM { [.] | ( ) } + [ FILE_FORMAT = ( { TYPE = { CSV | JSON | PARQUET | TSV } [ formatTypeOptions ] } ) ] + [ copyOptions ]`" + | #copy_into_table: "`COPY + INTO { [.] { ( ) } } + FROM { internalStage | externalStage | externalLocation | ( ) } + [ FILE_FORMAT = ( { TYPE = { CSV | JSON | PARQUET | TSV } [ formatTypeOptions ] } ) ] + [ FILES = ( '' [ , '' ] [ , ... ] ) ] + [ PATTERN = '' ] + [ VALIDATION_MODE = RETURN_ROWS ] + [ copyOptions ]`" + )(i) +} + +fn copy_into_table_option(i: Input) -> IResult { + alt(( + map( + rule! { FILES ~ "=" ~ "(" ~ #comma_separated_list0(literal_string) ~ ")" }, + |(_, _, _, files, _)| CopyIntoTableOption::Files(files), + ), + map( + rule! { PATTERN ~ "=" ~ #literal_string }, + |(_, _, pattern)| CopyIntoTableOption::Pattern(pattern), + ), + map(rule! { #file_format_clause }, |options| { + CopyIntoTableOption::FileFormat(options) + }), + map( + rule! { VALIDATION_MODE ~ "=" ~ #literal_string }, + |(_, _, validation_mode)| CopyIntoTableOption::ValidationMode(validation_mode), + ), + map( + rule! { SIZE_LIMIT ~ "=" ~ #literal_u64 }, + |(_, _, size_limit)| CopyIntoTableOption::SizeLimit(size_limit as usize), + ), + map( + rule! { MAX_FILES ~ "=" ~ #literal_u64 }, + |(_, _, max_files)| { + CopyIntoTableOption::MaxFiles(min(MAX_COPIED_FILES_NUM, max_files as usize)) + }, + ), + map( + rule! { SPLIT_SIZE ~ "=" ~ #literal_u64 }, + |(_, _, split_size)| CopyIntoTableOption::SplitSize(split_size as usize), + ), + map(rule! { PURGE ~ "=" ~ #literal_bool }, |(_, _, purge)| { + CopyIntoTableOption::Purge(purge) + }), + map(rule! { FORCE ~ "=" ~ #literal_bool }, |(_, _, force)| { + CopyIntoTableOption::Force(force) + }), + map(rule! { ON_ERROR ~ "=" ~ #ident }, |(_, _, on_error)| { + CopyIntoTableOption::OnError(on_error.to_string()) + }), + map( + rule! { DISABLE_VARIANT_CHECK ~ "=" ~ #literal_bool }, + |(_, _, disable_variant_check)| { + CopyIntoTableOption::DisableVariantCheck(disable_variant_check) + }, + ), + map( + rule! { RETURN_FAILED_ONLY ~ "=" ~ #literal_bool }, + |(_, _, return_failed_only)| CopyIntoTableOption::ReturnFailedOnly(return_failed_only), + ), + ))(i) +} + +fn copy_into_location_option(i: Input) -> IResult { + alt(( + map(rule! { SINGLE ~ "=" ~ #literal_bool }, |(_, _, single)| { + CopyIntoLocationOption::Single(single) + }), + map( + rule! { MAX_FILE_SIZE ~ "=" ~ #literal_u64 }, + |(_, _, max_file_size)| CopyIntoLocationOption::MaxFileSize(max_file_size as usize), + ), + map(rule! { #file_format_clause }, |options| { + CopyIntoLocationOption::FileFormat(options) + }), + ))(i) +} diff --git a/src/query/ast/src/parser/mod.rs b/src/query/ast/src/parser/mod.rs index 4ff2948c5b8f..101d2aa2764f 100644 --- a/src/query/ast/src/parser/mod.rs +++ b/src/query/ast/src/parser/mod.rs @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +mod copy; mod data_mask; pub mod expr; #[allow(clippy::module_inception)] diff --git a/src/query/ast/src/parser/statement.rs b/src/query/ast/src/parser/statement.rs index c9aa5d1399cb..2e2f74ad0651 100644 --- a/src/query/ast/src/parser/statement.rs +++ b/src/query/ast/src/parser/statement.rs @@ -12,7 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::cmp::min; use std::collections::BTreeMap; use std::time::Duration; @@ -33,6 +32,7 @@ use nom::Slice; use crate::ast::*; use crate::input::Input; +use crate::parser::copy::copy_into; use crate::parser::data_mask::data_mask_policy; use crate::parser::expr::subexpr; use crate::parser::expr::*; @@ -45,8 +45,6 @@ use crate::util::*; use crate::Error; use crate::ErrorKind; -const MAX_COPIED_FILES_NUM: usize = 2000; - pub enum ShowGrantOption { PrincipalIdentity(PrincipalIdentity), ShareGrantObjectName(ShareGrantObjectName), @@ -1087,41 +1085,6 @@ pub fn statement(i: Input) -> IResult { }, ); - let copy_into = map( - rule! { - COPY - ~ #hint? - ~ INTO ~ #copy_unit - ~ FROM ~ #copy_unit - ~ #copy_option* - }, - |(_, opt_hints, _, dst, _, src, opts)| { - let mut copy_stmt = CopyStmt { - hints: opt_hints, - src, - dst, - files: Default::default(), - pattern: Default::default(), - file_format: Default::default(), - validation_mode: Default::default(), - size_limit: Default::default(), - max_files: Default::default(), - max_file_size: Default::default(), - split_size: Default::default(), - single: Default::default(), - purge: Default::default(), - force: Default::default(), - disable_variant_check: Default::default(), - on_error: "abort".to_string(), - return_failed_only: Default::default(), - }; - for opt in opts { - copy_stmt.apply_option(opt); - } - Statement::Copy(copy_stmt) - }, - ); - let call = map( rule! { CALL ~ #ident ~ "(" ~ #comma_separated_list0(parameter_to_string) ~ ")" @@ -1545,16 +1508,7 @@ pub fn statement(i: Input) -> IResult { | #show_file_formats: "`SHOW FILE FORMATS`" | #drop_file_format: "`DROP FILE FORMAT [ IF EXISTS ] `" ), - rule!( - #copy_into: "`COPY - INTO { internalStage | externalStage | externalLocation | [.] } - FROM { internalStage | externalStage | externalLocation | [.] | ( ) } - [ FILE_FORMAT = ( { TYPE = { CSV | JSON | PARQUET } [ formatTypeOptions ] } ) ] - [ FILES = ( '' [ , '' ] [ , ... ] ) ] - [ PATTERN = '' ] - [ VALIDATION_MODE = RETURN_ROWS ] - [ copyOptions ]`" - ), + rule!( #copy_into ), rule!( #call: "`CALL (, ...)`" ), @@ -2394,44 +2348,6 @@ pub fn kill_target(i: Input) -> IResult { ))(i) } -/// Parse input into `CopyUnit` -/// -/// # Notes -/// -/// It's required to parse stage location first. Or stage could be parsed as table. -pub fn copy_unit(i: Input) -> IResult { - // Parse input like `mytable` - let table = |i| { - map( - rule! { - #dot_separated_idents_1_to_3 - ~ ( "(" ~ #comma_separated_list1(ident) ~ ")" )? - }, - |((catalog, database, table), opt_columns)| CopyUnit::Table { - catalog, - database, - table, - columns: opt_columns.map(|(_, columns, _)| columns), - }, - )(i) - }; - - // Parse input like `( SELECT * from mytable )` - let query = |i| { - map(rule! { "(" ~ #query ~ ")" }, |(_, query, _)| { - CopyUnit::Query(Box::new(query)) - })(i) - }; - - let location = |i| map(file_location, CopyUnit::Location)(i); - - rule!( - #location - | #table: "{ { . } . }
" - | #query: "( )" - )(i) -} - pub fn show_limit(i: Input) -> IResult { let limit_like = map( rule! { @@ -2602,62 +2518,6 @@ pub fn auth_type(i: Input) -> IResult { ))(i) } -pub fn copy_option(i: Input) -> IResult { - alt(( - map( - rule! { FILES ~ "=" ~ "(" ~ #comma_separated_list0(literal_string) ~ ")" }, - |(_, _, _, files, _)| CopyOption::Files(files), - ), - map( - rule! { PATTERN ~ "=" ~ #literal_string }, - |(_, _, pattern)| CopyOption::Pattern(pattern), - ), - map(rule! { #file_format_clause }, |options| { - CopyOption::FileFormat(options) - }), - map( - rule! { VALIDATION_MODE ~ "=" ~ #literal_string }, - |(_, _, validation_mode)| CopyOption::ValidationMode(validation_mode), - ), - map( - rule! { SIZE_LIMIT ~ "=" ~ #literal_u64 }, - |(_, _, size_limit)| CopyOption::SizeLimit(size_limit as usize), - ), - map( - rule! { MAX_FILES ~ "=" ~ #literal_u64 }, - |(_, _, max_files)| CopyOption::MaxFiles(min(MAX_COPIED_FILES_NUM, max_files as usize)), - ), - map( - rule! { MAX_FILE_SIZE ~ "=" ~ #literal_u64 }, - |(_, _, max_file_size)| CopyOption::MaxFileSize(max_file_size as usize), - ), - map( - rule! { SPLIT_SIZE ~ "=" ~ #literal_u64 }, - |(_, _, split_size)| CopyOption::SplitSize(split_size as usize), - ), - map(rule! { SINGLE ~ "=" ~ #literal_bool }, |(_, _, single)| { - CopyOption::Single(single) - }), - map(rule! { PURGE ~ "=" ~ #literal_bool }, |(_, _, purge)| { - CopyOption::Purge(purge) - }), - map(rule! { FORCE ~ "=" ~ #literal_bool }, |(_, _, force)| { - CopyOption::Force(force) - }), - map(rule! { ON_ERROR ~ "=" ~ #ident }, |(_, _, on_error)| { - CopyOption::OnError(on_error.to_string()) - }), - map( - rule! { DISABLE_VARIANT_CHECK ~ "=" ~ #literal_bool }, - |(_, _, disable_variant_check)| CopyOption::DisableVariantCheck(disable_variant_check), - ), - map( - rule! { RETURN_FAILED_ONLY ~ "=" ~ #literal_bool }, - |(_, _, return_failed_only)| CopyOption::ReturnFailedOnly(return_failed_only), - ), - ))(i) -} - pub fn presign_action(i: Input) -> IResult { alt(( value(PresignAction::Download, rule! { DOWNLOAD }), diff --git a/src/query/ast/src/visitors/visitor.rs b/src/query/ast/src/visitors/visitor.rs index 520dddc74a68..2ca77a112eaf 100644 --- a/src/query/ast/src/visitors/visitor.rs +++ b/src/query/ast/src/visitors/visitor.rs @@ -366,9 +366,8 @@ pub trait Visitor<'ast>: Sized { fn visit_explain(&mut self, _kind: &'ast ExplainKind, _query: &'ast Statement) {} - fn visit_copy(&mut self, _copy: &'ast CopyStmt) {} - - fn visit_copy_unit(&mut self, _copy_unit: &'ast CopyUnit) {} + fn visit_copy_into_table(&mut self, _copy: &'ast CopyIntoTableStmt) {} + fn visit_copy_into_location(&mut self, _copy: &'ast CopyIntoLocationStmt) {} fn visit_call(&mut self, _call: &'ast CallStmt) {} diff --git a/src/query/ast/src/visitors/visitor_mut.rs b/src/query/ast/src/visitors/visitor_mut.rs index bee45ee26889..bcb80e6e2391 100644 --- a/src/query/ast/src/visitors/visitor_mut.rs +++ b/src/query/ast/src/visitors/visitor_mut.rs @@ -381,9 +381,8 @@ pub trait VisitorMut: Sized { walk_statement_mut(self, stmt); } - fn visit_copy(&mut self, _copy: &mut CopyStmt) {} - - fn visit_copy_unit(&mut self, _copy_unit: &mut CopyUnit) {} + fn visit_copy_into_table(&mut self, _copy: &mut CopyIntoTableStmt) {} + fn visit_copy_into_location(&mut self, _copy: &mut CopyIntoLocationStmt) {} fn visit_call(&mut self, _call: &mut CallStmt) {} diff --git a/src/query/ast/src/visitors/walk.rs b/src/query/ast/src/visitors/walk.rs index 383950b98a3e..943655f8ac41 100644 --- a/src/query/ast/src/visitors/walk.rs +++ b/src/query/ast/src/visitors/walk.rs @@ -343,7 +343,8 @@ pub fn walk_statement<'a, V: Visitor<'a>>(visitor: &mut V, statement: &'a Statem .. } => visitor.visit_delete(table_reference, selection), Statement::Update(update) => visitor.visit_update(update), - Statement::Copy(stmt) => visitor.visit_copy(stmt), + Statement::CopyIntoTable(stmt) => visitor.visit_copy_into_table(stmt), + Statement::CopyIntoLocation(stmt) => visitor.visit_copy_into_location(stmt), Statement::ShowSettings { like } => visitor.visit_show_settings(like), Statement::ShowProcessList => visitor.visit_show_process_list(), Statement::ShowMetrics => visitor.visit_show_metrics(), diff --git a/src/query/ast/src/visitors/walk_mut.rs b/src/query/ast/src/visitors/walk_mut.rs index d29873105d6a..1c66fcbebd72 100644 --- a/src/query/ast/src/visitors/walk_mut.rs +++ b/src/query/ast/src/visitors/walk_mut.rs @@ -318,7 +318,8 @@ pub fn walk_statement_mut(visitor: &mut V, statement: &mut Statem .. } => visitor.visit_delete(table_reference, selection), Statement::Update(update) => visitor.visit_update(update), - Statement::Copy(stmt) => visitor.visit_copy(stmt), + Statement::CopyIntoLocation(stmt) => visitor.visit_copy_into_location(stmt), + Statement::CopyIntoTable(stmt) => visitor.visit_copy_into_table(stmt), Statement::ShowSettings { like } => visitor.visit_show_settings(like), Statement::ShowProcessList => visitor.visit_show_process_list(), Statement::ShowMetrics => visitor.visit_show_metrics(), diff --git a/src/query/ast/tests/it/parser.rs b/src/query/ast/tests/it/parser.rs index 230fb54c3966..337ed1c7cba5 100644 --- a/src/query/ast/tests/it/parser.rs +++ b/src/query/ast/tests/it/parser.rs @@ -323,11 +323,9 @@ fn test_statement() { field_delimiter = ',' record_delimiter = '\n' skip_header = 1 - ) - size_limit=10;"#, + )"#, r#"COPY INTO '@my_stage/my data' - FROM mytable - size_limit=10;"#, + FROM mytable;"#, r#"COPY INTO @my_stage FROM mytable FILE_FORMAT = ( @@ -335,8 +333,7 @@ fn test_statement() { field_delimiter = ',' record_delimiter = '\n' skip_header = 1 - ) - size_limit=10;"#, + );"#, r#"COPY INTO mytable FROM 's3://mybucket/data.csv' CREDENTIALS = ( @@ -387,8 +384,7 @@ fn test_statement() { ) size_limit=10 disable_variant_check=true;"#, - r#"copy into t1 from "" FILE_FORMAT = (TYPE = TSV, COMPRESSION = GZIP)"#, - r#"COPY INTO books FROM 's3://databend/books.csv' + r#"COPY INTO books FROM 's3://databend/books.csv' CONNECTION = ( ENDPOINT_URL = 'http://localhost:9000/', ACCESS_KEY_ID = 'ROOTUSER', diff --git a/src/query/ast/tests/it/testdata/statement-error.txt b/src/query/ast/tests/it/testdata/statement-error.txt index 63157363c243..725e47ba9fc6 100644 --- a/src/query/ast/tests/it/testdata/statement-error.txt +++ b/src/query/ast/tests/it/testdata/statement-error.txt @@ -307,7 +307,7 @@ error: --> SQL:1:38 | 1 | COPY INTO mytable FROM 's3://bucket' CREDENTIAL = (); - | ^^^^^^^^^^ expected `CREDENTIALS`, `DISABLE_VARIANT_CHECK`, `RETURN_FAILED_ONLY`, `CONNECTION`, `PURGE`, `VALIDATION_MODE`, `FORCE`, `LOCATION_PREFIX`, `SINGLE`, `FORMAT`, `PATTERN`, `FILES`, `MAX_FILES`, `SIZE_LIMIT`, `FILE_FORMAT`, `MAX_FILE_SIZE`, `ON_ERROR`, `SPLIT_SIZE`, or `;` + | ^^^^^^^^^^ expected `CREDENTIALS`, `DISABLE_VARIANT_CHECK`, `RETURN_FAILED_ONLY`, `CONNECTION`, `PURGE`, `VALIDATION_MODE`, `FORCE`, `LOCATION_PREFIX`, `FORMAT`, `PATTERN`, `FILES`, `MAX_FILES`, `SIZE_LIMIT`, `FILE_FORMAT`, `ON_ERROR`, `SPLIT_SIZE`, or `;` ---------- Input ---------- @@ -317,7 +317,7 @@ error: --> SQL:1:33 | 1 | COPY INTO mytable FROM @mystage CREDENTIALS = (); - | ^^^^^^^^^^^ expected `DISABLE_VARIANT_CHECK`, `RETURN_FAILED_ONLY`, `MAX_FILES`, `PURGE`, `VALIDATION_MODE`, `MAX_FILE_SIZE`, `FORCE`, `SINGLE`, `FORMAT`, `PATTERN`, `FILES`, `SIZE_LIMIT`, `SPLIT_SIZE`, `FILE_FORMAT`, `ON_ERROR`, or `;` + | ^^^^^^^^^^^ expected `DISABLE_VARIANT_CHECK`, `RETURN_FAILED_ONLY`, `MAX_FILES`, `PURGE`, `VALIDATION_MODE`, `FORCE`, `FORMAT`, `PATTERN`, `FILES`, `SIZE_LIMIT`, `SPLIT_SIZE`, `FILE_FORMAT`, `ON_ERROR`, or `;` ---------- Input ---------- @@ -500,25 +500,34 @@ error: copy into t1 from "" FILE ---------- Output --------- error: - --> SQL:1:22 + --> SQL:1:19 | 1 | copy into t1 from "" FILE - | ^^^^ expected `FILES`, `FILE_FORMAT`, `MAX_FILE_SIZE`, `SINGLE`, `RETURN_FAILED_ONLY`, `DISABLE_VARIANT_CHECK`, `FORCE`, `SIZE_LIMIT`, `PURGE`, `FORMAT`, `PATTERN`, `ON_ERROR`, `SPLIT_SIZE`, `VALIDATION_MODE`, `.`, `(`, `MAX_FILES`, or `;` + | ---- ^^ expected , `AtString`, or `(` + | | + | while parsing `COPY + INTO { [.] { ( ) } } + FROM { internalStage | externalStage | externalLocation | ( ) } + [ FILE_FORMAT = ( { TYPE = { CSV | JSON | PARQUET | TSV } [ formatTypeOptions ] } ) ] + [ FILES = ( '' [ , '' ] [ , ... ] ) ] + [ PATTERN = '' ] + [ VALIDATION_MODE = RETURN_ROWS ] + [ copyOptions ]` ---------- Input ---------- copy into t1 from "" FILE_FORMAT ---------- Output --------- error: - --> SQL:1:33 + --> SQL:1:19 | 1 | copy into t1 from "" FILE_FORMAT - | ---- ^ expected `=` - | | + | ---- ^^ expected , `AtString`, or `(` + | | | while parsing `COPY - INTO { internalStage | externalStage | externalLocation | [.] } - FROM { internalStage | externalStage | externalLocation | [.] | ( ) } - [ FILE_FORMAT = ( { TYPE = { CSV | JSON | PARQUET } [ formatTypeOptions ] } ) ] + INTO { [.] { ( ) } } + FROM { internalStage | externalStage | externalLocation | ( ) } + [ FILE_FORMAT = ( { TYPE = { CSV | JSON | PARQUET | TSV } [ formatTypeOptions ] } ) ] [ FILES = ( '' [ , '' ] [ , ... ] ) ] [ PATTERN = '' ] [ VALIDATION_MODE = RETURN_ROWS ] @@ -529,15 +538,15 @@ error: copy into t1 from "" FILE_FORMAT = ---------- Output --------- error: - --> SQL:1:36 + --> SQL:1:19 | 1 | copy into t1 from "" FILE_FORMAT = - | ---- ^ expected `(` - | | + | ---- ^^ expected , `AtString`, or `(` + | | | while parsing `COPY - INTO { internalStage | externalStage | externalLocation | [.] } - FROM { internalStage | externalStage | externalLocation | [.] | ( ) } - [ FILE_FORMAT = ( { TYPE = { CSV | JSON | PARQUET } [ formatTypeOptions ] } ) ] + INTO { [.] { ( ) } } + FROM { internalStage | externalStage | externalLocation | ( ) } + [ FILE_FORMAT = ( { TYPE = { CSV | JSON | PARQUET | TSV } [ formatTypeOptions ] } ) ] [ FILES = ( '' [ , '' ] [ , ... ] ) ] [ PATTERN = '' ] [ VALIDATION_MODE = RETURN_ROWS ] @@ -548,15 +557,15 @@ error: copy into t1 from "" FILE_FORMAT = ( ---------- Output --------- error: - --> SQL:1:37 + --> SQL:1:19 | 1 | copy into t1 from "" FILE_FORMAT = ( - | ---- ^ expected `)`, `TYPE`, `COMPRESSION`, `FORMAT_NAME`, `RECORD_DELIMITER`, `FIELD_DELIMITER`, `QUOTE`, `NAN_DISPLAY`, `NULL_DISPLAY`, `ESCAPE`, `ROW_TAG`, `SKIP_HEADER`, `ERROR_ON_COLUMN_COUNT_MISMATCH`, or `NON_DISPLAY` - | | + | ---- ^^ expected , `AtString`, or `(` + | | | while parsing `COPY - INTO { internalStage | externalStage | externalLocation | [.] } - FROM { internalStage | externalStage | externalLocation | [.] | ( ) } - [ FILE_FORMAT = ( { TYPE = { CSV | JSON | PARQUET } [ formatTypeOptions ] } ) ] + INTO { [.] { ( ) } } + FROM { internalStage | externalStage | externalLocation | ( ) } + [ FILE_FORMAT = ( { TYPE = { CSV | JSON | PARQUET | TSV } [ formatTypeOptions ] } ) ] [ FILES = ( '' [ , '' ] [ , ... ] ) ] [ PATTERN = '' ] [ VALIDATION_MODE = RETURN_ROWS ] @@ -567,15 +576,15 @@ error: copy into t1 from "" FILE_FORMAT = (TYPE ---------- Output --------- error: - --> SQL:1:41 + --> SQL:1:19 | 1 | copy into t1 from "" FILE_FORMAT = (TYPE - | ---- ^ expected `=` - | | + | ---- ^^ expected , `AtString`, or `(` + | | | while parsing `COPY - INTO { internalStage | externalStage | externalLocation | [.] } - FROM { internalStage | externalStage | externalLocation | [.] | ( ) } - [ FILE_FORMAT = ( { TYPE = { CSV | JSON | PARQUET } [ formatTypeOptions ] } ) ] + INTO { [.] { ( ) } } + FROM { internalStage | externalStage | externalLocation | ( ) } + [ FILE_FORMAT = ( { TYPE = { CSV | JSON | PARQUET | TSV } [ formatTypeOptions ] } ) ] [ FILES = ( '' [ , '' ] [ , ... ] ) ] [ PATTERN = '' ] [ VALIDATION_MODE = RETURN_ROWS ] @@ -586,15 +595,15 @@ error: copy into t1 from "" FILE_FORMAT = (TYPE = ---------- Output --------- error: - --> SQL:1:43 + --> SQL:1:19 | 1 | copy into t1 from "" FILE_FORMAT = (TYPE = - | ---- ^ expected , `TSV`, `CSV`, `NDJSON`, `PARQUET`, `JSON`, or `XML` - | | + | ---- ^^ expected , `AtString`, or `(` + | | | while parsing `COPY - INTO { internalStage | externalStage | externalLocation | [.] } - FROM { internalStage | externalStage | externalLocation | [.] | ( ) } - [ FILE_FORMAT = ( { TYPE = { CSV | JSON | PARQUET } [ formatTypeOptions ] } ) ] + INTO { [.] { ( ) } } + FROM { internalStage | externalStage | externalLocation | ( ) } + [ FILE_FORMAT = ( { TYPE = { CSV | JSON | PARQUET | TSV } [ formatTypeOptions ] } ) ] [ FILES = ( '' [ , '' ] [ , ... ] ) ] [ PATTERN = '' ] [ VALIDATION_MODE = RETURN_ROWS ] @@ -605,15 +614,15 @@ error: copy into t1 from "" FILE_FORMAT = (TYPE = ---------- Output --------- error: - --> SQL:1:43 + --> SQL:1:19 | 1 | copy into t1 from "" FILE_FORMAT = (TYPE = - | ---- ^ expected , `TSV`, `CSV`, `NDJSON`, `PARQUET`, `JSON`, or `XML` - | | + | ---- ^^ expected , `AtString`, or `(` + | | | while parsing `COPY - INTO { internalStage | externalStage | externalLocation | [.] } - FROM { internalStage | externalStage | externalLocation | [.] | ( ) } - [ FILE_FORMAT = ( { TYPE = { CSV | JSON | PARQUET } [ formatTypeOptions ] } ) ] + INTO { [.] { ( ) } } + FROM { internalStage | externalStage | externalLocation | ( ) } + [ FILE_FORMAT = ( { TYPE = { CSV | JSON | PARQUET | TSV } [ formatTypeOptions ] } ) ] [ FILES = ( '' [ , '' ] [ , ... ] ) ] [ PATTERN = '' ] [ VALIDATION_MODE = RETURN_ROWS ] @@ -624,15 +633,15 @@ error: COPY INTO t1 FROM "" PATTERN = '.*[.]csv' FILE_FORMAT = (type = TSV field_delimiter = '\t' skip_headerx = 0); ---------- Output --------- error: - --> SQL:1:92 + --> SQL:1:19 | 1 | COPY INTO t1 FROM "" PATTERN = '.*[.]csv' FILE_FORMAT = (type = TSV field_delimiter = '\t' skip_headerx = 0); - | ---- ^^^^^^^^^^^^ expected `SKIP_HEADER`, `FIELD_DELIMITER`, `ESCAPE`, `NON_DISPLAY`, `NULL_DISPLAY`, `TYPE`, `NAN_DISPLAY`, `ROW_TAG`, `RECORD_DELIMITER`, `QUOTE`, `COMPRESSION`, `FORMAT_NAME`, `ERROR_ON_COLUMN_COUNT_MISMATCH`, `)`, or `,` - | | + | ---- ^^ expected , `AtString`, or `(` + | | | while parsing `COPY - INTO { internalStage | externalStage | externalLocation | [.] } - FROM { internalStage | externalStage | externalLocation | [.] | ( ) } - [ FILE_FORMAT = ( { TYPE = { CSV | JSON | PARQUET } [ formatTypeOptions ] } ) ] + INTO { [.] { ( ) } } + FROM { internalStage | externalStage | externalLocation | ( ) } + [ FILE_FORMAT = ( { TYPE = { CSV | JSON | PARQUET | TSV } [ formatTypeOptions ] } ) ] [ FILES = ( '' [ , '' ] [ , ... ] ) ] [ PATTERN = '' ] [ VALIDATION_MODE = RETURN_ROWS ] @@ -652,9 +661,9 @@ error: | 1 | COPY INTO mytable | ---- while parsing `COPY - INTO { internalStage | externalStage | externalLocation | [.] } - FROM { internalStage | externalStage | externalLocation | [.] | ( ) } - [ FILE_FORMAT = ( { TYPE = { CSV | JSON | PARQUET } [ formatTypeOptions ] } ) ] + INTO { [.] { ( ) } } + FROM { internalStage | externalStage | externalLocation | ( ) } + [ FILE_FORMAT = ( { TYPE = { CSV | JSON | PARQUET | TSV } [ formatTypeOptions ] } ) ] [ FILES = ( '' [ , '' ] [ , ... ] ) ] [ PATTERN = '' ] [ VALIDATION_MODE = RETURN_ROWS ] diff --git a/src/query/ast/tests/it/testdata/statement.txt b/src/query/ast/tests/it/testdata/statement.txt index e69f4b2024de..f456112a13c2 100644 --- a/src/query/ast/tests/it/testdata/statement.txt +++ b/src/query/ast/tests/it/testdata/statement.txt @@ -9179,17 +9179,16 @@ COPY INTO mytable FROM '@~/mybucket/my data.csv' size_limit=10; ---------- Output --------- -COPY INTO mytable FROM @~/mybucket/my data.csv SIZE_LIMIT = 10 SINGLE = false PURGE = false FORCE = false DISABLE_VARIANT_CHECK = false ON_ERROR = 'abort' +COPY INTO mytable FROM @~/mybucket/my data.csv SIZE_LIMIT = 10 PURGE = false FORCE = false DISABLE_VARIANT_CHECK = false ON_ERROR = 'abort' ---------- AST ------------ -Copy( - CopyStmt { - hints: None, +CopyIntoTable( + CopyIntoTableStmt { src: Location( Stage( "~/mybucket/my data.csv", ), ), - dst: Table { + dst: TableIdentifier { catalog: None, database: None, table: Identifier { @@ -9199,19 +9198,18 @@ Copy( 10..17, ), }, - columns: None, }, + dst_columns: None, + hints: None, + file_format: {}, files: None, pattern: None, - file_format: {}, + force: false, validation_mode: "", size_limit: 10, max_files: 0, - max_file_size: 0, split_size: 0, - single: false, purge: false, - force: false, disable_variant_check: false, return_failed_only: false, on_error: "abort", @@ -9231,17 +9229,16 @@ COPY INTO mytable size_limit=10; ---------- Output --------- COPY INTO mytable FROM @~/mybucket/data.csv FILE_FORMAT = (field_delimiter=',', record_delimiter=' -', skip_header='1', type='CSV') SIZE_LIMIT = 10 SINGLE = false PURGE = false FORCE = false DISABLE_VARIANT_CHECK = false ON_ERROR = 'abort' +', skip_header='1', type='CSV') SIZE_LIMIT = 10 PURGE = false FORCE = false DISABLE_VARIANT_CHECK = false ON_ERROR = 'abort' ---------- AST ------------ -Copy( - CopyStmt { - hints: None, +CopyIntoTable( + CopyIntoTableStmt { src: Location( Stage( "~/mybucket/data.csv", ), ), - dst: Table { + dst: TableIdentifier { catalog: None, database: None, table: Identifier { @@ -9251,24 +9248,23 @@ Copy( 10..17, ), }, - columns: None, }, - files: None, - pattern: None, + dst_columns: None, + hints: None, file_format: { "field_delimiter": ",", "record_delimiter": "\n", "skip_header": "1", "type": "CSV", }, + files: None, + pattern: None, + force: false, validation_mode: "", size_limit: 10, max_files: 0, - max_file_size: 0, split_size: 0, - single: false, purge: false, - force: false, disable_variant_check: false, return_failed_only: false, on_error: "abort", @@ -9289,11 +9285,10 @@ COPY INTO mytable max_files=10; ---------- Output --------- COPY INTO mytable FROM 's3://mybucket/data.csv' FILE_FORMAT = (field_delimiter=',', record_delimiter=' -', skip_header='1', type='CSV') SIZE_LIMIT = 10 MAX_FILES = 10 SINGLE = false PURGE = false FORCE = false DISABLE_VARIANT_CHECK = false ON_ERROR = 'abort' +', skip_header='1', type='CSV') SIZE_LIMIT = 10 MAX_FILES = 10 PURGE = false FORCE = false DISABLE_VARIANT_CHECK = false ON_ERROR = 'abort' ---------- AST ------------ -Copy( - CopyStmt { - hints: None, +CopyIntoTable( + CopyIntoTableStmt { src: Location( Uri( UriLocation { @@ -9308,7 +9303,7 @@ Copy( }, ), ), - dst: Table { + dst: TableIdentifier { catalog: None, database: None, table: Identifier { @@ -9318,24 +9313,23 @@ Copy( 10..17, ), }, - columns: None, }, - files: None, - pattern: None, + dst_columns: None, + hints: None, file_format: { "field_delimiter": ",", "record_delimiter": "\n", "skip_header": "1", "type": "CSV", }, + files: None, + pattern: None, + force: false, validation_mode: "", size_limit: 10, max_files: 10, - max_file_size: 0, split_size: 0, - single: false, purge: false, - force: false, disable_variant_check: false, return_failed_only: false, on_error: "abort", @@ -9356,11 +9350,10 @@ COPY INTO mytable max_files=3000; ---------- Output --------- COPY INTO mytable FROM 's3://mybucket/data.csv' FILE_FORMAT = (field_delimiter=',', record_delimiter=' -', skip_header='1', type='CSV') SIZE_LIMIT = 10 MAX_FILES = 2000 SINGLE = false PURGE = false FORCE = false DISABLE_VARIANT_CHECK = false ON_ERROR = 'abort' +', skip_header='1', type='CSV') SIZE_LIMIT = 10 MAX_FILES = 2000 PURGE = false FORCE = false DISABLE_VARIANT_CHECK = false ON_ERROR = 'abort' ---------- AST ------------ -Copy( - CopyStmt { - hints: None, +CopyIntoTable( + CopyIntoTableStmt { src: Location( Uri( UriLocation { @@ -9375,7 +9368,7 @@ Copy( }, ), ), - dst: Table { + dst: TableIdentifier { catalog: None, database: None, table: Identifier { @@ -9385,24 +9378,23 @@ Copy( 10..17, ), }, - columns: None, }, - files: None, - pattern: None, + dst_columns: None, + hints: None, file_format: { "field_delimiter": ",", "record_delimiter": "\n", "skip_header": "1", "type": "CSV", }, + files: None, + pattern: None, + force: false, validation_mode: "", size_limit: 10, max_files: 2000, - max_file_size: 0, split_size: 0, - single: false, purge: false, - force: false, disable_variant_check: false, return_failed_only: false, on_error: "abort", @@ -9425,11 +9417,10 @@ COPY INTO mytable size_limit=10; ---------- Output --------- COPY INTO mytable FROM 's3://mybucket/data.csv' CONNECTION = ( endpoint_url='http://127.0.0.1:9900' ) FILE_FORMAT = (field_delimiter=',', record_delimiter=' -', skip_header='1', type='CSV') SIZE_LIMIT = 10 SINGLE = false PURGE = false FORCE = false DISABLE_VARIANT_CHECK = false ON_ERROR = 'abort' +', skip_header='1', type='CSV') SIZE_LIMIT = 10 PURGE = false FORCE = false DISABLE_VARIANT_CHECK = false ON_ERROR = 'abort' ---------- AST ------------ -Copy( - CopyStmt { - hints: None, +CopyIntoTable( + CopyIntoTableStmt { src: Location( Uri( UriLocation { @@ -9446,7 +9437,7 @@ Copy( }, ), ), - dst: Table { + dst: TableIdentifier { catalog: None, database: None, table: Identifier { @@ -9456,24 +9447,23 @@ Copy( 10..17, ), }, - columns: None, }, - files: None, - pattern: None, + dst_columns: None, + hints: None, file_format: { "field_delimiter": ",", "record_delimiter": "\n", "skip_header": "1", "type": "CSV", }, + files: None, + pattern: None, + force: false, validation_mode: "", size_limit: 10, max_files: 0, - max_file_size: 0, split_size: 0, - single: false, purge: false, - force: false, disable_variant_check: false, return_failed_only: false, on_error: "abort", @@ -9496,11 +9486,10 @@ COPY INTO mytable ); ---------- Output --------- COPY INTO mytable FROM 's3://mybucket/data.csv' CONNECTION = ( endpoint_url='http://127.0.0.1:9900' ) FILE_FORMAT = (field_delimiter=',', record_delimiter=' -', skip_header='1', type='CSV') SIZE_LIMIT = 10 SINGLE = false PURGE = false FORCE = false DISABLE_VARIANT_CHECK = false ON_ERROR = 'abort' +', skip_header='1', type='CSV') SIZE_LIMIT = 10 PURGE = false FORCE = false DISABLE_VARIANT_CHECK = false ON_ERROR = 'abort' ---------- AST ------------ -Copy( - CopyStmt { - hints: None, +CopyIntoTable( + CopyIntoTableStmt { src: Location( Uri( UriLocation { @@ -9517,7 +9506,7 @@ Copy( }, ), ), - dst: Table { + dst: TableIdentifier { catalog: None, database: None, table: Identifier { @@ -9527,24 +9516,23 @@ Copy( 10..17, ), }, - columns: None, }, - files: None, - pattern: None, + dst_columns: None, + hints: None, file_format: { "field_delimiter": ",", "record_delimiter": "\n", "skip_header": "1", "type": "CSV", }, + files: None, + pattern: None, + force: false, validation_mode: "", size_limit: 10, max_files: 0, - max_file_size: 0, split_size: 0, - single: false, purge: false, - force: false, disable_variant_check: false, return_failed_only: false, on_error: "abort", @@ -9556,11 +9544,10 @@ Copy( COPY INTO mytable FROM 'https://127.0.0.1:9900'; ---------- Output --------- -COPY INTO mytable FROM 'https://127.0.0.1:9900/' SINGLE = false PURGE = false FORCE = false DISABLE_VARIANT_CHECK = false ON_ERROR = 'abort' +COPY INTO mytable FROM 'https://127.0.0.1:9900/' PURGE = false FORCE = false DISABLE_VARIANT_CHECK = false ON_ERROR = 'abort' ---------- AST ------------ -Copy( - CopyStmt { - hints: None, +CopyIntoTable( + CopyIntoTableStmt { src: Location( Uri( UriLocation { @@ -9575,7 +9562,7 @@ Copy( }, ), ), - dst: Table { + dst: TableIdentifier { catalog: None, database: None, table: Identifier { @@ -9585,19 +9572,18 @@ Copy( 10..17, ), }, - columns: None, }, + dst_columns: None, + hints: None, + file_format: {}, files: None, pattern: None, - file_format: {}, + force: false, validation_mode: "", size_limit: 0, max_files: 0, - max_file_size: 0, split_size: 0, - single: false, purge: false, - force: false, disable_variant_check: false, return_failed_only: false, on_error: "abort", @@ -9609,11 +9595,10 @@ Copy( COPY INTO mytable FROM 'https://127.0.0.1:'; ---------- Output --------- -COPY INTO mytable FROM 'https://127.0.0.1/' SINGLE = false PURGE = false FORCE = false DISABLE_VARIANT_CHECK = false ON_ERROR = 'abort' +COPY INTO mytable FROM 'https://127.0.0.1/' PURGE = false FORCE = false DISABLE_VARIANT_CHECK = false ON_ERROR = 'abort' ---------- AST ------------ -Copy( - CopyStmt { - hints: None, +CopyIntoTable( + CopyIntoTableStmt { src: Location( Uri( UriLocation { @@ -9628,7 +9613,7 @@ Copy( }, ), ), - dst: Table { + dst: TableIdentifier { catalog: None, database: None, table: Identifier { @@ -9638,19 +9623,18 @@ Copy( 10..17, ), }, - columns: None, }, + dst_columns: None, + hints: None, + file_format: {}, files: None, pattern: None, - file_format: {}, + force: false, validation_mode: "", size_limit: 0, max_files: 0, - max_file_size: 0, split_size: 0, - single: false, purge: false, - force: false, disable_variant_check: false, return_failed_only: false, on_error: "abort", @@ -9671,17 +9655,16 @@ COPY INTO mytable size_limit=10; ---------- Output --------- COPY INTO mytable FROM @my_stage FILE_FORMAT = (error_on_column_count_mismatch='false', field_delimiter=',', record_delimiter=' -', skip_header='1', type='CSV') SIZE_LIMIT = 10 SINGLE = false PURGE = false FORCE = false DISABLE_VARIANT_CHECK = false ON_ERROR = 'abort' +', skip_header='1', type='CSV') SIZE_LIMIT = 10 PURGE = false FORCE = false DISABLE_VARIANT_CHECK = false ON_ERROR = 'abort' ---------- AST ------------ -Copy( - CopyStmt { - hints: None, +CopyIntoTable( + CopyIntoTableStmt { src: Location( Stage( "my_stage", ), ), - dst: Table { + dst: TableIdentifier { catalog: None, database: None, table: Identifier { @@ -9691,10 +9674,9 @@ Copy( 10..17, ), }, - columns: None, }, - files: None, - pattern: None, + dst_columns: None, + hints: None, file_format: { "error_on_column_count_mismatch": "false", "field_delimiter": ",", @@ -9702,14 +9684,14 @@ Copy( "skip_header": "1", "type": "CSV", }, + files: None, + pattern: None, + force: false, validation_mode: "", size_limit: 10, max_files: 0, - max_file_size: 0, split_size: 0, - single: false, purge: false, - force: false, disable_variant_check: false, return_failed_only: false, on_error: "abort", @@ -9726,104 +9708,78 @@ COPY INTO 's3://mybucket/data.csv' record_delimiter = '\n' skip_header = 1 ) - size_limit=10; ---------- Output --------- COPY INTO 's3://mybucket/data.csv' FROM mytable FILE_FORMAT = (field_delimiter=',', record_delimiter=' -', skip_header='1', type='CSV') SIZE_LIMIT = 10 SINGLE = false PURGE = false FORCE = false DISABLE_VARIANT_CHECK = false ON_ERROR = 'abort' +', skip_header='1', type='CSV') SINGLE = false MAX_FILE_SIZE= 0 ---------- AST ------------ -Copy( - CopyStmt { +CopyIntoLocation( + CopyIntoLocationStmt { hints: None, - src: Table { - catalog: None, - database: None, - table: Identifier { - name: "mytable", - quote: None, - span: Some( - 56..63, - ), + src: Table( + TableIdentifier { + catalog: None, + database: None, + table: Identifier { + name: "mytable", + quote: None, + span: Some( + 56..63, + ), + }, }, - columns: None, - }, - dst: Location( - Uri( - UriLocation { - protocol: "s3", - name: "mybucket", - path: "/data.csv", - part_prefix: "", - connection: Connection { - visited_keys: {}, - conns: {}, - }, + ), + dst: Uri( + UriLocation { + protocol: "s3", + name: "mybucket", + path: "/data.csv", + part_prefix: "", + connection: Connection { + visited_keys: {}, + conns: {}, }, - ), + }, ), - files: None, - pattern: None, file_format: { "field_delimiter": ",", "record_delimiter": "\n", "skip_header": "1", "type": "CSV", }, - validation_mode: "", - size_limit: 10, - max_files: 0, - max_file_size: 0, - split_size: 0, single: false, - purge: false, - force: false, - disable_variant_check: false, - return_failed_only: false, - on_error: "abort", + max_file_size: 0, }, ) ---------- Input ---------- COPY INTO '@my_stage/my data' - FROM mytable - size_limit=10; + FROM mytable; ---------- Output --------- -COPY INTO @my_stage/my data FROM mytable SIZE_LIMIT = 10 SINGLE = false PURGE = false FORCE = false DISABLE_VARIANT_CHECK = false ON_ERROR = 'abort' +COPY INTO @my_stage/my data FROM mytable SINGLE = false MAX_FILE_SIZE= 0 ---------- AST ------------ -Copy( - CopyStmt { +CopyIntoLocation( + CopyIntoLocationStmt { hints: None, - src: Table { - catalog: None, - database: None, - table: Identifier { - name: "mytable", - quote: None, - span: Some( - 51..58, - ), + src: Table( + TableIdentifier { + catalog: None, + database: None, + table: Identifier { + name: "mytable", + quote: None, + span: Some( + 51..58, + ), + }, }, - columns: None, - }, - dst: Location( - Stage( - "my_stage/my data", - ), ), - files: None, - pattern: None, + dst: Stage( + "my_stage/my data", + ), file_format: {}, - validation_mode: "", - size_limit: 10, - max_files: 0, - max_file_size: 0, - split_size: 0, single: false, - purge: false, - force: false, - disable_variant_check: false, - return_failed_only: false, - on_error: "abort", + max_file_size: 0, }, ) @@ -9836,51 +9792,38 @@ COPY INTO @my_stage field_delimiter = ',' record_delimiter = '\n' skip_header = 1 - ) - size_limit=10; + ); ---------- Output --------- COPY INTO @my_stage FROM mytable FILE_FORMAT = (field_delimiter=',', record_delimiter=' -', skip_header='1', type='CSV') SIZE_LIMIT = 10 SINGLE = false PURGE = false FORCE = false DISABLE_VARIANT_CHECK = false ON_ERROR = 'abort' +', skip_header='1', type='CSV') SINGLE = false MAX_FILE_SIZE= 0 ---------- AST ------------ -Copy( - CopyStmt { +CopyIntoLocation( + CopyIntoLocationStmt { hints: None, - src: Table { - catalog: None, - database: None, - table: Identifier { - name: "mytable", - quote: None, - span: Some( - 41..48, - ), + src: Table( + TableIdentifier { + catalog: None, + database: None, + table: Identifier { + name: "mytable", + quote: None, + span: Some( + 41..48, + ), + }, }, - columns: None, - }, - dst: Location( - Stage( - "my_stage", - ), ), - files: None, - pattern: None, + dst: Stage( + "my_stage", + ), file_format: { "field_delimiter": ",", "record_delimiter": "\n", "skip_header": "1", "type": "CSV", }, - validation_mode: "", - size_limit: 10, - max_files: 0, - max_file_size: 0, - split_size: 0, single: false, - purge: false, - force: false, - disable_variant_check: false, - return_failed_only: false, - on_error: "abort", + max_file_size: 0, }, ) @@ -9901,11 +9844,10 @@ COPY INTO mytable size_limit=10; ---------- Output --------- COPY INTO mytable FROM 's3://mybucket/data.csv' CONNECTION = ( aws_key_id='access_key', aws_secret_key='secret_key' ) FILE_FORMAT = (field_delimiter=',', record_delimiter=' -', skip_header='1', type='CSV') SIZE_LIMIT = 10 SINGLE = false PURGE = false FORCE = false DISABLE_VARIANT_CHECK = false ON_ERROR = 'abort' +', skip_header='1', type='CSV') SIZE_LIMIT = 10 PURGE = false FORCE = false DISABLE_VARIANT_CHECK = false ON_ERROR = 'abort' ---------- AST ------------ -Copy( - CopyStmt { - hints: None, +CopyIntoTable( + CopyIntoTableStmt { src: Location( Uri( UriLocation { @@ -9923,7 +9865,7 @@ Copy( }, ), ), - dst: Table { + dst: TableIdentifier { catalog: None, database: None, table: Identifier { @@ -9933,24 +9875,23 @@ Copy( 10..17, ), }, - columns: None, }, - files: None, - pattern: None, + dst_columns: None, + hints: None, file_format: { "field_delimiter": ",", "record_delimiter": "\n", "skip_header": "1", "type": "CSV", }, + files: None, + pattern: None, + force: false, validation_mode: "", size_limit: 10, max_files: 0, - max_file_size: 0, split_size: 0, - single: false, purge: false, - force: false, disable_variant_check: false, return_failed_only: false, on_error: "abort", @@ -9970,17 +9911,16 @@ COPY INTO mytable size_limit=10; ---------- Output --------- COPY INTO mytable FROM @external_stage/path/to/file.csv FILE_FORMAT = (field_delimiter=',', record_delimiter=' -', skip_header='1', type='CSV') SIZE_LIMIT = 10 SINGLE = false PURGE = false FORCE = false DISABLE_VARIANT_CHECK = false ON_ERROR = 'abort' +', skip_header='1', type='CSV') SIZE_LIMIT = 10 PURGE = false FORCE = false DISABLE_VARIANT_CHECK = false ON_ERROR = 'abort' ---------- AST ------------ -Copy( - CopyStmt { - hints: None, +CopyIntoTable( + CopyIntoTableStmt { src: Location( Stage( "external_stage/path/to/file.csv", ), ), - dst: Table { + dst: TableIdentifier { catalog: None, database: None, table: Identifier { @@ -9990,24 +9930,23 @@ Copy( 10..17, ), }, - columns: None, }, - files: None, - pattern: None, + dst_columns: None, + hints: None, file_format: { "field_delimiter": ",", "record_delimiter": "\n", "skip_header": "1", "type": "CSV", }, + files: None, + pattern: None, + force: false, validation_mode: "", size_limit: 10, max_files: 0, - max_file_size: 0, split_size: 0, - single: false, purge: false, - force: false, disable_variant_check: false, return_failed_only: false, on_error: "abort", @@ -10027,17 +9966,16 @@ COPY INTO mytable size_limit=10; ---------- Output --------- COPY INTO mytable FROM @external_stage/path/to/dir/ FILE_FORMAT = (field_delimiter=',', record_delimiter=' -', skip_header='1', type='CSV') SIZE_LIMIT = 10 SINGLE = false PURGE = false FORCE = false DISABLE_VARIANT_CHECK = false ON_ERROR = 'abort' +', skip_header='1', type='CSV') SIZE_LIMIT = 10 PURGE = false FORCE = false DISABLE_VARIANT_CHECK = false ON_ERROR = 'abort' ---------- AST ------------ -Copy( - CopyStmt { - hints: None, +CopyIntoTable( + CopyIntoTableStmt { src: Location( Stage( "external_stage/path/to/dir/", ), ), - dst: Table { + dst: TableIdentifier { catalog: None, database: None, table: Identifier { @@ -10047,24 +9985,23 @@ Copy( 10..17, ), }, - columns: None, }, - files: None, - pattern: None, + dst_columns: None, + hints: None, file_format: { "field_delimiter": ",", "record_delimiter": "\n", "skip_header": "1", "type": "CSV", }, + files: None, + pattern: None, + force: false, validation_mode: "", size_limit: 10, max_files: 0, - max_file_size: 0, split_size: 0, - single: false, purge: false, - force: false, disable_variant_check: false, return_failed_only: false, on_error: "abort", @@ -10084,17 +10021,16 @@ COPY INTO mytable force=true; ---------- Output --------- COPY INTO mytable FROM @external_stage/path/to/file.csv FILE_FORMAT = (field_delimiter=',', record_delimiter=' -', skip_header='1', type='CSV') SINGLE = false PURGE = false FORCE = true DISABLE_VARIANT_CHECK = false ON_ERROR = 'abort' +', skip_header='1', type='CSV') PURGE = false FORCE = true DISABLE_VARIANT_CHECK = false ON_ERROR = 'abort' ---------- AST ------------ -Copy( - CopyStmt { - hints: None, +CopyIntoTable( + CopyIntoTableStmt { src: Location( Stage( "external_stage/path/to/file.csv", ), ), - dst: Table { + dst: TableIdentifier { catalog: None, database: None, table: Identifier { @@ -10104,24 +10040,23 @@ Copy( 10..17, ), }, - columns: None, }, - files: None, - pattern: None, + dst_columns: None, + hints: None, file_format: { "field_delimiter": ",", "record_delimiter": "\n", "skip_header": "1", "type": "CSV", }, + files: None, + pattern: None, + force: true, validation_mode: "", size_limit: 0, max_files: 0, - max_file_size: 0, split_size: 0, - single: false, purge: false, - force: true, disable_variant_check: false, return_failed_only: false, on_error: "abort", @@ -10142,11 +10077,10 @@ COPY INTO mytable disable_variant_check=true; ---------- Output --------- COPY INTO mytable FROM 'fs:///path/to/data.csv' FILE_FORMAT = (field_delimiter=',', record_delimiter=' -', skip_header='1', type='CSV') SIZE_LIMIT = 10 SINGLE = false PURGE = false FORCE = false DISABLE_VARIANT_CHECK = true ON_ERROR = 'abort' +', skip_header='1', type='CSV') SIZE_LIMIT = 10 PURGE = false FORCE = false DISABLE_VARIANT_CHECK = true ON_ERROR = 'abort' ---------- AST ------------ -Copy( - CopyStmt { - hints: None, +CopyIntoTable( + CopyIntoTableStmt { src: Location( Uri( UriLocation { @@ -10161,7 +10095,7 @@ Copy( }, ), ), - dst: Table { + dst: TableIdentifier { catalog: None, database: None, table: Identifier { @@ -10171,80 +10105,24 @@ Copy( 10..17, ), }, - columns: None, }, - files: None, - pattern: None, + dst_columns: None, + hints: None, file_format: { "field_delimiter": ",", "record_delimiter": "\n", "skip_header": "1", "type": "CSV", }, - validation_mode: "", - size_limit: 10, - max_files: 0, - max_file_size: 0, - split_size: 0, - single: false, - purge: false, - force: false, - disable_variant_check: true, - return_failed_only: false, - on_error: "abort", - }, -) - - ----------- Input ---------- -copy into t1 from "" FILE_FORMAT = (TYPE = TSV, COMPRESSION = GZIP) ----------- Output --------- -COPY INTO t1 FROM "" FILE_FORMAT = (compression='GZIP', type='TSV') SINGLE = false PURGE = false FORCE = false DISABLE_VARIANT_CHECK = false ON_ERROR = 'abort' ----------- AST ------------ -Copy( - CopyStmt { - hints: None, - src: Table { - catalog: None, - database: None, - table: Identifier { - name: "", - quote: Some( - '"', - ), - span: Some( - 18..20, - ), - }, - columns: None, - }, - dst: Table { - catalog: None, - database: None, - table: Identifier { - name: "t1", - quote: None, - span: Some( - 10..12, - ), - }, - columns: None, - }, files: None, pattern: None, - file_format: { - "compression": "GZIP", - "type": "TSV", - }, + force: false, validation_mode: "", - size_limit: 0, + size_limit: 10, max_files: 0, - max_file_size: 0, split_size: 0, - single: false, purge: false, - force: false, - disable_variant_check: false, + disable_variant_check: true, return_failed_only: false, on_error: "abort", }, @@ -10252,7 +10130,7 @@ Copy( ---------- Input ---------- -COPY INTO books FROM 's3://databend/books.csv' +COPY INTO books FROM 's3://databend/books.csv' CONNECTION = ( ENDPOINT_URL = 'http://localhost:9000/', ACCESS_KEY_ID = 'ROOTUSER', @@ -10261,11 +10139,10 @@ COPY INTO books FROM 's3://databend/books.csv' ) FILE_FORMAT = (type = CSV); ---------- Output --------- -COPY INTO books FROM 's3://databend/books.csv' CONNECTION = ( access_key_id='ROOTUSER', endpoint_url='http://localhost:9000/', region='us-west-2', secret_access_key='CHANGEME123' ) FILE_FORMAT = (type='CSV') SINGLE = false PURGE = false FORCE = false DISABLE_VARIANT_CHECK = false ON_ERROR = 'abort' +COPY INTO books FROM 's3://databend/books.csv' CONNECTION = ( access_key_id='ROOTUSER', endpoint_url='http://localhost:9000/', region='us-west-2', secret_access_key='CHANGEME123' ) FILE_FORMAT = (type='CSV') PURGE = false FORCE = false DISABLE_VARIANT_CHECK = false ON_ERROR = 'abort' ---------- AST ------------ -Copy( - CopyStmt { - hints: None, +CopyIntoTable( + CopyIntoTableStmt { src: Location( Uri( UriLocation { @@ -10285,7 +10162,7 @@ Copy( }, ), ), - dst: Table { + dst: TableIdentifier { catalog: None, database: None, table: Identifier { @@ -10295,21 +10172,20 @@ Copy( 10..15, ), }, - columns: None, }, - files: None, - pattern: None, + dst_columns: None, + hints: None, file_format: { "type": "CSV", }, + files: None, + pattern: None, + force: false, validation_mode: "", size_limit: 0, max_files: 0, - max_file_size: 0, split_size: 0, - single: false, purge: false, - force: false, disable_variant_check: false, return_failed_only: false, on_error: "abort", diff --git a/src/query/catalog/src/query_kind.rs b/src/query/catalog/src/query_kind.rs index 04009198b0e7..b2e27cd799d5 100644 --- a/src/query/catalog/src/query_kind.rs +++ b/src/query/catalog/src/query_kind.rs @@ -23,7 +23,7 @@ pub enum QueryKind { Unknown, Query, Explain, - Copy, + CopyIntoTable, Update, Insert, Other, diff --git a/src/query/service/src/interpreters/access/privilege_access.rs b/src/query/service/src/interpreters/access/privilege_access.rs index 5eb5346b5a8f..a16bd2f4168c 100644 --- a/src/query/service/src/interpreters/access/privilege_access.rs +++ b/src/query/service/src/interpreters/access/privilege_access.rs @@ -20,7 +20,6 @@ use common_exception::Result; use common_meta_app::principal::GrantObject; use common_meta_app::principal::UserGrantSet; use common_meta_app::principal::UserPrivilegeType; -use common_sql::plans::CopyPlan; use common_sql::plans::RewriteKind; use common_users::RoleCacheManager; @@ -601,31 +600,24 @@ impl AccessChecker for PrivilegeAccess { .validate_privilege(&GrantObject::Global, vec![UserPrivilegeType::Alter], false) .await?; } - Plan::Copy(plan) => match plan.as_ref() { - CopyPlan::IntoTable(plan) => { - session - .validate_privilege( - &GrantObject::Table( - plan.catalog_info.catalog_name().to_string(), - plan.database_name.to_string(), - plan.table_name.to_string(), - ), - vec![UserPrivilegeType::Insert], - true, - ) - .await?; - } - CopyPlan::IntoStage { .. } => { - session - .validate_privilege( - &GrantObject::Global, - vec![UserPrivilegeType::Super], - false, - ) - .await?; - } - CopyPlan::NoFileToCopy => {} - }, + Plan::CopyIntoTable(plan) => { + session + .validate_privilege( + &GrantObject::Table( + plan.catalog_info.catalog_name().to_string(), + plan.database_name.to_string(), + plan.table_name.to_string(), + ), + vec![UserPrivilegeType::Insert], + true, + ) + .await?; + } + Plan::CopyIntoLocation(_plan) => { + session + .validate_privilege(&GrantObject::Global, vec![UserPrivilegeType::Super], false) + .await?; + } Plan::CreateShareEndpoint(_) | Plan::ShowShareEndpoint(_) | Plan::DropShareEndpoint(_) diff --git a/src/query/service/src/interpreters/interpreter_copy_into_location.rs b/src/query/service/src/interpreters/interpreter_copy_into_location.rs new file mode 100644 index 000000000000..3323843acaeb --- /dev/null +++ b/src/query/service/src/interpreters/interpreter_copy_into_location.rs @@ -0,0 +1,147 @@ +// Copyright 2021 Datafuse Labs +// +// 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. + +use std::sync::Arc; + +use common_catalog::plan::StageTableInfo; +use common_catalog::table::AppendMode; +use common_exception::Result; +use common_expression::infer_table_schema; +use common_expression::DataField; +use common_expression::DataSchemaRef; +use common_expression::DataSchemaRefExt; +use common_meta_app::principal::StageInfo; +use common_storage::StageFilesInfo; +use common_storages_stage::StageTable; +use log::debug; + +use crate::interpreters::common::check_deduplicate_label; +use crate::interpreters::Interpreter; +use crate::interpreters::SelectInterpreter; +use crate::pipelines::builders::build_append2table_with_commit_pipeline; +use crate::pipelines::PipelineBuildResult; +use crate::sessions::QueryContext; +use crate::sessions::TableContext; +use crate::sql::plans::CopyIntoLocationPlan; +use crate::sql::plans::Plan; + +pub struct CopyIntoLocationInterpreter { + ctx: Arc, + plan: CopyIntoLocationPlan, +} + +impl CopyIntoLocationInterpreter { + /// Create a CopyInterpreter with context and [`CopyIntoLocationPlan`]. + pub fn try_create(ctx: Arc, plan: CopyIntoLocationPlan) -> Result { + Ok(CopyIntoLocationInterpreter { ctx, plan }) + } + + #[async_backtrace::framed] + async fn build_query(&self, query: &Plan) -> Result<(SelectInterpreter, DataSchemaRef)> { + let (s_expr, metadata, bind_context, formatted_ast) = match query { + Plan::Query { + s_expr, + metadata, + bind_context, + formatted_ast, + .. + } => (s_expr, metadata, bind_context, formatted_ast), + v => unreachable!("Input plan must be Query, but it's {}", v), + }; + + let select_interpreter = SelectInterpreter::try_create( + self.ctx.clone(), + *(bind_context.clone()), + *s_expr.clone(), + metadata.clone(), + formatted_ast.clone(), + false, + )?; + + // Building data schema from bind_context columns + // TODO(leiyskey): Extract the following logic as new API of BindContext. + let fields = bind_context + .columns + .iter() + .map(|column_binding| { + DataField::new( + &column_binding.column_name, + *column_binding.data_type.clone(), + ) + }) + .collect(); + let data_schema = DataSchemaRefExt::create(fields); + + Ok((select_interpreter, data_schema)) + } + + /// Build a pipeline for local copy into stage. + #[async_backtrace::framed] + async fn build_local_copy_into_stage_pipeline( + &self, + stage: &StageInfo, + path: &str, + query: &Plan, + ) -> Result { + let (select_interpreter, data_schema) = self.build_query(query).await?; + let plan = select_interpreter.build_physical_plan().await?; + let mut build_res = select_interpreter.build_pipeline(plan).await?; + let table_schema = infer_table_schema(&data_schema)?; + let stage_table_info = StageTableInfo { + schema: table_schema, + stage_info: stage.clone(), + files_info: StageFilesInfo { + path: path.to_string(), + files: None, + pattern: None, + }, + files_to_copy: None, + is_select: false, + }; + let to_table = StageTable::try_create(stage_table_info)?; + build_append2table_with_commit_pipeline( + self.ctx.clone(), + &mut build_res.main_pipeline, + to_table, + data_schema, + None, + false, + AppendMode::Normal, + )?; + Ok(build_res) + } +} + +#[async_trait::async_trait] +impl Interpreter for CopyIntoLocationInterpreter { + fn name(&self) -> &str { + "CopyIntoLocationInterpreterV2" + } + + #[minitrace::trace(name = "copy_into_location_interpreter_execute_v2")] + #[async_backtrace::framed] + async fn execute2(&self) -> Result { + debug!("ctx.id" = self.ctx.get_id().as_str(); "copy_into_location_interpreter_execute_v2"); + + if check_deduplicate_label(self.ctx.clone()).await? { + return Ok(PipelineBuildResult::create()); + } + self.build_local_copy_into_stage_pipeline( + &self.plan.stage, + &self.plan.path, + &self.plan.from, + ) + .await + } +} diff --git a/src/query/service/src/interpreters/interpreter_copy.rs b/src/query/service/src/interpreters/interpreter_copy_into_table.rs similarity index 71% rename from src/query/service/src/interpreters/interpreter_copy.rs rename to src/query/service/src/interpreters/interpreter_copy_into_table.rs index 543dc95c63c4..6482cd552800 100644 --- a/src/query/service/src/interpreters/interpreter_copy.rs +++ b/src/query/service/src/interpreters/interpreter_copy_into_table.rs @@ -16,9 +16,7 @@ use std::sync::Arc; use std::time::Instant; use common_catalog::plan::StageTableInfo; -use common_catalog::table::AppendMode; use common_exception::Result; -use common_expression::infer_table_schema; use common_expression::types::Int32Type; use common_expression::types::StringType; use common_expression::BlockThresholds; @@ -29,17 +27,14 @@ use common_expression::DataSchemaRefExt; use common_expression::FromData; use common_expression::FromOptData; use common_expression::SendableDataBlockStream; -use common_meta_app::principal::StageInfo; use common_pipeline_core::Pipeline; use common_sql::executor::table_read_plan::ToReadDataSourcePlan; -use common_sql::executor::CopyIntoTable; +use common_sql::executor::CopyIntoTablePhysicalPlan; use common_sql::executor::CopyIntoTableSource; use common_sql::executor::Exchange; use common_sql::executor::FragmentKind; use common_sql::executor::PhysicalPlan; -use common_sql::plans::CopyIntoTablePlan; use common_storage::StageFileInfo; -use common_storage::StageFilesInfo; use common_storages_stage::StageTable; use log::debug; use log::info; @@ -50,25 +45,24 @@ use crate::interpreters::common::CompactHookTraceCtx; use crate::interpreters::common::CompactTargetTableDescription; use crate::interpreters::Interpreter; use crate::interpreters::SelectInterpreter; -use crate::pipelines::builders::build_append2table_with_commit_pipeline; use crate::pipelines::builders::build_commit_data_pipeline; use crate::pipelines::PipelineBuildResult; use crate::schedulers::build_query_pipeline_without_render_result_set; use crate::sessions::QueryContext; use crate::sessions::TableContext; -use crate::sql::plans::CopyPlan; +use crate::sql::plans::CopyIntoTablePlan; use crate::sql::plans::Plan; use crate::stream::DataBlockStream; -pub struct CopyInterpreter { +pub struct CopyIntoTableInterpreter { ctx: Arc, - plan: CopyPlan, + plan: CopyIntoTablePlan, } -impl CopyInterpreter { - /// Create a CopyInterpreter with context and [`CopyPlan`]. - pub fn try_create(ctx: Arc, plan: CopyPlan) -> Result { - Ok(CopyInterpreter { ctx, plan }) +impl CopyIntoTableInterpreter { + /// Create a CopyInterpreter with context and [`CopyIntoTablePlan`]. + pub fn try_create(ctx: Arc, plan: CopyIntoTablePlan) -> Result { + Ok(CopyIntoTableInterpreter { ctx, plan }) } #[async_backtrace::framed] @@ -110,42 +104,6 @@ impl CopyInterpreter { Ok((select_interpreter, data_schema)) } - /// Build a pipeline for local copy into stage. - #[async_backtrace::framed] - async fn build_local_copy_into_stage_pipeline( - &self, - stage: &StageInfo, - path: &str, - query: &Plan, - ) -> Result { - let (select_interpreter, data_schema) = self.build_query(query).await?; - let plan = select_interpreter.build_physical_plan().await?; - let mut build_res = select_interpreter.build_pipeline(plan).await?; - let table_schema = infer_table_schema(&data_schema)?; - let stage_table_info = StageTableInfo { - schema: table_schema, - stage_info: stage.clone(), - files_info: StageFilesInfo { - path: path.to_string(), - files: None, - pattern: None, - }, - files_to_copy: None, - is_select: false, - }; - let to_table = StageTable::try_create(stage_table_info)?; - build_append2table_with_commit_pipeline( - self.ctx.clone(), - &mut build_res.main_pipeline, - to_table, - data_schema, - None, - false, - AppendMode::Normal, - )?; - Ok(build_res) - } - fn set_status(&self, status: &str) { self.ctx.set_status_info(status); info!("{}", status); @@ -195,7 +153,7 @@ impl CopyInterpreter { CopyIntoTableSource::Stage(read_source_plan) }; - let mut root = PhysicalPlan::CopyIntoTable(Box::new(CopyIntoTable { + let mut root = PhysicalPlan::CopyIntoTable(Box::new(CopyIntoTablePhysicalPlan { catalog_info: plan.catalog_info.clone(), required_values_schema: plan.required_values_schema.clone(), values_consts: plan.values_consts.clone(), @@ -254,10 +212,12 @@ impl CopyInterpreter { } fn get_copy_into_table_result(&self) -> Result> { - let return_all = match self.plan.copy_into_table_options() { - None => return Ok(vec![]), - Some(o) => !o.return_failed_only, - }; + let return_all = !self + .plan + .stage_table_info + .stage_info + .copy_options + .return_failed_only; let cs = self.ctx.get_copy_status(); let mut results = cs.files.iter().collect::>(); @@ -298,15 +258,15 @@ impl CopyInterpreter { } #[async_trait::async_trait] -impl Interpreter for CopyInterpreter { +impl Interpreter for CopyIntoTableInterpreter { fn name(&self) -> &str { - "CopyInterpreterV2" + "CopyIntoTableInterpreterV2" } - #[minitrace::trace(name = "copy_interpreter_execute_v2")] + #[minitrace::trace(name = "copy_into_table_interpreter_execute_v2")] #[async_backtrace::framed] async fn execute2(&self) -> Result { - debug!("ctx.id" = self.ctx.get_id().as_str(); "copy_interpreter_execute_v2"); + debug!("ctx.id" = self.ctx.get_id().as_str(); "copy_into_table_interpreter_execute_v2"); let start = Instant::now(); @@ -314,58 +274,44 @@ impl Interpreter for CopyInterpreter { return Ok(PipelineBuildResult::create()); } - match &self.plan { - CopyPlan::IntoTable(plan) => { - let (physical_plan, files) = self.build_physical_plan(plan).await?; - let mut build_res = build_query_pipeline_without_render_result_set( - &self.ctx, - &physical_plan, - false, - ) + if self.plan.no_file_to_copy { + return Ok(PipelineBuildResult::create()); + } + let (physical_plan, files) = self.build_physical_plan(&self.plan).await?; + let mut build_res = + build_query_pipeline_without_render_result_set(&self.ctx, &physical_plan, false) .await?; - build_commit_data_pipeline(&self.ctx, &mut build_res.main_pipeline, plan, &files) - .await?; + build_commit_data_pipeline(&self.ctx, &mut build_res.main_pipeline, &self.plan, &files) + .await?; - let compact_target = CompactTargetTableDescription { - catalog: plan.catalog_info.name_ident.catalog_name.clone(), - database: plan.database_name.clone(), - table: plan.table_name.clone(), - }; + let compact_target = CompactTargetTableDescription { + catalog: self.plan.catalog_info.name_ident.catalog_name.clone(), + database: self.plan.database_name.clone(), + table: self.plan.table_name.clone(), + }; - let trace_ctx = CompactHookTraceCtx { - start, - operation_name: "copy_into".to_owned(), - }; + let trace_ctx = CompactHookTraceCtx { + start, + operation_name: "copy_into_table".to_owned(), + }; - hook_compact( - self.ctx.clone(), - &mut build_res.main_pipeline, - compact_target, - trace_ctx, - ) - .await; - Ok(build_res) - } - CopyPlan::IntoStage { - stage, from, path, .. - } => { - self.build_local_copy_into_stage_pipeline(stage, path, from) - .await - } - CopyPlan::NoFileToCopy => Ok(PipelineBuildResult::create()), - } + hook_compact( + self.ctx.clone(), + &mut build_res.main_pipeline, + compact_target, + trace_ctx, + ) + .await; + Ok(build_res) } fn inject_result(&self) -> Result { - let blocks = match &self.plan { - CopyPlan::NoFileToCopy => { - vec![DataBlock::empty_with_schema(self.plan.schema())] - } - CopyPlan::IntoTable(p) if !p.from_attachment => self.get_copy_into_table_result()?, - _ => { - vec![] - } + let blocks = if self.plan.no_file_to_copy { + vec![DataBlock::empty_with_schema(self.plan.schema())] + } else { + self.get_copy_into_table_result()? }; + Ok(Box::pin(DataBlockStream::create(None, blocks))) } } diff --git a/src/query/service/src/interpreters/interpreter_factory.rs b/src/query/service/src/interpreters/interpreter_factory.rs index b4fa1a557e7d..c490f6eff1c1 100644 --- a/src/query/service/src/interpreters/interpreter_factory.rs +++ b/src/query/service/src/interpreters/interpreter_factory.rs @@ -29,7 +29,8 @@ use super::interpreter_user_stage_drop::DropUserStageInterpreter; use super::*; use crate::interpreters::access::Accessor; use crate::interpreters::interpreter_catalog_drop::DropCatalogInterpreter; -use crate::interpreters::interpreter_copy::CopyInterpreter; +use crate::interpreters::interpreter_copy_into_location::CopyIntoLocationInterpreter; +use crate::interpreters::interpreter_copy_into_table::CopyIntoTableInterpreter; use crate::interpreters::interpreter_file_format_create::CreateFileFormatInterpreter; use crate::interpreters::interpreter_file_format_drop::DropFileFormatInterpreter; use crate::interpreters::interpreter_file_format_show::ShowFileFormatsInterpreter; @@ -102,10 +103,13 @@ impl InterpreterFactory { ExplainKind::AnalyzePlan, )?)), - Plan::Copy(copy_plan) => Ok(Arc::new(CopyInterpreter::try_create( + Plan::CopyIntoTable(copy_plan) => Ok(Arc::new(CopyIntoTableInterpreter::try_create( ctx, - *copy_plan.clone(), + copy_plan.clone(), )?)), + Plan::CopyIntoLocation(copy_plan) => Ok(Arc::new( + CopyIntoLocationInterpreter::try_create(ctx, copy_plan.clone())?, + )), // catalogs Plan::ShowCreateCatalog(plan) => Ok(Arc::new( ShowCreateCatalogInterpreter::try_create(ctx, *plan.clone())?, diff --git a/src/query/service/src/interpreters/interpreter_replace.rs b/src/query/service/src/interpreters/interpreter_replace.rs index 3051d1c471d7..a139edbd2a6c 100644 --- a/src/query/service/src/interpreters/interpreter_replace.rs +++ b/src/query/service/src/interpreters/interpreter_replace.rs @@ -29,7 +29,6 @@ use common_sql::executor::OnConflictField; use common_sql::executor::PhysicalPlan; use common_sql::executor::ReplaceInto; use common_sql::executor::SelectCtx; -use common_sql::plans::CopyPlan; use common_sql::plans::InsertInputSource; use common_sql::plans::Plan; use common_sql::plans::Replace; @@ -42,7 +41,7 @@ use crate::interpreters::common::check_deduplicate_label; use crate::interpreters::common::hook_compact; use crate::interpreters::common::CompactHookTraceCtx; use crate::interpreters::common::CompactTargetTableDescription; -use crate::interpreters::interpreter_copy::CopyInterpreter; +use crate::interpreters::interpreter_copy_into_table::CopyIntoTableInterpreter; use crate::interpreters::Interpreter; use crate::interpreters::InterpreterPtr; use crate::interpreters::SelectInterpreter; @@ -284,22 +283,15 @@ impl ReplaceInterpreter { self.connect_query_plan_source(ctx.clone(), plan).await } InsertInputSource::Stage(plan) => match *plan.clone() { - Plan::Copy(copy_plan) => match copy_plan.as_ref() { - CopyPlan::IntoTable(copy_into_table_plan) => { - let interpreter = - CopyInterpreter::try_create(ctx.clone(), *copy_plan.clone())?; - let (physical_plan, files) = interpreter - .build_physical_plan(copy_into_table_plan) - .await?; - *purge_info = Some(( - files, - copy_into_table_plan.stage_table_info.stage_info.clone(), - )); - Ok((Box::new(physical_plan), None)) - } - _ => unreachable!("plan in InsertInputSource::Stage must be CopyIntoTable"), - }, - _ => unreachable!("plan in InsertInputSource::Stag must be Copy"), + Plan::CopyIntoTable(copy_plan) => { + let interpreter = + CopyIntoTableInterpreter::try_create(ctx.clone(), copy_plan.clone())?; + let (physical_plan, files) = + interpreter.build_physical_plan(©_plan).await?; + *purge_info = Some((files, copy_plan.stage_table_info.stage_info.clone())); + Ok((Box::new(physical_plan), None)) + } + _ => unreachable!("plan in InsertInputSource::Stag must be CopyIntoTable"), }, _ => Err(ErrorCode::Unimplemented( "input source other than literal VALUES and sub queries are NOT supported yet.", diff --git a/src/query/service/src/interpreters/mod.rs b/src/query/service/src/interpreters/mod.rs index 7feea4602174..80fe92d23968 100644 --- a/src/query/service/src/interpreters/mod.rs +++ b/src/query/service/src/interpreters/mod.rs @@ -21,7 +21,8 @@ mod interpreter_catalog_show_create; mod interpreter_cluster_key_alter; mod interpreter_cluster_key_drop; mod interpreter_clustering_history; -mod interpreter_copy; +mod interpreter_copy_into_location; +mod interpreter_copy_into_table; mod interpreter_data_mask_create; mod interpreter_data_mask_desc; mod interpreter_data_mask_drop; diff --git a/src/query/service/src/pipelines/builders/copy.rs b/src/query/service/src/pipelines/builders/copy.rs index 39c55d08a77d..a5f66afcf1f8 100644 --- a/src/query/service/src/pipelines/builders/copy.rs +++ b/src/query/service/src/pipelines/builders/copy.rs @@ -29,7 +29,7 @@ use common_meta_app::principal::StageInfo; use common_meta_app::schema::TableCopiedFileInfo; use common_meta_app::schema::UpsertTableCopiedFileReq; use common_pipeline_core::Pipeline; -use common_sql::executor::CopyIntoTable; +use common_sql::executor::CopyIntoTablePhysicalPlan; use common_sql::plans::CopyIntoTableMode; use common_sql::plans::CopyIntoTablePlan; use common_storage::metrics::copy::metrics_inc_copy_purge_files_cost_milliseconds; @@ -49,7 +49,7 @@ use crate::sessions::QueryContext; pub fn build_append_data_pipeline( ctx: Arc, main_pipeline: &mut Pipeline, - plan: &CopyIntoTable, + plan: &CopyIntoTablePhysicalPlan, source_schema: Arc, to_table: Arc, ) -> Result<()> { diff --git a/src/query/service/src/pipelines/pipeline_builder.rs b/src/query/service/src/pipelines/pipeline_builder.rs index afd3728fba48..5ff201c0e78d 100644 --- a/src/query/service/src/pipelines/pipeline_builder.rs +++ b/src/query/service/src/pipelines/pipeline_builder.rs @@ -79,7 +79,7 @@ use common_sql::executor::AsyncSourcerPlan; use common_sql::executor::CommitSink; use common_sql::executor::CompactPartial; use common_sql::executor::ConstantTableScan; -use common_sql::executor::CopyIntoTable; +use common_sql::executor::CopyIntoTablePhysicalPlan; use common_sql::executor::CopyIntoTableSource; use common_sql::executor::CteScan; use common_sql::executor::Deduplicate; @@ -840,7 +840,7 @@ impl PipelineBuilder { Ok(()) } - fn build_copy_into_table(&mut self, copy: &CopyIntoTable) -> Result<()> { + fn build_copy_into_table(&mut self, copy: &CopyIntoTablePhysicalPlan) -> Result<()> { let to_table = self.ctx .build_table_by_table_info(©.catalog_info, ©.table_info, None)?; diff --git a/src/query/service/src/schedulers/fragments/fragmenter.rs b/src/query/service/src/schedulers/fragments/fragmenter.rs index b6ac093b1f17..412969f96920 100644 --- a/src/query/service/src/schedulers/fragments/fragmenter.rs +++ b/src/query/service/src/schedulers/fragments/fragmenter.rs @@ -16,7 +16,7 @@ use std::sync::Arc; use common_catalog::table_context::TableContext; use common_exception::Result; -use common_sql::executor::CopyIntoTable; +use common_sql::executor::CopyIntoTablePhysicalPlan; use common_sql::executor::CopyIntoTableSource; use common_sql::executor::FragmentKind; use common_sql::executor::QuerySource; @@ -161,7 +161,10 @@ impl PhysicalPlanReplacer for Fragmenter { } // TODO(Sky): remove rebudant code - fn replace_copy_into_table(&mut self, plan: &CopyIntoTable) -> Result { + fn replace_copy_into_table( + &mut self, + plan: &CopyIntoTablePhysicalPlan, + ) -> Result { match &plan.source { CopyIntoTableSource::Stage(_) => { self.state = State::SelectLeaf; @@ -169,13 +172,15 @@ impl PhysicalPlanReplacer for Fragmenter { } CopyIntoTableSource::Query(query_ctx) => { let input = self.replace(&query_ctx.plan)?; - Ok(PhysicalPlan::CopyIntoTable(Box::new(CopyIntoTable { - source: CopyIntoTableSource::Query(Box::new(QuerySource { - plan: input, - ..*query_ctx.clone() - })), - ..plan.clone() - }))) + Ok(PhysicalPlan::CopyIntoTable(Box::new( + CopyIntoTablePhysicalPlan { + source: CopyIntoTableSource::Query(Box::new(QuerySource { + plan: input, + ..*query_ctx.clone() + })), + ..plan.clone() + }, + ))) } } } diff --git a/src/query/service/src/schedulers/fragments/plan_fragment.rs b/src/query/service/src/schedulers/fragments/plan_fragment.rs index d1f870a4b2c6..88154be0c46b 100644 --- a/src/query/service/src/schedulers/fragments/plan_fragment.rs +++ b/src/query/service/src/schedulers/fragments/plan_fragment.rs @@ -21,7 +21,7 @@ use common_exception::ErrorCode; use common_exception::Result; use common_settings::ReplaceIntoShuffleStrategy; use common_sql::executor::CompactPartial; -use common_sql::executor::CopyIntoTable; +use common_sql::executor::CopyIntoTablePhysicalPlan; use common_sql::executor::CopyIntoTableSource; use common_sql::executor::Deduplicate; use common_sql::executor::DeletePartial; @@ -395,24 +395,29 @@ impl PhysicalPlanReplacer for ReplaceReadSource { })) } - fn replace_copy_into_table(&mut self, plan: &CopyIntoTable) -> Result { + fn replace_copy_into_table( + &mut self, + plan: &CopyIntoTablePhysicalPlan, + ) -> Result { match &plan.source { CopyIntoTableSource::Query(query_ctx) => { let input = self.replace(&query_ctx.plan)?; - Ok(PhysicalPlan::CopyIntoTable(Box::new(CopyIntoTable { - source: CopyIntoTableSource::Query(Box::new(QuerySource { - plan: input, - ..*query_ctx.clone() - })), - ..plan.clone() - }))) + Ok(PhysicalPlan::CopyIntoTable(Box::new( + CopyIntoTablePhysicalPlan { + source: CopyIntoTableSource::Query(Box::new(QuerySource { + plan: input, + ..*query_ctx.clone() + })), + ..plan.clone() + }, + ))) } - CopyIntoTableSource::Stage(_) => { - Ok(PhysicalPlan::CopyIntoTable(Box::new(CopyIntoTable { + CopyIntoTableSource::Stage(_) => Ok(PhysicalPlan::CopyIntoTable(Box::new( + CopyIntoTablePhysicalPlan { source: CopyIntoTableSource::Stage(Box::new(self.source.clone())), ..plan.clone() - }))) - } + }, + ))), } } } diff --git a/src/query/service/src/sessions/query_ctx.rs b/src/query/service/src/sessions/query_ctx.rs index 95502160da74..01f4a726e164 100644 --- a/src/query/service/src/sessions/query_ctx.rs +++ b/src/query/service/src/sessions/query_ctx.rs @@ -755,7 +755,7 @@ impl TableContext for QueryContext { } fn add_file_status(&self, file_path: &str, file_status: FileStatus) -> Result<()> { - if matches!(self.get_query_kind(), QueryKind::Copy) { + if matches!(self.get_query_kind(), QueryKind::CopyIntoTable) { self.shared.copy_status.add_chunk(file_path, file_status); } Ok(()) diff --git a/src/query/sql/src/executor/format.rs b/src/query/sql/src/executor/format.rs index f218b8e03e20..2fd907e9669d 100644 --- a/src/query/sql/src/executor/format.rs +++ b/src/query/sql/src/executor/format.rs @@ -27,7 +27,7 @@ use crate::executor::AggregateFunctionDesc; use crate::executor::AggregatePartial; use crate::executor::CommitSink; use crate::executor::ConstantTableScan; -use crate::executor::CopyIntoTable; +use crate::executor::CopyIntoTablePhysicalPlan; use crate::executor::CteScan; use crate::executor::DeletePartial; use crate::executor::DistributedInsertSelect; @@ -246,7 +246,7 @@ fn append_profile_info( } } -fn copy_into_table(plan: &CopyIntoTable) -> Result> { +fn copy_into_table(plan: &CopyIntoTablePhysicalPlan) -> Result> { Ok(FormatTreeNode::new(format!( "CopyIntoTable: {}", plan.table_info diff --git a/src/query/sql/src/executor/physical_plan.rs b/src/query/sql/src/executor/physical_plan.rs index af7189f94280..69ef0827cfec 100644 --- a/src/query/sql/src/executor/physical_plan.rs +++ b/src/query/sql/src/executor/physical_plan.rs @@ -794,7 +794,7 @@ pub struct MergeInto { } #[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] -pub struct CopyIntoTable { +pub struct CopyIntoTablePhysicalPlan { pub catalog_info: CatalogInfo, pub required_values_schema: DataSchemaRef, pub values_consts: Vec, @@ -823,7 +823,7 @@ pub struct QuerySource { pub result_columns: Vec, } -impl CopyIntoTable { +impl CopyIntoTablePhysicalPlan { pub fn output_schema(&self) -> Result { match &self.source { CopyIntoTableSource::Query(query_ctx) => Ok(query_ctx.query_source_schema.clone()), @@ -1041,7 +1041,7 @@ pub enum PhysicalPlan { /// Delete DeletePartial(Box), /// Copy into table - CopyIntoTable(Box), + CopyIntoTable(Box), /// Replace AsyncSourcer(AsyncSourcerPlan), Deduplicate(Deduplicate), diff --git a/src/query/sql/src/executor/physical_plan_display.rs b/src/query/sql/src/executor/physical_plan_display.rs index c1e8a03283d7..8e37782a10f5 100644 --- a/src/query/sql/src/executor/physical_plan_display.rs +++ b/src/query/sql/src/executor/physical_plan_display.rs @@ -25,7 +25,7 @@ use crate::executor::AsyncSourcerPlan; use crate::executor::CommitSink; use crate::executor::CompactPartial; use crate::executor::ConstantTableScan; -use crate::executor::CopyIntoTable; +use crate::executor::CopyIntoTablePhysicalPlan; use crate::executor::CteScan; use crate::executor::Deduplicate; use crate::executor::DeletePartial; @@ -418,7 +418,7 @@ impl Display for CommitSink { write!(f, "CommitSink") } } -impl Display for CopyIntoTable { +impl Display for CopyIntoTablePhysicalPlan { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "CopyIntoTable") } diff --git a/src/query/sql/src/executor/physical_plan_visitor.rs b/src/query/sql/src/executor/physical_plan_visitor.rs index 9b990c13cbbe..ee8b562f9ca0 100644 --- a/src/query/sql/src/executor/physical_plan_visitor.rs +++ b/src/query/sql/src/executor/physical_plan_visitor.rs @@ -21,7 +21,7 @@ use crate::executor::AsyncSourcerPlan; use crate::executor::CommitSink; use crate::executor::CompactPartial; use crate::executor::ConstantTableScan; -use crate::executor::CopyIntoTable; +use crate::executor::CopyIntoTablePhysicalPlan; use crate::executor::CopyIntoTableSource; use crate::executor::CteScan; use crate::executor::Deduplicate; @@ -330,20 +330,25 @@ pub trait PhysicalPlanReplacer { })) } - fn replace_copy_into_table(&mut self, plan: &CopyIntoTable) -> Result { + fn replace_copy_into_table( + &mut self, + plan: &CopyIntoTablePhysicalPlan, + ) -> Result { match &plan.source { CopyIntoTableSource::Stage(_) => { Ok(PhysicalPlan::CopyIntoTable(Box::new(plan.clone()))) } CopyIntoTableSource::Query(query_ctx) => { let input = self.replace(&query_ctx.plan)?; - Ok(PhysicalPlan::CopyIntoTable(Box::new(CopyIntoTable { - source: CopyIntoTableSource::Query(Box::new(QuerySource { - plan: input, - ..*query_ctx.clone() - })), - ..plan.clone() - }))) + Ok(PhysicalPlan::CopyIntoTable(Box::new( + CopyIntoTablePhysicalPlan { + source: CopyIntoTableSource::Query(Box::new(QuerySource { + plan: input, + ..*query_ctx.clone() + })), + ..plan.clone() + }, + ))) } } } diff --git a/src/query/sql/src/planner/binder/binder.rs b/src/query/sql/src/planner/binder/binder.rs index 959fb3de28b9..721a692e3a0b 100644 --- a/src/query/sql/src/planner/binder/binder.rs +++ b/src/query/sql/src/planner/binder/binder.rs @@ -228,13 +228,22 @@ impl<'a> Binder { self.bind_show_table_functions(bind_context, limit).await? } - Statement::Copy(stmt) => { + Statement::CopyIntoTable(stmt) => { if let Some(hints) = &stmt.hints { if let Some(e) = self.opt_hints_set_var(bind_context, hints).await.err() { warn!("In Copy resolve optimize hints {:?} failed, err: {:?}", hints, e); } } - self.bind_copy(bind_context, stmt).await? + self.bind_copy_into_table(bind_context, stmt).await? + } + + Statement::CopyIntoLocation(stmt) => { + if let Some(hints) = &stmt.hints { + if let Some(e) = self.opt_hints_set_var(bind_context, hints).await.err() { + warn!("In Copy resolve optimize hints {:?} failed, err: {:?}", hints, e); + } + } + self.bind_copy_into_location(bind_context, stmt).await? } Statement::ShowMetrics => { diff --git a/src/query/sql/src/planner/binder/copy_into_location.rs b/src/query/sql/src/planner/binder/copy_into_location.rs new file mode 100644 index 000000000000..1c2a38948aeb --- /dev/null +++ b/src/query/sql/src/planner/binder/copy_into_location.rs @@ -0,0 +1,100 @@ +// Copyright 2021 Datafuse Labs +// +// 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. + +use common_ast::ast::CopyIntoLocationSource; +use common_ast::ast::CopyIntoLocationStmt; +use common_ast::ast::Statement; +use common_ast::parser::parse_sql; +use common_ast::parser::tokenize_sql; +use common_ast::Dialect; +use common_exception::ErrorCode; +use common_exception::Result; +use common_meta_app::principal::StageInfo; + +use crate::binder::copy_into_table::resolve_file_location; +use crate::binder::Binder; +use crate::plans::CopyIntoLocationPlan; +use crate::plans::Plan; +use crate::BindContext; + +impl<'a> Binder { + #[async_backtrace::framed] + pub(in crate::planner::binder) async fn bind_copy_into_location( + &mut self, + bind_context: &mut BindContext, + stmt: &CopyIntoLocationStmt, + ) -> Result { + let query = match &stmt.src { + CopyIntoLocationSource::Table(table) => { + let (catalog_name, database_name, table_name) = self + .normalize_object_identifier_triple( + &table.catalog, + &table.database, + &table.table, + ); + let subquery = format!("SELECT * FROM {catalog_name}.{database_name}.{table_name}"); + let tokens = tokenize_sql(&subquery)?; + let sub_stmt_msg = parse_sql(&tokens, Dialect::PostgreSQL)?; + let sub_stmt = sub_stmt_msg.0; + match &sub_stmt { + Statement::Query(query) => { + self.bind_statement(bind_context, &Statement::Query(query.clone())) + .await + } + _ => { + return Err(ErrorCode::SyntaxException( + "COPY INTO FROM is invalid", + )); + } + } + } + CopyIntoLocationSource::Query(query) => { + self.bind_statement(bind_context, &Statement::Query(query.clone())) + .await + } + }?; + + let (mut stage_info, path) = resolve_file_location(&self.ctx, &stmt.dst).await?; + self.apply_copy_into_location_options(stmt, &mut stage_info) + .await?; + + Ok(Plan::CopyIntoLocation(CopyIntoLocationPlan { + stage: Box::new(stage_info), + path, + from: Box::new(query), + })) + } + + #[async_backtrace::framed] + pub async fn apply_copy_into_location_options( + &mut self, + stmt: &CopyIntoLocationStmt, + stage: &mut StageInfo, + ) -> Result<()> { + if !stmt.file_format.is_empty() { + stage.file_format_params = self.try_resolve_file_format(&stmt.file_format).await?; + } + + // Copy options. + { + // max_file_size. + if stmt.max_file_size != 0 { + stage.copy_options.max_file_size = stmt.max_file_size; + } + stage.copy_options.single = stmt.single; + } + + Ok(()) + } +} diff --git a/src/query/sql/src/planner/binder/copy.rs b/src/query/sql/src/planner/binder/copy_into_table.rs similarity index 75% rename from src/query/sql/src/planner/binder/copy.rs rename to src/query/sql/src/planner/binder/copy_into_table.rs index 0f24ef3ffa72..a2393e4e56f0 100644 --- a/src/query/sql/src/planner/binder/copy.rs +++ b/src/query/sql/src/planner/binder/copy_into_table.rs @@ -16,22 +16,19 @@ use std::str::FromStr; use std::sync::Arc; use common_ast::ast::ColumnID as AstColumnID; -use common_ast::ast::CopyStmt; -use common_ast::ast::CopyUnit; +use common_ast::ast::CopyIntoTableSource; +use common_ast::ast::CopyIntoTableStmt; use common_ast::ast::Expr; use common_ast::ast::FileLocation; use common_ast::ast::Identifier; use common_ast::ast::Query; use common_ast::ast::SelectTarget; use common_ast::ast::SetExpr; -use common_ast::ast::Statement; use common_ast::ast::TableAlias; use common_ast::ast::TableReference; use common_ast::ast::TypeName; -use common_ast::parser::parse_sql; use common_ast::parser::parser_values_with_placeholder; use common_ast::parser::tokenize_sql; -use common_ast::Dialect; use common_ast::Visitor; use common_catalog::plan::StageTableInfo; use common_catalog::table_context::StageAttachment; @@ -58,7 +55,6 @@ use crate::binder::select::MaxColumnPosition; use crate::binder::Binder; use crate::plans::CopyIntoTableMode; use crate::plans::CopyIntoTablePlan; -use crate::plans::CopyPlan; use crate::plans::Plan; use crate::plans::ValidationMode; use crate::BindContext; @@ -67,42 +63,35 @@ use crate::NameResolutionContext; impl<'a> Binder { #[async_backtrace::framed] - pub(in crate::planner::binder) async fn bind_copy( + pub(in crate::planner::binder) async fn bind_copy_into_table( &mut self, bind_context: &mut BindContext, - stmt: &CopyStmt, + stmt: &CopyIntoTableStmt, ) -> Result { - match (&stmt.src, &stmt.dst) { - ( - CopyUnit::Location(location), - CopyUnit::Table { - catalog, - database, - table, - columns, - }, - ) => { - let (catalog_name, database_name, table_name) = - self.normalize_object_identifier_triple(catalog, database, table); + let (catalog_name, database_name, table_name) = self.normalize_object_identifier_triple( + &stmt.dst.catalog, + &stmt.dst.database, + &stmt.dst.table, + ); + let catalog = self.ctx.get_catalog(&catalog_name).await?; + let catalog_info = catalog.info(); + let table = self + .ctx + .get_table(&catalog_name, &database_name, &table_name) + .await?; + match &stmt.src { + CopyIntoTableSource::Location(location) => { let (mut stage_info, path) = resolve_file_location(&self.ctx, location).await?; - self.apply_stage_options(stmt, &mut stage_info).await?; + self.apply_copy_into_table_options(stmt, &mut stage_info) + .await?; let files_info = StageFilesInfo { path, files: stmt.files.clone(), pattern: stmt.pattern.clone(), }; - - let catalog = self.ctx.get_catalog(&catalog_name).await?; - let catalog_info = catalog.info(); - - let table = self - .ctx - .get_table(&catalog_name, &database_name, &table_name) - .await?; - let required_values_schema: DataSchemaRef = Arc::new( - match columns { + match &stmt.dst_columns { Some(cols) => self.schema_project(&table.schema(), cols)?, None => self.schema_project(&table.schema(), &[])?, } @@ -119,6 +108,7 @@ impl<'a> Binder { database_name, table_name, validation_mode, + no_file_to_copy: false, from_attachment: false, force: stmt.force, stage_table_info: StageTableInfo { @@ -140,49 +130,7 @@ impl<'a> Binder { self.bind_copy_into_table_from_location(bind_context, plan) .await } - ( - CopyUnit::Table { - catalog, - database, - table, - columns, - }, - CopyUnit::Location(location), - ) => { - if columns.is_some() { - return Err(ErrorCode::SyntaxException( - "COPY FROM STAGE does not support column list", - )); - } - let (catalog_name, database_name, table_name) = - self.normalize_object_identifier_triple(catalog, database, table); - - self.bind_copy_from_table_into_location( - bind_context, - stmt, - &catalog_name, - &database_name, - &table_name, - location, - ) - .await - } - (CopyUnit::Query(query), CopyUnit::Location(location)) => { - self.bind_copy_from_query_into_location(bind_context, stmt, query, location) - .await - } - ( - CopyUnit::Query(query), - CopyUnit::Table { - catalog, - database, - table, - columns, - }, - ) => { - let (catalog_name, database_name, table_name) = - self.normalize_object_identifier_triple(catalog, database, table); - + CopyIntoTableSource::Query(query) => { let mut max_column_position = MaxColumnPosition::new(); max_column_position.visit_query(query.as_ref()); self.metadata @@ -191,29 +139,23 @@ impl<'a> Binder { let (select_list, location, alias) = check_transform_query(query)?; let (mut stage_info, path) = resolve_file_location(&self.ctx, location).await?; - self.apply_stage_options(stmt, &mut stage_info).await?; + self.apply_copy_into_table_options(stmt, &mut stage_info) + .await?; let files_info = StageFilesInfo { path, pattern: stmt.pattern.clone(), files: stmt.files.clone(), }; - let catalog = self.ctx.get_catalog(&catalog_name).await?; - let catalog_info = catalog.info(); - - let table = self - .ctx - .get_table(&catalog_name, &database_name, &table_name) - .await?; - let required_values_schema: DataSchemaRef = Arc::new( - match columns { + match &stmt.dst_columns { Some(cols) => self.schema_project(&table.schema(), cols)?, None => table.schema(), } .into(), ); let plan = CopyIntoTablePlan { + no_file_to_copy: false, catalog_info, database_name, table_name, @@ -238,11 +180,6 @@ impl<'a> Binder { self.bind_copy_from_query_into_table(bind_context, plan, select_list, alias) .await } - (src, dst) => Err(ErrorCode::SyntaxException(format!( - "COPY INTO <{}> FROM <{}> is invalid", - dst.target(), - src.target() - ))), } } @@ -289,7 +226,7 @@ impl<'a> Binder { self.bind_copy_from_query_into_table(bind_ctx, plan, &select_list, &None) .await } else { - Ok(Plan::Copy(Box::new(CopyPlan::IntoTable(plan)))) + Ok(Plan::CopyIntoTable(plan)) } } @@ -351,6 +288,7 @@ impl<'a> Binder { catalog_info, database_name, table_name, + no_file_to_copy: false, from_attachment: true, required_source_schema: data_schema.clone(), required_values_schema, @@ -374,78 +312,6 @@ impl<'a> Binder { .await } - /// Bind COPY INFO FROM
- #[allow(clippy::too_many_arguments)] - #[async_backtrace::framed] - async fn bind_copy_from_table_into_location( - &mut self, - bind_context: &mut BindContext, - stmt: &CopyStmt, - src_catalog_name: &str, - src_database_name: &str, - src_table_name: &str, - location: &FileLocation, - ) -> Result { - let subquery = - format!("SELECT * FROM {src_catalog_name}.{src_database_name}.{src_table_name}"); - let tokens = tokenize_sql(&subquery)?; - let sub_stmt_msg = parse_sql(&tokens, Dialect::PostgreSQL)?; - let sub_stmt = sub_stmt_msg.0; - let query = match &sub_stmt { - Statement::Query(query) => { - self.bind_statement(bind_context, &Statement::Query(query.clone())) - .await? - } - _ => { - return Err(ErrorCode::SyntaxException( - "COPY INTO FROM is invalid", - )); - } - }; - - // Validation mode. - let validation_mode = ValidationMode::from_str(stmt.validation_mode.as_str()) - .map_err(ErrorCode::SyntaxException)?; - - let (mut stage_info, path) = resolve_file_location(&self.ctx, location).await?; - self.apply_stage_options(stmt, &mut stage_info).await?; - - Ok(Plan::Copy(Box::new(CopyPlan::IntoStage { - stage: Box::new(stage_info), - path, - validation_mode, - from: Box::new(query), - }))) - } - - /// Bind COPY INFO FROM - #[async_backtrace::framed] - async fn bind_copy_from_query_into_location( - &mut self, - bind_context: &mut BindContext, - stmt: &CopyStmt, - src_query: &Query, - location: &FileLocation, - ) -> Result { - let query = self - .bind_statement(bind_context, &Statement::Query(Box::new(src_query.clone()))) - .await?; - - // Validation mode. - let validation_mode = ValidationMode::from_str(stmt.validation_mode.as_str()) - .map_err(ErrorCode::SyntaxException)?; - - let (mut stage_info, path) = resolve_file_location(&self.ctx, location).await?; - self.apply_stage_options(stmt, &mut stage_info).await?; - - Ok(Plan::Copy(Box::new(CopyPlan::IntoStage { - stage: Box::new(stage_info), - path, - validation_mode, - from: Box::new(query), - }))) - } - /// Bind COPY INTO
FROM #[async_backtrace::framed] #[allow(clippy::too_many_arguments)] @@ -459,7 +325,8 @@ impl<'a> Binder { let need_copy_file_infos = plan.collect_files(self.ctx.as_ref()).await?; if need_copy_file_infos.is_empty() { - return Ok(Plan::Copy(Box::new(CopyPlan::NoFileToCopy))); + plan.no_file_to_copy = true; + return Ok(Plan::CopyIntoTable(plan)); } plan.stage_table_info.files_to_copy = Some(need_copy_file_infos.clone()); @@ -504,11 +371,15 @@ impl<'a> Binder { ignore_result: false, formatted_ast: None, })); - Ok(Plan::Copy(Box::new(CopyPlan::IntoTable(plan)))) + Ok(Plan::CopyIntoTable(plan)) } #[async_backtrace::framed] - async fn apply_stage_options(&mut self, stmt: &CopyStmt, stage: &mut StageInfo) -> Result<()> { + pub async fn apply_copy_into_table_options( + &mut self, + stmt: &CopyIntoTableStmt, + stage: &mut StageInfo, + ) -> Result<()> { if !stmt.file_format.is_empty() { stage.file_format_params = self.try_resolve_file_format(&stmt.file_format).await?; } @@ -526,13 +397,7 @@ impl<'a> Binder { if stmt.max_files != 0 { stage.copy_options.max_files = stmt.max_files; } - // max_file_size. - if stmt.max_file_size != 0 { - stage.copy_options.max_file_size = stmt.max_file_size; - } stage.copy_options.split_size = stmt.split_size; - - stage.copy_options.single = stmt.single; stage.copy_options.purge = stmt.purge; stage.copy_options.disable_variant_check = stmt.disable_variant_check; stage.copy_options.return_failed_only = stmt.return_failed_only; diff --git a/src/query/sql/src/planner/binder/ddl/stage.rs b/src/query/sql/src/planner/binder/ddl/stage.rs index c42e039e036f..4e5a575880c2 100644 --- a/src/query/sql/src/planner/binder/ddl/stage.rs +++ b/src/query/sql/src/planner/binder/ddl/stage.rs @@ -25,7 +25,7 @@ use common_meta_app::principal::OnErrorMode; use common_meta_app::principal::StageInfo; use common_storage::init_operator; -use super::super::copy::resolve_stage_location; +use super::super::copy_into_table::resolve_stage_location; use crate::binder::location::parse_uri_location; use crate::binder::Binder; use crate::plans::CreateStagePlan; diff --git a/src/query/sql/src/planner/binder/mod.rs b/src/query/sql/src/planner/binder/mod.rs index aeaaeb96c933..79483a975d31 100644 --- a/src/query/sql/src/planner/binder/mod.rs +++ b/src/query/sql/src/planner/binder/mod.rs @@ -20,7 +20,8 @@ mod binder; mod builders; mod call; mod column_binding; -mod copy; +mod copy_into_location; +mod copy_into_table; mod ddl; mod delete; mod distinct; @@ -51,13 +52,14 @@ mod udf; mod update; mod values; mod window; + pub use aggregate::AggregateInfo; pub use bind_context::*; pub use binder::Binder; pub use builders::*; pub use column_binding::ColumnBinding; pub use column_binding::ColumnBindingBuilder; -pub use copy::resolve_stage_location; +pub use copy_into_table::resolve_stage_location; pub use internal_column_factory::INTERNAL_COLUMN_FACTORY; pub use location::parse_uri_location; pub use scalar::ScalarBinder; diff --git a/src/query/sql/src/planner/binder/table.rs b/src/query/sql/src/planner/binder/table.rs index 92d6aa20b0c2..dca2c3a0a233 100644 --- a/src/query/sql/src/planner/binder/table.rs +++ b/src/query/sql/src/planner/binder/table.rs @@ -79,7 +79,7 @@ use common_users::UserApiProvider; use dashmap::DashMap; use parking_lot::RwLock; -use crate::binder::copy::resolve_file_location; +use crate::binder::copy_into_table::resolve_file_location; use crate::binder::scalar::ScalarBinder; use crate::binder::table_args::bind_table_args; use crate::binder::Binder; diff --git a/src/query/sql/src/planner/format/display_plan.rs b/src/query/sql/src/planner/format/display_plan.rs index cc57ad9078b9..583767f8070d 100644 --- a/src/query/sql/src/planner/format/display_plan.rs +++ b/src/query/sql/src/planner/format/display_plan.rs @@ -46,7 +46,8 @@ impl Plan { Plan::ExplainSyntax { .. } => Ok("ExplainSyntax".to_string()), Plan::ExplainAnalyze { .. } => Ok("ExplainAnalyze".to_string()), - Plan::Copy(plan) => Ok(format!("{:?}", plan)), + Plan::CopyIntoTable(plan) => Ok(format!("{:?}", plan)), + Plan::CopyIntoLocation(plan) => Ok(format!("{:?}", plan)), // catalog Plan::ShowCreateCatalog(show_create_catalog) => { diff --git a/src/query/sql/src/planner/optimizer/optimizer.rs b/src/query/sql/src/planner/optimizer/optimizer.rs index 0bf259b654b4..6ca4fa5e745e 100644 --- a/src/query/sql/src/planner/optimizer/optimizer.rs +++ b/src/query/sql/src/planner/optimizer/optimizer.rs @@ -34,7 +34,7 @@ use crate::optimizer::RuleID; use crate::optimizer::SExpr; use crate::optimizer::DEFAULT_REWRITE_RULES; use crate::optimizer::RESIDUAL_RULES; -use crate::plans::CopyPlan; +use crate::plans::CopyIntoLocationPlan; use crate::plans::Plan; use crate::IndexType; use crate::MetadataRef; @@ -107,34 +107,21 @@ pub fn optimize( Plan::ExplainAnalyze { plan } => Ok(Plan::ExplainAnalyze { plan: Box::new(optimize(ctx, opt_ctx, *plan)?), }), - Plan::Copy(v) => { - Ok(Plan::Copy(Box::new(match *v { - CopyPlan::IntoStage { - stage, - path, - validation_mode, - from, - } => { - CopyPlan::IntoStage { - stage, - path, - validation_mode, - // Make sure the subquery has been optimized. - from: Box::new(optimize(ctx, opt_ctx, *from)?), - } - } - CopyPlan::NoFileToCopy => *v, - - CopyPlan::IntoTable(mut into_table) => { - into_table.enable_distributed = opt_ctx.config.enable_distributed_optimization - && ctx.get_settings().get_enable_distributed_copy()?; - info!( - "after optimization enable_distributed_copy? : {}", - into_table.enable_distributed - ); - CopyPlan::IntoTable(into_table) - } - }))) + Plan::CopyIntoLocation(CopyIntoLocationPlan { stage, path, from }) => { + Ok(Plan::CopyIntoLocation(CopyIntoLocationPlan { + stage, + path, + from: Box::new(optimize(ctx, opt_ctx, *from)?), + })) + } + Plan::CopyIntoTable(mut plan) if !plan.no_file_to_copy => { + plan.enable_distributed = opt_ctx.config.enable_distributed_optimization + && ctx.get_settings().get_enable_distributed_copy()?; + info!( + "after optimization enable_distributed_copy? : {}", + plan.enable_distributed + ); + Ok(Plan::CopyIntoTable(plan)) } // Passthrough statements. _ => Ok(plan), diff --git a/src/query/sql/src/planner/planner.rs b/src/query/sql/src/planner/planner.rs index a442dc0d70f7..bb8a4f7ef754 100644 --- a/src/query/sql/src/planner/planner.rs +++ b/src/query/sql/src/planner/planner.rs @@ -93,9 +93,10 @@ impl Planner { // Step 2: Parse the SQL. let (mut stmt, format) = parse_sql(&tokens, sql_dialect)?; - if matches!(stmt, Statement::Copy(_)) { + if matches!(stmt, Statement::CopyIntoLocation(_)) { // Indicate binder there is no need to collect column statistics for the binding table. - self.ctx.attach_query_str(QueryKind::Copy, String::new()); + self.ctx + .attach_query_str(QueryKind::CopyIntoTable, String::new()); } self.replace_stmt(&mut stmt, sql_dialect); diff --git a/src/query/sql/src/planner/plans/copy_into_location.rs b/src/query/sql/src/planner/plans/copy_into_location.rs new file mode 100644 index 000000000000..b4b1ef282036 --- /dev/null +++ b/src/query/sql/src/planner/plans/copy_into_location.rs @@ -0,0 +1,38 @@ +// Copyright 2021 Datafuse Labs +// +// 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. + +use std::fmt::Debug; +use std::fmt::Formatter; + +use common_meta_app::principal::StageInfo; + +use crate::plans::Plan; + +#[derive(Clone)] +pub struct CopyIntoLocationPlan { + pub stage: Box, + pub path: String, + pub from: Box, +} + +impl Debug for CopyIntoLocationPlan { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!( + f, + "Copy into {:?}/{} from {:?}", + self.stage, self.path, self.from + )?; + Ok(()) + } +} diff --git a/src/query/sql/src/planner/plans/copy.rs b/src/query/sql/src/planner/plans/copy_into_table.rs similarity index 84% rename from src/query/sql/src/planner/plans/copy.rs rename to src/query/sql/src/planner/plans/copy_into_table.rs index 870b522b0a81..a5a786a53d0c 100644 --- a/src/query/sql/src/planner/plans/copy.rs +++ b/src/query/sql/src/planner/plans/copy_into_table.rs @@ -28,8 +28,6 @@ use common_expression::DataSchema; use common_expression::DataSchemaRef; use common_expression::DataSchemaRefExt; use common_expression::Scalar; -use common_meta_app::principal::CopyOptions; -use common_meta_app::principal::StageInfo; use common_meta_app::schema::CatalogInfo; use common_storage::init_stage_operator; use common_storage::metrics::copy::metrics_inc_collect_files_get_all_source_files_milliseconds; @@ -87,6 +85,8 @@ impl CopyIntoTableMode { #[derive(Clone)] pub struct CopyIntoTablePlan { + pub no_file_to_copy: bool, + pub catalog_info: CatalogInfo, pub database_name: String, pub table_name: String, @@ -201,6 +201,7 @@ impl Debug for CopyIntoTablePlan { catalog_info, database_name, table_name, + no_file_to_copy, validation_mode, force, stage_table_info, @@ -212,28 +213,19 @@ impl Debug for CopyIntoTablePlan { "Copy into {:}.{database_name:}.{table_name:}", catalog_info.catalog_name() )?; + write!(f, ", no_file_to_copy: {no_file_to_copy:?}")?; write!(f, ", validation_mode: {validation_mode:?}")?; write!(f, ", from: {stage_table_info:?}")?; write!(f, " force: {force}")?; + write!(f, " is_from: {force}")?; write!(f, " query: {query:?}")?; Ok(()) } } /// CopyPlan supports CopyIntoTable & CopyIntoStage -#[derive(Clone)] -pub enum CopyPlan { - NoFileToCopy, - IntoTable(CopyIntoTablePlan), - IntoStage { - stage: Box, - path: String, - validation_mode: ValidationMode, - from: Box, - }, -} -impl CopyPlan { +impl CopyIntoTablePlan { fn copy_into_table_schema() -> DataSchemaRef { DataSchemaRefExt::create(vec![ DataField::new("File", DataType::String), @@ -250,43 +242,11 @@ impl CopyPlan { ]) } - pub fn copy_into_table_options(&self) -> Option { - match self { - CopyPlan::IntoTable(p) => Some(p.stage_table_info.stage_info.copy_options.clone()), - _ => None, - } - } - pub fn schema(&self) -> DataSchemaRef { - match self { - CopyPlan::NoFileToCopy => Self::copy_into_table_schema(), - CopyPlan::IntoTable(p) if !p.from_attachment => Self::copy_into_table_schema(), - _ => Arc::new(DataSchema::empty()), - } - } -} - -impl Debug for CopyPlan { - // Ignore the schema. - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - match &self { - CopyPlan::IntoTable(plan) => { - write!(f, "{plan:?}")?; - } - CopyPlan::IntoStage { - stage, - path, - validation_mode, - .. - } => { - write!(f, "Copy into {stage:?}")?; - write!(f, ", path: {path:?}")?; - write!(f, ", validation_mode: {validation_mode:?}")?; - } - CopyPlan::NoFileToCopy => { - write!(f, "No file to copy")?; - } + if self.from_attachment { + Arc::new(DataSchema::empty()) + } else { + Self::copy_into_table_schema() } - Ok(()) } } diff --git a/src/query/sql/src/planner/plans/mod.rs b/src/query/sql/src/planner/plans/mod.rs index 2cac567cdd20..f6968964ba5f 100644 --- a/src/query/sql/src/planner/plans/mod.rs +++ b/src/query/sql/src/planner/plans/mod.rs @@ -15,7 +15,7 @@ mod aggregate; mod call; mod constant_table_scan; -mod copy; +mod copy_into_table; mod cte_scan; pub mod data_mask; mod ddl; @@ -32,6 +32,7 @@ mod limit; mod materialized_cte; mod merge_into; +mod copy_into_location; pub mod operator; mod pattern; mod plan; @@ -49,10 +50,12 @@ mod sort; mod union_all; mod update; mod window; + pub use aggregate::*; pub use call::CallPlan; pub use constant_table_scan::ConstantTableScan; -pub use copy::*; +pub use copy_into_location::*; +pub use copy_into_table::*; pub use cte_scan::CteScan; pub use data_mask::*; pub use ddl::*; diff --git a/src/query/sql/src/planner/plans/plan.rs b/src/query/sql/src/planner/plans/plan.rs index 9bdba2ab7952..42c1d7e3e5ca 100644 --- a/src/query/sql/src/planner/plans/plan.rs +++ b/src/query/sql/src/planner/plans/plan.rs @@ -14,7 +14,6 @@ use std::fmt::Display; use std::fmt::Formatter; -use std::ops::Deref; use std::sync::Arc; use common_ast::ast::ExplainKind; @@ -26,6 +25,7 @@ use common_expression::DataSchemaRef; use common_expression::DataSchemaRefExt; use crate::optimizer::SExpr; +use crate::plans::copy_into_location::CopyIntoLocationPlan; use crate::plans::AddTableColumnPlan; use crate::plans::AlterNetworkPolicyPlan; use crate::plans::AlterShareTenantsPlan; @@ -36,7 +36,7 @@ use crate::plans::AlterViewPlan; use crate::plans::AlterVirtualColumnPlan; use crate::plans::AnalyzeTablePlan; use crate::plans::CopyIntoTableMode; -use crate::plans::CopyPlan; +use crate::plans::CopyIntoTablePlan; use crate::plans::CreateCatalogPlan; use crate::plans::CreateDatabasePlan; use crate::plans::CreateDatamaskPolicyPlan; @@ -148,8 +148,8 @@ pub enum Plan { plan: Box, }, - // Copy - Copy(Box), + CopyIntoTable(CopyIntoTablePlan), + CopyIntoLocation(CopyIntoLocationPlan), // Call is rewrite into Query // Call(Box), @@ -308,12 +308,9 @@ impl Plan { pub fn kind(&self) -> QueryKind { match self { Plan::Query { .. } => QueryKind::Query, - Plan::Copy(plan) => match plan.deref() { - CopyPlan::IntoTable(copy_plan) => match copy_plan.write_mode { - CopyIntoTableMode::Insert { .. } => QueryKind::Insert, - _ => QueryKind::Copy, - }, - _ => QueryKind::Copy, + Plan::CopyIntoTable(copy_plan) => match copy_plan.write_mode { + CopyIntoTableMode::Insert { .. } => QueryKind::Insert, + _ => QueryKind::CopyIntoTable, }, Plan::Explain { .. } | Plan::ExplainAnalyze { .. } @@ -378,7 +375,7 @@ impl Plan { Plan::DropNetworkPolicy(plan) => plan.schema(), Plan::DescNetworkPolicy(plan) => plan.schema(), Plan::ShowNetworkPolicies(plan) => plan.schema(), - Plan::Copy(plan) => plan.schema(), + Plan::CopyIntoTable(plan) => plan.schema(), other => { debug_assert!(!other.has_result_set()); Arc::new(DataSchema::empty()) @@ -412,7 +409,7 @@ impl Plan { | Plan::DescDatamaskPolicy(_) | Plan::DescNetworkPolicy(_) | Plan::ShowNetworkPolicies(_) - | Plan::Copy(_) + | Plan::CopyIntoTable(_) ) } } diff --git a/src/query/storages/parquet/src/parquet2/parquet_table/partition.rs b/src/query/storages/parquet/src/parquet2/parquet_table/partition.rs index e31c17e630d7..ddf90eae055f 100644 --- a/src/query/storages/parquet/src/parquet2/parquet_table/partition.rs +++ b/src/query/storages/parquet/src/parquet2/parquet_table/partition.rs @@ -172,7 +172,7 @@ impl Parquet2Table { &file_locations, ctx.get_settings().get_max_threads()? as usize, &ctx.get_copy_status(), - matches!(ctx.get_query_kind(), QueryKind::Copy), + matches!(ctx.get_query_kind(), QueryKind::CopyIntoTable), ) .await } diff --git a/src/query/storages/parquet/src/parquet2/processors/deserialize_transform.rs b/src/query/storages/parquet/src/parquet2/processors/deserialize_transform.rs index b2f2ba80cd88..871252e103f1 100644 --- a/src/query/storages/parquet/src/parquet2/processors/deserialize_transform.rs +++ b/src/query/storages/parquet/src/parquet2/processors/deserialize_transform.rs @@ -128,7 +128,7 @@ impl Parquet2DeserializeTransform { remain_reader, partition_pruner, - is_copy: matches!(ctx.get_query_kind(), QueryKind::Copy), + is_copy: matches!(ctx.get_query_kind(), QueryKind::CopyIntoTable), copy_status: ctx.get_copy_status(), }, ))) diff --git a/src/query/storages/parquet/src/parquet_rs/parquet_table/partition.rs b/src/query/storages/parquet/src/parquet_rs/parquet_table/partition.rs index 9a52b90c99e6..ab8d4d36c113 100644 --- a/src/query/storages/parquet/src/parquet_rs/parquet_table/partition.rs +++ b/src/query/storages/parquet/src/parquet_rs/parquet_table/partition.rs @@ -99,7 +99,7 @@ impl ParquetRSTable { self.read_options, )?); - let copy_status = if matches!(ctx.get_query_kind(), QueryKind::Copy) { + let copy_status = if matches!(ctx.get_query_kind(), QueryKind::CopyIntoTable) { Some(ctx.get_copy_status()) } else { None diff --git a/src/query/storages/parquet/src/parquet_rs/parquet_table/table.rs b/src/query/storages/parquet/src/parquet_rs/parquet_table/table.rs index cc73cd19938f..572c1d725a02 100644 --- a/src/query/storages/parquet/src/parquet_rs/parquet_table/table.rs +++ b/src/query/storages/parquet/src/parquet_rs/parquet_table/table.rs @@ -137,7 +137,7 @@ impl ParquetRSTable { // If the query is `COPY`, we don't need to collect column statistics. // It's because the only transform could be contained in `COPY` command is projection. - let need_stats_provider = !matches!(ctx.get_query_kind(), QueryKind::Copy); + let need_stats_provider = !matches!(ctx.get_query_kind(), QueryKind::CopyIntoTable); let settings = ctx.get_settings(); let max_threads = settings.get_max_threads()? as usize; let max_memory_usage = settings.get_max_memory_usage()?; diff --git a/src/query/storages/parquet/src/parquet_rs/source.rs b/src/query/storages/parquet/src/parquet_rs/source.rs index 443242ae1e43..99a046959a39 100644 --- a/src/query/storages/parquet/src/parquet_rs/source.rs +++ b/src/query/storages/parquet/src/parquet_rs/source.rs @@ -75,7 +75,7 @@ impl ParquetSource { topk: Arc>, ) -> Result { let scan_progress = ctx.get_scan_progress(); - let is_copy = matches!(ctx.get_query_kind(), QueryKind::Copy); + let is_copy = matches!(ctx.get_query_kind(), QueryKind::CopyIntoTable); let copy_status = ctx.get_copy_status(); let topk_sorter = topk diff --git a/tests/sqllogictests/suites/mode/standalone/explain/explain.test b/tests/sqllogictests/suites/mode/standalone/explain/explain.test index 99e0116e013b..6a3bff0473b1 100644 --- a/tests/sqllogictests/suites/mode/standalone/explain/explain.test +++ b/tests/sqllogictests/suites/mode/standalone/explain/explain.test @@ -219,10 +219,10 @@ DISABLE_VARIANT_CHECK = false skipif clickhouse query T -explain syntax copy into 's3://mybucket/data.csv' from t1 file_format = ( type = CSV field_delimiter = ',' record_delimiter = '\n' skip_header = 1) size_limit=10 +explain syntax copy into 's3://mybucket/data.csv' from t1 file_format = ( type = CSV field_delimiter = ',' record_delimiter = '\n' skip_header = 1) ---- COPY -INTO 's3://mybucket/data.csv' +INTO Uri(UriLocation { protocol: "s3", name: "mybucket", path: "/data.csv", part_prefix: "", connection: Connection { visited_keys: {}, conns: {} } }) FROM t1 FILE_FORMAT = ( field_delimiter = ",", @@ -230,9 +230,7 @@ FILE_FORMAT = ( skip_header = "1", type = "CSV" ) -SIZE_LIMIT = 10 -PURGE = false -DISABLE_VARIANT_CHECK = false +SINGLE = false query T explain syntax create table t3(a int64, b uint64, c float64, d string, e array(int32), f tuple(f1 bool, f2 string)) engine=fuse cluster by (a, b, c) comment='test' compression='LZ4' @@ -461,11 +459,11 @@ skipif clickhouse query T explain ast copy into t1 from 's3://mybucket/data.csv' file_format = ( type = CSV field_delimiter = ',' record_delimiter = '\n' skip_header = 1) size_limit=10 max_files=10 ---- -Copy (children 7) -├── CopyUnit (children 1) -│ └── Location 's3://mybucket/data.csv' -├── CopyUnit (children 1) +CopyIntoTable (children 7) +├── TO (children 1) │ └── TableIdentifier t1 +├── FROM (children 1) +│ └── Location 's3://mybucket/data.csv' ├── FileFormats (children 4) │ ├── FileFormat field_delimiter = "," │ ├── FileFormat record_delimiter = "\n" diff --git a/tests/sqllogictests/suites/mode/standalone/explain_native/explain.test b/tests/sqllogictests/suites/mode/standalone/explain_native/explain.test index 76e85b3119d3..0a9cbd5809df 100644 --- a/tests/sqllogictests/suites/mode/standalone/explain_native/explain.test +++ b/tests/sqllogictests/suites/mode/standalone/explain_native/explain.test @@ -207,10 +207,10 @@ DISABLE_VARIANT_CHECK = false skipif clickhouse query T -explain syntax copy into 's3://mybucket/data.csv' from t1 file_format = ( type = CSV field_delimiter = ',' record_delimiter = '\n' skip_header = 1) size_limit=10 +explain syntax copy into 's3://mybucket/data.csv' from t1 file_format = ( type = CSV field_delimiter = ',' record_delimiter = '\n' skip_header = 1) ---- COPY -INTO 's3://mybucket/data.csv' +INTO Uri(UriLocation { protocol: "s3", name: "mybucket", path: "/data.csv", part_prefix: "", connection: Connection { visited_keys: {}, conns: {} } }) FROM t1 FILE_FORMAT = ( field_delimiter = ",", @@ -218,9 +218,7 @@ FILE_FORMAT = ( skip_header = "1", type = "CSV" ) -SIZE_LIMIT = 10 -PURGE = false -DISABLE_VARIANT_CHECK = false +SINGLE = false query T explain syntax create table t3(a int64, b uint64, c float64, d string, e array(int32), f tuple(f1 bool, f2 string)) engine=fuse cluster by (a, b, c) comment='test' compression='LZ4' @@ -449,11 +447,11 @@ skipif clickhouse query T explain ast copy into t1 from 's3://mybucket/data.csv' file_format = ( type = CSV field_delimiter = ',' record_delimiter = '\n' skip_header = 1) size_limit=10 max_files=10 ---- -Copy (children 7) -├── CopyUnit (children 1) -│ └── Location 's3://mybucket/data.csv' -├── CopyUnit (children 1) +CopyIntoTable (children 7) +├── TO (children 1) │ └── TableIdentifier t1 +├── FROM (children 1) +│ └── Location 's3://mybucket/data.csv' ├── FileFormats (children 4) │ ├── FileFormat field_delimiter = "," │ ├── FileFormat record_delimiter = "\n" diff --git a/tests/suites/1_stateful/05_formats/05_00_00_named_format.sh b/tests/suites/1_stateful/05_formats/05_00_00_named_format.sh index 2d80cc791987..722ed8cb6d7f 100755 --- a/tests/suites/1_stateful/05_formats/05_00_00_named_format.sh +++ b/tests/suites/1_stateful/05_formats/05_00_00_named_format.sh @@ -47,7 +47,7 @@ echo "select count(*) from table_parquet" | $MYSQL_CLIENT_CONNECT # test copy from table echo "---copy from table" -echo "copy into @stage_05_00_00 from table_csv FILE_FORMAT = ( FORMAT_NAME = 'my_csv') pattern = '.*csv' ;" | $MYSQL_CLIENT_CONNECT +echo "copy into @stage_05_00_00 from table_csv FILE_FORMAT = ( FORMAT_NAME = 'my_csv');" | $MYSQL_CLIENT_CONNECT cat $DATADIR_PATH/*.csv | wc -l | sed 's/ //g' # test select stage From 4071436a961b0dd0eb965b656caf60ebd49e1693 Mon Sep 17 00:00:00 2001 From: TCeason <33082201+TCeason@users.noreply.github.com> Date: Tue, 10 Oct 2023 21:42:57 +0800 Subject: [PATCH 09/25] feat(query): add func: to_week_of_year (#13173) --- src/query/expression/src/utils/date_helper.rs | 8 +++ src/query/functions/src/scalars/datetime.rs | 14 +++++ .../functions/tests/it/scalars/datetime.rs | 10 +++ .../tests/it/scalars/testdata/datetime.txt | 62 +++++++++++++++++++ .../it/scalars/testdata/function_list.txt | 4 ++ .../02_function/02_0012_function_datetimes | 15 +++++ 6 files changed, 113 insertions(+) diff --git a/src/query/expression/src/utils/date_helper.rs b/src/query/expression/src/utils/date_helper.rs index 0722f0097f9c..8362e4d89d36 100644 --- a/src/query/expression/src/utils/date_helper.rs +++ b/src/query/expression/src/utils/date_helper.rs @@ -427,12 +427,20 @@ pub struct ToMinute; pub struct ToSecond; pub struct ToUnixTimestamp; +pub struct ToWeekOfYear; + impl ToNumber for ToYYYYMM { fn to_number(dt: &DateTime) -> u32 { dt.year() as u32 * 100 + dt.month() } } +impl ToNumber for ToWeekOfYear { + fn to_number(dt: &DateTime) -> u32 { + dt.iso_week().week() + } +} + impl ToNumber for ToYYYYMMDD { fn to_number(dt: &DateTime) -> u32 { dt.year() as u32 * 10_000 + dt.month() * 100 + dt.day() diff --git a/src/query/functions/src/scalars/datetime.rs b/src/query/functions/src/scalars/datetime.rs index 6d8a25f63f98..c822f4c11063 100644 --- a/src/query/functions/src/scalars/datetime.rs +++ b/src/query/functions/src/scalars/datetime.rs @@ -943,6 +943,13 @@ fn register_to_number_functions(registry: &mut FunctionRegistry) { ToNumberImpl::eval_date::(val, ctx.func_ctx.tz) }), ); + registry.register_passthrough_nullable_1_arg::( + "to_week_of_year", + |_, _| FunctionDomain::Full, + vectorize_1_arg::(|val, ctx| { + ToNumberImpl::eval_date::(val, ctx.func_ctx.tz) + }), + ); // timestamp registry.register_passthrough_nullable_1_arg::( "to_yyyymm", @@ -1014,6 +1021,13 @@ fn register_to_number_functions(registry: &mut FunctionRegistry) { ToNumberImpl::eval_timestamp::(val, ctx.func_ctx.tz) }), ); + registry.register_passthrough_nullable_1_arg::( + "to_week_of_year", + |_, _| FunctionDomain::Full, + vectorize_1_arg::(|val, ctx| { + ToNumberImpl::eval_timestamp::(val, ctx.func_ctx.tz) + }), + ); registry.register_passthrough_nullable_1_arg::( "to_unix_timestamp", |_, _| FunctionDomain::Full, diff --git a/src/query/functions/tests/it/scalars/datetime.rs b/src/query/functions/tests/it/scalars/datetime.rs index 8f5fb8ccc028..6ee5e07d902b 100644 --- a/src/query/functions/tests/it/scalars/datetime.rs +++ b/src/query/functions/tests/it/scalars/datetime.rs @@ -475,6 +475,7 @@ fn test_to_number(file: &mut impl Write) { run_ast(file, "to_day_of_year(to_date(18875))", &[]); run_ast(file, "to_day_of_month(to_date(18875))", &[]); run_ast(file, "to_day_of_week(to_date(18875))", &[]); + run_ast(file, "to_week_of_year(to_date(18875))", &[]); run_ast(file, "to_yyyymm(a)", &[( "a", DateType::from_data(vec![-100, 0, 100]), @@ -511,6 +512,10 @@ fn test_to_number(file: &mut impl Write) { "a", DateType::from_data(vec![-100, 0, 100]), )]); + run_ast(file, "to_week_of_year(a)", &[( + "a", + DateType::from_data(vec![-100, 0, 100]), + )]); // timestamp run_ast(file, "to_yyyymm(to_timestamp(1630812366))", &[]); @@ -522,6 +527,7 @@ fn test_to_number(file: &mut impl Write) { run_ast(file, "to_day_of_year(to_timestamp(1630812366))", &[]); run_ast(file, "to_day_of_month(to_timestamp(1630812366))", &[]); run_ast(file, "to_day_of_week(to_timestamp(1630812366))", &[]); + run_ast(file, "to_week_of_year(to_timestamp(1630812366))", &[]); run_ast(file, "to_hour(to_timestamp(1630812366))", &[]); run_ast(file, "to_minute(to_timestamp(1630812366))", &[]); run_ast(file, "to_second(to_timestamp(1630812366))", &[]); @@ -561,6 +567,10 @@ fn test_to_number(file: &mut impl Write) { "a", TimestampType::from_data(vec![-100, 0, 100]), )]); + run_ast(file, "to_week_of_year(a)", &[( + "a", + TimestampType::from_data(vec![-100, 0, 100]), + )]); run_ast(file, "to_hour(a)", &[( "a", TimestampType::from_data(vec![-100, 0, 100]), diff --git a/src/query/functions/tests/it/scalars/testdata/datetime.txt b/src/query/functions/tests/it/scalars/testdata/datetime.txt index 2b42bc1f3d04..ed0c1124abe2 100644 --- a/src/query/functions/tests/it/scalars/testdata/datetime.txt +++ b/src/query/functions/tests/it/scalars/testdata/datetime.txt @@ -2620,6 +2620,15 @@ output domain : {7..=7} output : 7 +ast : to_week_of_year(to_date(18875)) +raw expr : to_week_of_year(to_date(18875)) +checked expr : to_week_of_year(to_date(to_int64(18875_u16))) +optimized expr : 35_u32 +output type : UInt32 +output domain : {35..=35} +output : 35 + + ast : to_yyyymm(a) raw expr : to_yyyymm(a::Date) checked expr : to_yyyymm(a) @@ -2818,6 +2827,28 @@ evaluation (internal): +--------+------------------+ +ast : to_week_of_year(a) +raw expr : to_week_of_year(a::Date) +checked expr : to_week_of_year(a) +evaluation: ++--------+--------------+------------------+ +| | a | Output | ++--------+--------------+------------------+ +| Type | Date | UInt32 | +| Domain | {-100..=100} | {0..=4294967295} | +| Row 0 | '1969-09-23' | 39 | +| Row 1 | '1970-01-01' | 1 | +| Row 2 | '1970-04-11' | 15 | ++--------+--------------+------------------+ +evaluation (internal): ++--------+---------------------+ +| Column | Data | ++--------+---------------------+ +| a | [-100, 0, 100] | +| Output | UInt32([39, 1, 15]) | ++--------+---------------------+ + + ast : to_yyyymm(to_timestamp(1630812366)) raw expr : to_yyyymm(to_timestamp(1630812366)) checked expr : to_yyyymm(to_timestamp(to_int64(1630812366_u32))) @@ -2899,6 +2930,15 @@ output domain : {7..=7} output : 7 +ast : to_week_of_year(to_timestamp(1630812366)) +raw expr : to_week_of_year(to_timestamp(1630812366)) +checked expr : to_week_of_year(to_timestamp(to_int64(1630812366_u32))) +optimized expr : 35_u32 +output type : UInt32 +output domain : {35..=35} +output : 35 + + ast : to_hour(to_timestamp(1630812366)) raw expr : to_hour(to_timestamp(1630812366)) checked expr : to_hour(to_timestamp(to_int64(1630812366_u32))) @@ -3124,6 +3164,28 @@ evaluation (internal): +--------+------------------+ +ast : to_week_of_year(a) +raw expr : to_week_of_year(a::Timestamp) +checked expr : to_week_of_year(a) +evaluation: ++--------+------------------------------+------------------+ +| | a | Output | ++--------+------------------------------+------------------+ +| Type | Timestamp | UInt32 | +| Domain | {-100..=100} | {0..=4294967295} | +| Row 0 | '1969-12-31 23:59:59.999900' | 1 | +| Row 1 | '1970-01-01 00:00:00.000000' | 1 | +| Row 2 | '1970-01-01 00:00:00.000100' | 1 | ++--------+------------------------------+------------------+ +evaluation (internal): ++--------+-------------------+ +| Column | Data | ++--------+-------------------+ +| a | [-100, 0, 100] | +| Output | UInt32([1, 1, 1]) | ++--------+-------------------+ + + ast : to_hour(a) raw expr : to_hour(a::Timestamp) checked expr : to_hour(a) diff --git a/src/query/functions/tests/it/scalars/testdata/function_list.txt b/src/query/functions/tests/it/scalars/testdata/function_list.txt index 046f986f3510..63b3d85b79c7 100644 --- a/src/query/functions/tests/it/scalars/testdata/function_list.txt +++ b/src/query/functions/tests/it/scalars/testdata/function_list.txt @@ -3567,6 +3567,10 @@ Functions overloads: 1 to_unix_timestamp(Timestamp NULL) :: Int64 NULL 0 to_variant(T0) :: Variant 1 to_variant(T0 NULL) :: Variant NULL +0 to_week_of_year(Date) :: UInt32 +1 to_week_of_year(Date NULL) :: UInt32 NULL +2 to_week_of_year(Timestamp) :: UInt32 +3 to_week_of_year(Timestamp NULL) :: UInt32 NULL 0 to_year(Date) :: UInt16 1 to_year(Date NULL) :: UInt16 NULL 2 to_year(Timestamp) :: UInt16 diff --git a/tests/sqllogictests/suites/query/02_function/02_0012_function_datetimes b/tests/sqllogictests/suites/query/02_function/02_0012_function_datetimes index 45ea602d715e..646c3355004e 100644 --- a/tests/sqllogictests/suites/query/02_function/02_0012_function_datetimes +++ b/tests/sqllogictests/suites/query/02_function/02_0012_function_datetimes @@ -993,3 +993,18 @@ query T select to_timestamp('2022年02月04日,8时58分59秒,时区:+0800', '%Y年%m月%d日,%H时%M分%S秒,时区:%z'); ---- 2022-02-04 00:58:59.000000 + +query I +select to_week_of_year('2017-01-01'); +---- +52 + +query I +SELECT to_week_of_year('1900-12-31 23:59:59.999900'); +---- +1 + +query I +SELECT to_week_of_year('2016-01-02T23:39:20.123-07:00'); +---- +53 From 2cd332b2b7095934b30b545b3b2f230be6c2e28f Mon Sep 17 00:00:00 2001 From: everpcpc Date: Tue, 10 Oct 2023 23:09:54 +0800 Subject: [PATCH 10/25] chore(ci): add job ready for merge check (#13182) --- .github/workflows/dev.yml | 10 ++++++++-- .github/workflows/pr.yml | 1 + .github/workflows/release.yml | 3 +-- .github/workflows/reuse.linux.yml | 8 ++++---- .../ee/tests/it/aggregating_index/index_refresh.rs | 1 + src/query/ee/tests/it/aggregating_index/index_scan.rs | 7 +++++++ src/query/functions/tests/it/scalars/mod.rs | 2 ++ .../tests/it/servers/http/http_query_handlers.rs | 2 ++ 8 files changed, 26 insertions(+), 8 deletions(-) diff --git a/.github/workflows/dev.yml b/.github/workflows/dev.yml index 33f98cc828e6..c96474bd4cf1 100644 --- a/.github/workflows/dev.yml +++ b/.github/workflows/dev.yml @@ -16,8 +16,6 @@ on: - "scripts/setup/**" - ".devcontainer/**" merge_group: - types: - - checks_requested concurrency: group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }} @@ -37,3 +35,11 @@ jobs: with: build_profile: debug runner_provider: gcp + + ready: + runs-on: ubuntu-latest + needs: + - linux + - linux_hive + steps: + - run: echo "ready" diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 4f3a94f1c6dc..9cc849c7bac6 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -9,6 +9,7 @@ on: - edited - ready_for_review - converted_to_draft + merge_group: permissions: pull-requests: write diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f2bcfe933108..248f60c46b41 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -5,8 +5,7 @@ on: tags: - "v*" schedule: - # Release at 00:00 UTC+8 - - cron: "0 16 * * *" + - cron: "0 22 * * *" workflow_dispatch: inputs: tags: diff --git a/.github/workflows/reuse.linux.yml b/.github/workflows/reuse.linux.yml index 32e290cd19a3..0a46aed26700 100644 --- a/.github/workflows/reuse.linux.yml +++ b/.github/workflows/reuse.linux.yml @@ -170,7 +170,6 @@ jobs: - "standalone" - "crdb" - "duckdb" - - "tpch" steps: - uses: actions/checkout@v4 - uses: ./.github/actions/test_sqllogic_standalone_linux @@ -222,6 +221,7 @@ jobs: - "base" - "ydb" - "tpcds" + - "tpch" format: - "parquet" - "native" @@ -280,10 +280,8 @@ jobs: dirs: - "base" - "query" - - "cluster" - "crdb" - "duckdb" - - "tpch" steps: - uses: actions/checkout@v4 - uses: ./.github/actions/test_sqllogic_cluster_linux @@ -299,13 +297,15 @@ jobs: sqllogic_cluster_large: name: sqllogic_cluster_${{ matrix.dirs }} - runs-on: [self-hosted, X64, Linux, 4c8g, "${{ inputs.runner_provider }}"] + runs-on: [self-hosted, X64, Linux, 8c16g, "${{ inputs.runner_provider }}"] needs: [build, check] strategy: matrix: dirs: - "ydb" - "tpcds" + - "tpch" + - "cluster" steps: - uses: actions/checkout@v4 - uses: ./.github/actions/test_sqllogic_cluster_linux diff --git a/src/query/ee/tests/it/aggregating_index/index_refresh.rs b/src/query/ee/tests/it/aggregating_index/index_refresh.rs index d8a538754228..36ec168fcf8a 100644 --- a/src/query/ee/tests/it/aggregating_index/index_refresh.rs +++ b/src/query/ee/tests/it/aggregating_index/index_refresh.rs @@ -305,6 +305,7 @@ async fn test_refresh_agg_index_with_limit() -> Result<()> { Ok(()) } +#[ignore = "flaky"] #[tokio::test(flavor = "multi_thread")] async fn test_sync_agg_index() -> Result<()> { let (_guard, ctx, root) = create_ee_query_context(None).await.unwrap(); diff --git a/src/query/ee/tests/it/aggregating_index/index_scan.rs b/src/query/ee/tests/it/aggregating_index/index_scan.rs index 9316863ac46e..14192d39bb90 100644 --- a/src/query/ee/tests/it/aggregating_index/index_scan.rs +++ b/src/query/ee/tests/it/aggregating_index/index_scan.rs @@ -32,18 +32,21 @@ use databend_query::test_kits::TestFixture; use enterprise_query::test_kits::context::create_ee_query_context; use futures_util::TryStreamExt; +#[ignore = "flaky"] #[tokio::test(flavor = "multi_thread")] async fn test_index_scan() -> Result<()> { test_index_scan_impl("parquet").await?; test_index_scan_impl("native").await } +#[ignore = "flaky"] #[tokio::test(flavor = "multi_thread")] async fn test_index_scan_two_agg_funcs() -> Result<()> { test_index_scan_two_agg_funcs_impl("parquet").await?; test_index_scan_two_agg_funcs_impl("native").await } +#[ignore = "flaky"] #[tokio::test(flavor = "multi_thread")] async fn test_projected_index_scan() -> Result<()> { test_projected_index_scan_impl("parquet").await?; @@ -56,12 +59,14 @@ async fn test_index_scan_with_count() -> Result<()> { test_index_scan_with_count_impl("native").await } +#[ignore = "flaky"] #[tokio::test(flavor = "multi_thread")] async fn test_index_scan_agg_args_are_expression() -> Result<()> { test_index_scan_agg_args_are_expression_impl("parquet").await?; test_index_scan_agg_args_are_expression_impl("native").await } +#[ignore = "flaky"] #[tokio::test(flavor = "multi_thread")] async fn test_fuzz() -> Result<()> { test_fuzz_impl("parquet").await?; @@ -260,6 +265,7 @@ async fn test_index_scan_impl(format: &str) -> Result<()> { Ok(()) } +#[ignore = "flaky"] async fn test_index_scan_two_agg_funcs_impl(format: &str) -> Result<()> { let (_guard, ctx, _) = create_ee_query_context(None).await.unwrap(); let fixture = TestFixture::new_with_ctx(_guard, ctx).await; @@ -400,6 +406,7 @@ async fn test_index_scan_two_agg_funcs_impl(format: &str) -> Result<()> { Ok(()) } +#[ignore = "flaky"] async fn test_projected_index_scan_impl(format: &str) -> Result<()> { let (_guard, ctx, _) = create_ee_query_context(None).await.unwrap(); let fixture = TestFixture::new_with_ctx(_guard, ctx).await; diff --git a/src/query/functions/tests/it/scalars/mod.rs b/src/query/functions/tests/it/scalars/mod.rs index b1f1e3cf182c..dc2e9bc79792 100644 --- a/src/query/functions/tests/it/scalars/mod.rs +++ b/src/query/functions/tests/it/scalars/mod.rs @@ -37,6 +37,8 @@ mod comparison; mod control; mod datetime; mod geo; +// NOTE:(everpcpc) result different on macos +#[cfg(not(target_os = "macos"))] mod geo_h3; mod hash; mod map; diff --git a/src/query/service/tests/it/servers/http/http_query_handlers.rs b/src/query/service/tests/it/servers/http/http_query_handlers.rs index 5ef40a3edde6..82212c9353b1 100644 --- a/src/query/service/tests/it/servers/http/http_query_handlers.rs +++ b/src/query/service/tests/it/servers/http/http_query_handlers.rs @@ -111,6 +111,7 @@ async fn check_final(ep: &EndpointType, final_uri: &str) -> Result<()> { Ok(()) } +#[ignore = "flaky"] #[tokio::test(flavor = "current_thread")] async fn test_simple_sql() -> Result<()> { let _guard = TestGlobalServices::setup(ConfigBuilder::create().build()).await?; @@ -179,6 +180,7 @@ async fn test_simple_sql() -> Result<()> { Ok(()) } +#[ignore = "flaky"] #[tokio::test(flavor = "current_thread")] async fn test_show_databases() -> Result<()> { let _guard = TestGlobalServices::setup(ConfigBuilder::create().build()).await?; From d8451a1bf226e57df8176d4aabab021706b35662 Mon Sep 17 00:00:00 2001 From: Yang Xiufeng Date: Tue, 10 Oct 2023 18:44:21 -0500 Subject: [PATCH 11/25] fix: CopyIntoTablePlan should in Box. (#13183) Co-authored-by: dantengsky --- src/query/service/src/interpreters/interpreter_factory.rs | 2 +- src/query/service/src/interpreters/interpreter_replace.rs | 2 +- src/query/sql/src/planner/binder/copy_into_table.rs | 6 +++--- src/query/sql/src/planner/plans/plan.rs | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/query/service/src/interpreters/interpreter_factory.rs b/src/query/service/src/interpreters/interpreter_factory.rs index c490f6eff1c1..ddff62dab4f6 100644 --- a/src/query/service/src/interpreters/interpreter_factory.rs +++ b/src/query/service/src/interpreters/interpreter_factory.rs @@ -105,7 +105,7 @@ impl InterpreterFactory { Plan::CopyIntoTable(copy_plan) => Ok(Arc::new(CopyIntoTableInterpreter::try_create( ctx, - copy_plan.clone(), + *copy_plan.clone(), )?)), Plan::CopyIntoLocation(copy_plan) => Ok(Arc::new( CopyIntoLocationInterpreter::try_create(ctx, copy_plan.clone())?, diff --git a/src/query/service/src/interpreters/interpreter_replace.rs b/src/query/service/src/interpreters/interpreter_replace.rs index a139edbd2a6c..7f47d751474c 100644 --- a/src/query/service/src/interpreters/interpreter_replace.rs +++ b/src/query/service/src/interpreters/interpreter_replace.rs @@ -285,7 +285,7 @@ impl ReplaceInterpreter { InsertInputSource::Stage(plan) => match *plan.clone() { Plan::CopyIntoTable(copy_plan) => { let interpreter = - CopyIntoTableInterpreter::try_create(ctx.clone(), copy_plan.clone())?; + CopyIntoTableInterpreter::try_create(ctx.clone(), *copy_plan.clone())?; let (physical_plan, files) = interpreter.build_physical_plan(©_plan).await?; *purge_info = Some((files, copy_plan.stage_table_info.stage_info.clone())); diff --git a/src/query/sql/src/planner/binder/copy_into_table.rs b/src/query/sql/src/planner/binder/copy_into_table.rs index a2393e4e56f0..5b23ba04198a 100644 --- a/src/query/sql/src/planner/binder/copy_into_table.rs +++ b/src/query/sql/src/planner/binder/copy_into_table.rs @@ -226,7 +226,7 @@ impl<'a> Binder { self.bind_copy_from_query_into_table(bind_ctx, plan, &select_list, &None) .await } else { - Ok(Plan::CopyIntoTable(plan)) + Ok(Plan::CopyIntoTable(Box::new(plan))) } } @@ -326,7 +326,7 @@ impl<'a> Binder { if need_copy_file_infos.is_empty() { plan.no_file_to_copy = true; - return Ok(Plan::CopyIntoTable(plan)); + return Ok(Plan::CopyIntoTable(Box::new(plan))); } plan.stage_table_info.files_to_copy = Some(need_copy_file_infos.clone()); @@ -371,7 +371,7 @@ impl<'a> Binder { ignore_result: false, formatted_ast: None, })); - Ok(Plan::CopyIntoTable(plan)) + Ok(Plan::CopyIntoTable(Box::new(plan))) } #[async_backtrace::framed] diff --git a/src/query/sql/src/planner/plans/plan.rs b/src/query/sql/src/planner/plans/plan.rs index 42c1d7e3e5ca..1a0a1586ca9d 100644 --- a/src/query/sql/src/planner/plans/plan.rs +++ b/src/query/sql/src/planner/plans/plan.rs @@ -148,7 +148,7 @@ pub enum Plan { plan: Box, }, - CopyIntoTable(CopyIntoTablePlan), + CopyIntoTable(Box), CopyIntoLocation(CopyIntoLocationPlan), // Call is rewrite into Query From f239699ef2272539bebe9d1bbc02ca9556936b4f Mon Sep 17 00:00:00 2001 From: everpcpc Date: Wed, 11 Oct 2023 08:47:33 +0800 Subject: [PATCH 12/25] chore(ci): fix pr check for merge group (#13186) --- .github/workflows/pr.yml | 31 +++++++++++++++++++++++-------- .github/workflows/reuse.linux.yml | 3 ++- scripts/setup/rust-tools.txt | 8 ++++---- 3 files changed, 29 insertions(+), 13 deletions(-) diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 9cc849c7bac6..7ceeee75290a 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -18,17 +18,23 @@ permissions: jobs: title: runs-on: ubuntu-latest - if: '!github.event.pull_request.draft' steps: - name: Check PR title if not sematic uses: actions/github-script@v6 + id: check with: script: | + if (!context.payload.pull_request) { + core.info('PR payload is null'); + core.setOutput('title', 'ignore'); + return; + } const title = context.payload.pull_request.title; const regex = /^(rfc|feat|fix|refactor|ci|docs|chore)(\([a-z0-9-]+\))?:/; const m = title.match(regex); if (!m) { - core.setFailed('PR title is not semantic'); + core.error('PR title is not semantic'); + core.setOutput('title', 'not-semantic'); return; } const prType = m[1]; @@ -64,15 +70,16 @@ jobs: issue_number: context.issue.number, labels: [label], }); + core.setOutput('title', 'semantic'); - name: Delete Comment - if: success() + if: steps.check.outputs.title == 'semantic' uses: everpcpc/comment-on-pr-action@v1 with: token: ${{ github.token }} identifier: 'pr-assistant-title' delete: true - name: Comment on PR - if: failure() + if: steps.check.outputs.title == 'not-semantic' uses: everpcpc/comment-on-pr-action@v1 with: token: ${{ github.token }} @@ -103,26 +110,34 @@ jobs: cla: runs-on: ubuntu-latest - if: '!github.event.pull_request.draft' steps: - name: Check CLA if not signed uses: actions/github-script@v6 + id: check with: script: | + if (!context.payload.pull_request) { + core.info('PR payload is null'); + core.setOutput('cla', 'ignore'); + return; + } const body = context.payload.pull_request.body; const regex = /I hereby agree to the terms of the CLA available at: https:\/\/databend\.rs\/dev\/policies\/cla\//; if (!regex.test(body)) { - core.setFailed('CLA is not signed'); + core.error('CLA is not signed'); + core.setOutput('cla', 'not-signed'); + } else { + core.setOutput('cla', 'signed'); } - name: Delete Comment - if: success() + if: steps.check.outputs.cla == 'signed' uses: everpcpc/comment-on-pr-action@v1 with: token: ${{ github.token }} identifier: 'pr-assistant-cla' delete: true - name: Comment on PR - if: failure() + if: steps.check.outputs.cla == 'not-signed' uses: everpcpc/comment-on-pr-action@v1 with: token: ${{ github.token }} diff --git a/.github/workflows/reuse.linux.yml b/.github/workflows/reuse.linux.yml index 0a46aed26700..808fa3b0cc82 100644 --- a/.github/workflows/reuse.linux.yml +++ b/.github/workflows/reuse.linux.yml @@ -304,7 +304,8 @@ jobs: dirs: - "ydb" - "tpcds" - - "tpch" + # FIXME: flaky + # - "tpch" - "cluster" steps: - uses: actions/checkout@v4 diff --git a/scripts/setup/rust-tools.txt b/scripts/setup/rust-tools.txt index 5beae5e2934c..8ee72068403b 100644 --- a/scripts/setup/rust-tools.txt +++ b/scripts/setup/rust-tools.txt @@ -1,5 +1,5 @@ -cargo-audit@0.17.6 -cargo-machete@0.5.0 -cargo-nextest@0.9.58 +cargo-audit@0.18.2 +cargo-machete@0.6.0 +cargo-nextest@0.9.59 taplo-cli@0.8.1 -typos-cli@1.16.3 +typos-cli@1.16.18 From 0729bbf1b5aaf7fd3ecb947584cb852bec8401ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E7=82=8E=E6=B3=BC?= Date: Wed, 11 Oct 2023 09:42:55 +0800 Subject: [PATCH 13/25] chore: minor refine to raft service impl (#13180) --- .../src/meta_service/meta_service_impl.rs | 108 +++++++++--------- src/meta/service/src/meta_service/raftmeta.rs | 4 +- src/meta/service/src/metrics/meta_metrics.rs | 8 ++ 3 files changed, 64 insertions(+), 56 deletions(-) diff --git a/src/meta/service/src/meta_service/meta_service_impl.rs b/src/meta/service/src/meta_service/meta_service_impl.rs index f19adddc3e0d..5da92e148349 100644 --- a/src/meta/service/src/meta_service/meta_service_impl.rs +++ b/src/meta/service/src/meta_service/meta_service_impl.rs @@ -15,6 +15,7 @@ //! Meta service impl a grpc server that serves both raft protocol: append_entries, vote and install_snapshot. //! It also serves RPC for user-data access. +use std::error::Error; use std::pin::Pin; use std::sync::Arc; use std::time::Instant; @@ -49,6 +50,35 @@ impl RaftServiceImpl { raft_metrics::network::incr_recv_bytes_from_peer(addr.to_string(), bytes); } } + + /// Parse tonic::Request and decode it into required type. + fn parse_req(request: tonic::Request) -> Result + where T: serde::de::DeserializeOwned { + let raft_req = request.into_inner(); + let req: T = serde_json::from_str(&raft_req.data).map_err(Self::invalid_arg)?; + Ok(req) + } + + /// Create an Ok response for raft API. + fn ok_response(d: D) -> Result, tonic::Status> + where D: serde::Serialize { + let data = serde_json::to_string(&d).expect("fail to serialize resp"); + let reply = RaftReply { + data, + error: "".to_string(), + }; + Ok(tonic::Response::new(reply)) + } + + /// Create a tonic::Status with invalid argument error. + fn invalid_arg(e: impl Error) -> tonic::Status { + tonic::Status::invalid_argument(e.to_string()) + } + + /// Create a tonic::Status with internal error. + fn internal_err(e: impl Error) -> tonic::Status { + tonic::Status::internal(e.to_string()) + } } #[async_trait::async_trait] @@ -58,17 +88,15 @@ impl RaftService for RaftServiceImpl { request: tonic::Request, ) -> Result, tonic::Status> { let root = common_tracing::start_trace_for_remote_request(func_name!(), &request); - async { - let req = request.into_inner(); - let forward_req: ForwardRequest = serde_json::from_str(&req.data) - .map_err(|x| tonic::Status::invalid_argument(x.to_string()))?; + async { + let forward_req: ForwardRequest = Self::parse_req(request)?; let res = self.meta_node.handle_forwardable_request(forward_req).await; - let raft_mes: RaftReply = res.into(); + let raft_reply: RaftReply = res.into(); - Ok(tonic::Response::new(raft_mes)) + Ok(tonic::Response::new(raft_reply)) } .in_span(root) .await @@ -79,26 +107,19 @@ impl RaftService for RaftServiceImpl { request: tonic::Request, ) -> Result, tonic::Status> { let root = common_tracing::start_trace_for_remote_request(func_name!(), &request); + async { self.incr_meta_metrics_recv_bytes_from_peer(&request); - let req = request.into_inner(); - let ae_req = serde_json::from_str(&req.data) - .map_err(|x| tonic::Status::internal(x.to_string()))?; + let ae_req = Self::parse_req(request)?; + let raft = &self.meta_node.raft; - let resp = self - .meta_node - .raft + let resp = raft .append_entries(ae_req) .await - .map_err(|x| tonic::Status::internal(x.to_string()))?; - let data = serde_json::to_string(&resp).expect("fail to serialize resp"); - let mes = RaftReply { - data, - error: "".to_string(), - }; + .map_err(Self::internal_err)?; - Ok(tonic::Response::new(mes)) + Self::ok_response(resp) } .in_span(root) .await @@ -109,6 +130,7 @@ impl RaftService for RaftServiceImpl { request: tonic::Request, ) -> Result, tonic::Status> { let root = common_tracing::start_trace_for_remote_request(func_name!(), &request); + async { let start = Instant::now(); let addr = if let Some(addr) = request.remote_addr() { @@ -119,39 +141,25 @@ impl RaftService for RaftServiceImpl { self.incr_meta_metrics_recv_bytes_from_peer(&request); raft_metrics::network::incr_snapshot_recv_inflights_from_peer(addr.clone(), 1); - let req = request.into_inner(); - let is_req = serde_json::from_str(&req.data) - .map_err(|x| tonic::Status::internal(x.to_string()))?; + let is_req = Self::parse_req(request)?; + let raft = &self.meta_node.raft; - let resp = self - .meta_node - .raft + let resp = raft .install_snapshot(is_req) .await - .map_err(|x| tonic::Status::internal(x.to_string())); + .map_err(Self::internal_err); raft_metrics::network::sample_snapshot_recv( addr.clone(), start.elapsed().as_secs() as f64, ); raft_metrics::network::incr_snapshot_recv_inflights_from_peer(addr.clone(), -1); + raft_metrics::network::incr_snapshot_recv_status_from_peer(addr.clone(), resp.is_ok()); match resp { - Ok(resp) => { - let data = serde_json::to_string(&resp).expect("fail to serialize resp"); - let mes = RaftReply { - data, - error: "".to_string(), - }; - - raft_metrics::network::incr_snapshot_recv_success_from_peer(addr.clone()); - Ok(tonic::Response::new(mes)) - } - Err(e) => { - raft_metrics::network::incr_snapshot_recv_failure_from_peer(addr.clone()); - Err(e) - } + Ok(resp) => Self::ok_response(resp), + Err(e) => Err(e), } } .in_span(root) @@ -163,26 +171,16 @@ impl RaftService for RaftServiceImpl { request: tonic::Request, ) -> Result, tonic::Status> { let root = common_tracing::start_trace_for_remote_request(func_name!(), &request); + async { self.incr_meta_metrics_recv_bytes_from_peer(&request); - let req = request.into_inner(); - let v_req = serde_json::from_str(&req.data) - .map_err(|x| tonic::Status::internal(x.to_string()))?; + let v_req = Self::parse_req(request)?; + let raft = &self.meta_node.raft; - let resp = self - .meta_node - .raft - .vote(v_req) - .await - .map_err(|x| tonic::Status::internal(x.to_string()))?; - let data = serde_json::to_string(&resp).expect("fail to serialize resp"); - let mes = RaftReply { - data, - error: "".to_string(), - }; + let resp = raft.vote(v_req).await.map_err(Self::internal_err)?; - Ok(tonic::Response::new(mes)) + Self::ok_response(resp) } .in_span(root) .await diff --git a/src/meta/service/src/meta_service/raftmeta.rs b/src/meta/service/src/meta_service/raftmeta.rs index e503f4ed08a5..091de8fb6e10 100644 --- a/src/meta/service/src/meta_service/raftmeta.rs +++ b/src/meta/service/src/meta_service/raftmeta.rs @@ -998,7 +998,9 @@ impl MetaNode { &self, req: ForwardRequest, ) -> Result { - debug!(target = as_display!(&req.forward_to_leader), req = as_debug!(&req); "handle_forwardable_request"); + debug!(target = as_display!(&req.forward_to_leader), + req = as_debug!(&req); + "handle_forwardable_request"); let forward = req.forward_to_leader; diff --git a/src/meta/service/src/metrics/meta_metrics.rs b/src/meta/service/src/metrics/meta_metrics.rs index 47a96034d9b8..2955ba96ef56 100644 --- a/src/meta/service/src/metrics/meta_metrics.rs +++ b/src/meta/service/src/metrics/meta_metrics.rs @@ -424,6 +424,14 @@ pub mod raft_metrics { .observe(v); } + pub fn incr_snapshot_recv_status_from_peer(addr: String, success: bool) { + if success { + incr_snapshot_recv_failure_from_peer(addr); + } else { + incr_snapshot_recv_success_from_peer(addr); + } + } + pub fn incr_snapshot_recv_failure_from_peer(addr: String) { RAFT_METRICS .snapshot_recv_failures From 7e28ad99b186b2dafa9ff38b307d120970ce44f6 Mon Sep 17 00:00:00 2001 From: everpcpc Date: Wed, 11 Oct 2023 09:52:24 +0800 Subject: [PATCH 14/25] chore(ci): add ready check for docs (#13189) --- .github/workflows/bindings.python.yml | 9 +--- .github/workflows/dev.yml | 1 + .github/workflows/docs.yml | 66 +++++++++++++++++++++++++++ README.md | 8 ++-- 4 files changed, 73 insertions(+), 11 deletions(-) create mode 100644 .github/workflows/docs.yml diff --git a/.github/workflows/bindings.python.yml b/.github/workflows/bindings.python.yml index 43e7bc9fcdba..0398eeb2b97b 100644 --- a/.github/workflows/bindings.python.yml +++ b/.github/workflows/bindings.python.yml @@ -4,13 +4,8 @@ on: pull_request: branches: - main - paths-ignore: - - "docs/**" - - "website/**" - - "**.md" - - "docker/**" - - "scripts/setup/**" - - ".devcontainer/**" + paths: + - "src/**" workflow_call: inputs: tag: diff --git a/.github/workflows/dev.yml b/.github/workflows/dev.yml index c96474bd4cf1..01d48aa938ea 100644 --- a/.github/workflows/dev.yml +++ b/.github/workflows/dev.yml @@ -9,6 +9,7 @@ on: branches: - main paths-ignore: + - ".github/**" - "docs/**" - "website/**" - "**.md" diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 000000000000..0df47a6ca4ef --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,66 @@ +name: "Docs" + +on: + pull_request: + branches: + - main + paths: + - ".github/**" + - "docs/**" + - "website/**" + - "**.md" + - "docker/**" + - "scripts/setup/**" + - ".devcontainer/**" + merge_group: + +jobs: + check: + runs-on: ubuntu-latest + outputs: + any_docs_changed: ${{ steps.docs.outputs.any_changed }} + any_other_changed: ${{ steps.other.outputs.any_changed }} + steps: + - uses: actions/checkout@v4 + - name: Check Docs Changes + id: docs + uses: tj-actions/changed-files@v39 + with: + files: | + .github/** + docs/** + website/** + **.md + docker/** + scripts/setup/** + .devcontainer/** + - name: Check Other File Changes + uses: tj-actions/changed-files@v39 + id: other + with: + files_ignore: | + .github/** + docs/** + website/** + **.md + docker/** + scripts/setup/** + .devcontainer/** + - name: Output Other File Changes + run: | + echo "**version update for docs detected**" >> $GITHUB_STEP_SUMMARY + if [[ "${{ steps.other.outputs.any_changed }}" == "true" ]]; then + echo "these files should not be changed for docs:" >> $GITHUB_STEP_SUMMARY + for line in ${{ steps.other.outputs.all_changed_files }}; do + echo "- $line" >> $GITHUB_STEP_SUMMARY + done + else + echo "no other file changes detected" >> $GITHUB_STEP_SUMMARY + fi + + ready: + runs-on: ubuntu-latest + needs: check + if: ${{ needs.check.outputs.any_docs_changed == 'true' && needs.check.outputs.any_other_changed == 'false' }} + steps: + - run: echo "ready to merge docs only changes" diff --git a/README.md b/README.md index 3bb3c0efbe30..9f4b32b148b9 100644 --- a/README.md +++ b/README.md @@ -116,7 +116,7 @@ docker run --net=host datafuselabs/databend - [Connecting to Databend with BendSQL](https://databend.rs/doc/sql-clients/bendsql) - [Connecting to Databend with JDBC](https://databend.rs/doc/sql-clients/jdbc) - [Connecting to Databend with MySQL-Compatible Clients](https://databend.rs/doc/sql-clients/mysql) - +
@@ -129,7 +129,7 @@ docker run --net=host datafuselabs/databend - [Querying Data in Staged Files](https://databend.rs/doc/load-data/transform/querying-stage) - [Transforming Data During a Load](https://databend.rs/doc/load-data/transform/data-load-transform) - [How to Unload Data from Databend](https://databend.rs/doc/load-data/unload) - +
@@ -223,7 +223,7 @@ docker run --net=host datafuselabs/databend - [How to Create Data Masking Policy](https://databend.rs/doc/sql-commands/ddl/mask-policy/create-mask-policy) - [How to Drop Data Masking Policy](https://databend.rs/doc/sql-commands/ddl/mask-policy/drop-mask-policy) - +
@@ -274,7 +274,7 @@ For general help in using Databend, please refer to the official documentation. ## License -Databend is released under a combination of two licenses: the [Apache License 2.0](licenses/Apache-2.0.txt) and the [Elastic License 2.0](licenses/Elastic.txt). +Databend is released under a combination of two licenses: the [Apache License 2.0](licenses/Apache-2.0.txt) and the [Elastic License 2.0](licenses/Elastic.txt). When contributing to Databend, you can find the relevant license header in each file. From 6e1544adda11f29688a85c4188178f71a93ee1de Mon Sep 17 00:00:00 2001 From: Chojan Shang Date: Wed, 11 Oct 2023 09:53:45 +0800 Subject: [PATCH 15/25] docs(weekly): add this week in databend 114 (#13187) * docs(weekly): add this week in databend 114 Signed-off-by: Chojan Shang * docs: apply suggestions from code review Co-authored-by: soyeric128 * docs: minor update Signed-off-by: Chojan Shang --------- Signed-off-by: Chojan Shang Co-authored-by: soyeric128 Co-authored-by: everpcpc --- .../blog/2023-10-08-databend-weekly-114.md | 127 ++++++++++++++++++ website/static/img/blog/weekly/weekly-114.jpg | Bin 0 -> 242849 bytes 2 files changed, 127 insertions(+) create mode 100644 website/blog/2023-10-08-databend-weekly-114.md create mode 100644 website/static/img/blog/weekly/weekly-114.jpg diff --git a/website/blog/2023-10-08-databend-weekly-114.md b/website/blog/2023-10-08-databend-weekly-114.md new file mode 100644 index 000000000000..ca682ccb54c0 --- /dev/null +++ b/website/blog/2023-10-08-databend-weekly-114.md @@ -0,0 +1,127 @@ +--- +title: "This Week in Databend #114" +date: 2023-10-08 +slug: 2023-10-08-databend-weekly +cover_url: 'weekly/weekly-114.jpg' +image: 'weekly/weekly-114.jpg' +tags: [weekly] +description: "Stay up to date with the latest weekly developments on Databend!" +contributors: + - name: andylokandy + - name: ariesdevil + - name: b41sh + - name: BohuTang + - name: dantengsky + - name: dependabot[bot] + - name: Dousir9 + - name: drmingdrmer + - name: everpcpc + - name: flaneur2020 + - name: JackTan25 + - name: PsiACE + - name: RinChanNOWWW + - name: SkyFan2002 + - name: soyeric128 + - name: sundy-li + - name: TCeason + - name: xudong963 + - name: youngsofun + - name: zenus + - name: zhyass +authors: + - name: PsiACE + url: https://github.com/psiace + image_url: https://github.com/psiace.png +--- + +[Databend](https://github.com/datafuselabs/databend) is a modern cloud data warehouse, serving your massive-scale analytics needs at low cost and complexity. Open source alternative to Snowflake. Also available in the cloud: . + +## What's On In Databend + +Stay connected with the latest news about Databend. + +### MERGE + +The `MERGE` statement performs `INSERT`, `UPDATE`, or `DELETE` operations on rows within a target table, all in accordance with conditions and matching criteria specified within the statement, using data from a specified source. + +![](https://databend.rs/assets/images/merge-into-single-clause-96616a67419ab6991f8cb16526c81d4b.jpeg) + +A `MERGE` statement usually contains a `MATCHED` and / or a `NOT MATCHED` clause, instructing Databend on how to handle matched and unmatched scenarios. For a `MATCHED` clause, you have the option to choose between performing an `UPDATE` or `DELETE` operation on the target table. Conversely, in the case of a `NOT MATCHED` clause, the available choice is `INSERT`. + +![](https://databend.rs/assets/images/merge-into-multi-clause-a0013edb335b7f6c45f9b338a68c50c8.jpeg) + +```SQL +-- Merge data into 'salaries' based on employee details from 'employees' +MERGE INTO salaries +USING (SELECT * FROM employees) +ON salaries.employee_id = employees.employee_id +WHEN MATCHED AND employees.department = 'HR' THEN + UPDATE SET + salaries.salary = salaries.salary + 1000.00 +WHEN MATCHED THEN + UPDATE SET + salaries.salary = salaries.salary + 500.00 +WHEN NOT MATCHED THEN + INSERT (employee_id, salary) + VALUES (employees.employee_id, 55000.00); +``` + +> `MERGE` is currently in an experimental state. Before using the `MERGE` command, you need to run `SET enable_experimental_merge_into = 1;` to enable the feature. + +If you are interested in learning more, please check out the resources below: + +- [Docs | MERGE](https://databend.rs/doc/sql-commands/dml/dml-merge) + +## Code Corner + +Discover some fascinating code snippets or projects that showcase our work or learning journey. + +### Introducing `DATABEND_DATA_PATH` to Python Binding and Local Mode + +Databend's local mode now allows users to control the storage location of metadata and data files by setting the `DATABEND_DATA_PATH` environment variable. + +```shell +DATABEND_DATA_PATH=/tmp/data/ databend-query local -q "create table abc(a int); insert into abc values(3);" +``` + +`DATABEND_DATA_PATH` also works with Databend Python Binding, but it must be defined before using `databend`. + +```python +import os + +os.environ["DATABEND_DATA_PATH"] = "/tmp/def/" + +from databend import SessionContext +``` + +If you are interested in learning more, please check out the resources below: + +- [PR #13089 | feat: support DATABEND_DATA_PATH in bendpy and databend-local](https://github.com/datafuselabs/databend/pull/13089) + +## Highlights + +We have also made these improvements to Databend that we hope you will find helpful: + +- Improved Hash Join, resulting in a 10% performance improvement in certain scenarios. +- Enhanced distributed execution of MERGE. +- Improved CI by using `quickinstall` to install relevant binary tools and executing unit tests with `nextest`. + +## What's Up Next + +We're always open to cutting-edge technologies and innovative ideas. You're more than welcome to join the community and bring them to Databend. + +### Delete Files When Dropping Internal Stage + +In Databend, an Internal Stage stores data files in the storage backend specified in `databend-query.toml`. + +Considering that users will not be able to access the staged files after dropping an Internal Stage, it is necessary to remove the staged files when dropping the Internal Stage. + +[Issue #12986 | remove files at the same time of drop internal stage](https://github.com/datafuselabs/databend/issues/12986) + +Please let us know if you're interested in contributing to this feature, or pick up a good first issue at to get started. + +## Changelog + +You can check the changelog of Databend Nightly for details about our latest developments. + +**Full Changelog**: diff --git a/website/static/img/blog/weekly/weekly-114.jpg b/website/static/img/blog/weekly/weekly-114.jpg new file mode 100644 index 0000000000000000000000000000000000000000..b1a8c0b83034be7307db038e537d156d3bb63b7e GIT binary patch literal 242849 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�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

&19%-wZ<)OwaV2{_El{|MqUCJ z7x`DX&z9>0ZX@7~d3~C~bp!BcXIE)nzi#1lvAsyigVM3(GKt58X}kjrSYS>s?G67B z#d#f>r#!L`eR7vcp0ac9660^6JzQK3g~i3e@={N*{HV(Ms@RxMDQ$2Gz2zQzo=uil z^c_bpD$}t!HleHT7|GalOk_1>e!_bsFyZ$Ve777+;9`g2-3qV#hU)fRmk{oTW_v$S z>(f*DSnnSPxNZTQ*S=fpHuuZ&>`r*NbAj9&z>V%+<*&v5mwg!6ZXT`Oc{A=U?3r+f z#hvEym*?T$EpoReFPIoK(k|ytw+VidC7*nP`(PR@+;y%@b_kFJS2k! zC~m+0xjWME`Ac`8doIU;(>WjFFWYqLt4V>=9|%O-n5o=S3xKZy85pYUe0Mj|fmu}a z@1)Tpi94I#UEm$?40qaee;nkJ3m&HcL49@8Qy>&d}dXS5m(a zaWCfT-C_dpYp(ZiYjod@v_z3b{Q#OlHX$H*2aoc7)V||1FJ|(K5I?T9?xb~@O9i4A zv7#|8i!(3pYHk2-dARj7$8z2u1Mfbd*8pHV?s{Q?!7O+;As`v5-}?;15>D~a^sRn;DYUu zL~2T4$ZuI%G~)?>K?rk=`_=X%CUDj0(;9O(6x|{*E+jF`;f_8+^vuMf2;uJUu9otj zYj@R3emxO+RWZ>lpg@Gy0P@J%w%Tnx^Lc6>;$Ij_m7<*(gTa;^09DF&!%lFdFq5|F+ojCtOm)1? z*FyM0a9kC_dN}1s9r8yPRj~Q+Cgt)6IwZ5w3c{UINz~GTj#n?wH#*tRdlQPr}DphT0kEh z9!Qo`oP*OvXp;DqDam+WNut>-$N4m1( z*|DJ+14+m5@$e8)bfSCXeI*n3p2=T!E$j`$u8$&O*N=Ck?YA`g*%{u7|5VA)=0fU9 zd=#B=dyZ(hKQk+V$U%dtS^L>o788yMyRu3=Gxr9!z?`OKvw&a%L9SdUv-YX8WwByX z!o&1+VWZ^%Cr{UWJ6Eo3EzV_gdx|SGB%=_>qTps8UG}F|{Pi>3)Z9M^VeXD;Y%PM! zbIcwWv`ocbgmD>T}^$Z=%33x z8S9Vcl%<(sEF8GAbs2Nc!Sx=bN-tk#KwF){9}SZ zoTq*Rhh@N+G&X6-HCHFo$a`3s-tY7csk4xCXl`knq?WFX3MRwWR#~Bn3aB4RHkgA0 zz0pkCj6QMrS>_-=zLKN-h%R`J_ZEZX!x0mXM<|5ihrtJ*NrVT_Ejv1hJ7%A|)YiSI zNt@K#uBuF@_?=rEuDtEf<3GI-Is)CN*xTHBQ`w5bAHLE(uOD3W%2h;eZ4^breJ1|+ zIZ;{+!_y)iE9?E{rk!#oi{3D*IJMl!r3p2;7%Co~9gB8RdPO`6GF*W|TzS}&VRg0U zrJuF|%Q{i-8iuEaP1~p(^{TXPBHl_d^k}&E z+-QktUvsJcK@egdTb3L&Jw5t^kP>)(-J$+S86@c8MbUaZqI`H`kpITt$&+HZxQ<6? z1kYe1wka%R(cV$1E#zkbvq30D;klr6rd^Z2N(z;THVv4NBv`zc!j9V1e+BnuB#Z<$SdVzbwy@^Oj|_la+Xw|1L(jD8xxxn3GK-1)cG{Y z0y6FdDJpRrp4rw8K{O8U*GsY8qaD+^g z&9ozQ+gXsrz2Y#`jNC{|6Aq`9Kj9i2UyOp`;gjFXx@H;U?9<*Lg4UmNJ(W=i?o~9%>QDU(FDjIO4>kZ$Pja=ec zBkC$E4gFqrETVhE)|_&UI4HIAP2R$dof@Oc+8C@rE-wKpOnm2KU*r!RLU%+@8s}t) zbA-$VI*=huKlt379x%DArj8o2?(>$%{8COojo)oORevBr0LyK`;((`=m+LLrak(au%7By2WZv^W=- z$X@bE$-Ga!<$xf>0`rM`4IJ`{pGZ7uz zo`%5eRijPpNZ2;kL&EXQ@j(hP^fE8i?Ltul;}=87{Ma7Z)Y@RNjq(a}56D%SrO{4n z2Ift{6~$(33kYQ403GSo#}VO5ubEJJXH#QrNd?)IDVI1V+t-3z+SG`U5mtKTv7nTU z$KX(n9#=ehrJ-*Ep;eo^V zO9j;r$L;#m%Gd$m;Pmol#)-***36L%zR>3Q`#T%sB<(AKwS=}~Ot4D(9t z-cqlW)O79V*u|IlMN)VLSiHZ{<++L%nY-G17@Kw4rWaTq4J)ZzV8w!Ms~<6#4`d~T zoFIAg*fQ#WpfG>Ndb~EqOaCJ12LmnG%s&9Tnxr?;;+rwXk4K}KC=3saa12AXUm0{LkEWgI&g|4ex*3O;G{QH%7WBI*bv^`i zYH2S{u*8fCeaxz&*3g=<7|Dj9Efg@k0vRxl>Z)W#x(A1|HZ}26fFhYsOFm_9KcPni zWkn!2&*9^rY8?ghu>X_`C9My|VfMqci1#zqav%MKI3n*vw&#tB9J>GnDki43j zX4rb0hcxDsY-=A6itm2kCcRcSD3o@^4BE&2TWpU^DF@tCg#>MK$wIo{ zIJ`4r%;C1+sOT@wT0K!jh_>5=apPOYB}Ko{*mgx`b;x+o zqXB(%;GA8p!)n71@tMtQB}v#e^L>`q>Z*7E;vN+uyQ~DyCum(!VCUKMQ1_G!@d|5OAvY=Gqko&8DWNU|6sP>6lx{y)0! zaA)`*1cH3RYYu?SpJWX=p9KQGem`F`wQ&7|&}CsN)S-KAV++iPyfV9C(rVKi^F!Ro zEwf%?Y*$3p+632t*|C4FGCsmVs_hh))#D>LJ>_a;5kc3*yNb%3#ByH24x#sMERy@? zlnFx^8Kh20&l%OZxfQPOsP&(Jox1gz7z+eFizR(A@($7_PWyh9v?^*}$%DhrtsI2E z!}a1#lBzXCz^7SjqlqeyLn%g+y>qTAIfYL@Kfv&rdeh+PiQDby{$WHtLf)9B|Y#bO94Iih0$Vw$#;cM+L>uT7T8DPTYOkf7Nb9||#rjHJ+`kjr@B=+?220jhh#kT0A z8XD8=TWurfv5w@)b6$e6Fu7LXu&4c;#7(1rE=|rs=PnGl3N6=R(ax12o<7-@wZg=G6l%kxh9EC zuj}m41I|%l^Q5=GUpF?3Sc+}>sh?M_cb3BjHRr_8XOU$|;Byyks02!KkhNf-Bli*N2{feThY#`0 z@vpq;&kCK~ZB$J6WXbH}?@{rQ{XpWvvaHY$>ZX&z#NwroV`+HO-Vs%js& zPegr(wyDVQcb>HezIS5&W(AUS3|ccdWTI%(d<-Tp;C!tAlb4R^O>(*`<^+EN0%}B~ z$g$E>kL(e$V`FMbjR1G@F`8{I-0FU}$hEQwZ*lhQy41Di0>UDXPk7L(BKk~Iiu!o6 z1J_+eisoK+hNgKmmRkl7eu#wOqxmhnR94SzlLqZftI{yR0xo<9_vhxfvCMe10#iPX zTy77uhn+nY)0^U5*FVq)A<`!zJlIOfL_l#@o+dgGVSss5SfyYnThz*GSgDp-7@#Ot z4rEyx6Ju+ahg$UZ!=A1_&BW`>In-RSOR4~s-jpgEC~-!GkFJ0`)s(p_7eA_Vfh%W`JlSid8>^Y0k6^pAXyVOYbXgRl} zvW@rq;-XT=%~87B2|z!{m!R}fHiRDiQ8ed6g{T~za4fcE|a?#NpS9R3OKvYy@HD8r%>e1 z`21%M^xbSem9eo#zj{aVsX(#WnCO}%yUbVm>Pt0*)%00@=IHObWVCYFEyVcDBTthG z`?%cB^r3+!<6Jfp`|KibbmbYK&B%;N+Z;* zP|w~gXb}~KxP7j?-08yxnUT}ONTk)309I#RozeDIilucD%S}Her?Y1R8Z>+Cl_igQ zKIYMQjJ+UAR1);IWx1)?h8$nE=vsw)n=C2I#)21?=H7k$BuE`Yy592X8>lmd{!O|> zJ+|x3x-~AN{wD_T?s})2e1dWTwx@ycPbXh5EzJgYg4T{py^Oqc#tEPBA8eebTQ)^c zGjU&keQjDC+hA3IwVi-6wJxrR80WWqc?RjHX{JAaP`~`#sZBu*+6EO^j1XblrtQy9 z^)%3rxAU`*y}7ldU^4j7UjF_y4-z7me*ZaXw9_F8s7T`2*xLjD2ZwzOixA>;`8FNV z+cA(r7;(191(olV2&YjEn6OdeoCIu{%VO(IrArDCnJla1Kn%P2>OvUjh%}DdLT2er z1>3SryMC_S_%YjenYRB_5`Xw>l;%_8Lx*?vxICF@53M}5oKQ6Kbe{ekJuooF^K7s> zZoirR+{Tt<%Cu5#Ea)IeR4vLs{yNvYo}>hiUzFY6+wBS7jO0UUk0=BLlpyxjY3I## z$ZmeM_)I2jK~m*~$&j1VqlFY3L|xZfnd;Z`k))v{ay!{dHTmj~~b1Co6NtQX%rNs5I)Y){&qt$X(;-w}y!P|+IuQo8?S#@R zUoRX1K}_05uETsu4#+{P1O88!65i!)<%}HoqUH>Y)bV2a=a>u9r)l6R4cVC^H^5+C zc09&-c);_|<1?Odld6Kq#LMDmEs5LWO-N7BV7ItiK|4u|)F8fG0e_fsNwP9d@Jpqi z!k?5OUZyPmMT-lf%_U+s1xCaky`kh#7P{Ck7vcHo-u7$GXW_DhPN>?tZSD=j___Nm z+4V|}+O7_G`g@@m7Rs5L+qOP+^u6Uvm^AXq@!##7n86AYpn({I9|y6qM5pP^hlIZ6 z)YTd@Z0-a)@7g#W*wbVR#L%Z1%NPrVo7qXG^AR0^HZWqxkB|$IaSx+F(3pu)C$9t( zxF7$@q*Ue2V@I5UAd(65%lHtI?gBor+j1YRKf8}3ISrNeOlPF~Y5e?V zE2)&|Vy;#WQCh0EI-eF9+rAq*u3&m{vrTyRFdM@YwDL^RYSt`E+-xNZInn&TYBO_d z8oEcyk!rTZ$kuNj$pO~Wx1uzXHgr^%y!hPuSJ7b#X57R+=%mb=J5)2a^4y=0BSz)T zBws~mJG_fzsVebYc+5Mbfu&09pQ3s(L_j9k@x#?_t~M^_qrgIHyEB6>tsx&vWo1=) zO>JfBsbxrhz7UB2hv4+^re!XS)2HOAVDt5Af|U@(c6AfJET(dQFeoVsy{%SR6P8$c zmM;HOIw5jUl796}$$T=SVzimwJF9?jY|LZF{OIdLck z_}8#LtGr@^eC3joqR!*tBbsoL=%XN#3C1C04m9ghOB3Pz(CDCVJy2yfaH7{#Um2i{ zbml4^WQ;AqAJlg-7|@WBm5{FZ;Npt0UG8gMZi#PU%PR+{gFS4A&LG`=ORHN{xbaGj zk;Q;O+0miqaQ#WS2&41YLluMKim>`eQcJuF4%E{>wgwV~n#NzKa_`|fW`mG!go4(j zIihg98Z2uA#+G-w$|oIz5#obKHE33m4bn1mN9t7kP<2U$x=k`Pv$JZ~M>2wT1s`sx zy_0arw-62|eKR?4fSk@z0d=MSwSs8}X%JY^G;XENZG}PsZ!vK7w5R<=`nh z$9&kK{fa5qP&#+I*|GX{Bu!OqIyyaq3T;)T|H%*+Kgv6$(3_n6>BV8gDJ)yD%ui0g zb8V-xqK(GD%9NDG#yI52M%*2fLGv@3vdZj@9wUCVc{4sXaz7=GBd+M^JEvHIJl+3s zQX1B7mt7V#X#kGPTUR9x7Qh8t`NPD0`~CS>&;>@!k8ycF(0tojd&8}+MPMkm4!m29 zOquj7cEDP0hxTKu*oSjY!TZ)_z0%Z-9v_5_9_}HMF;s=P@zBWQvo;HGf{^0@$qSDW zl4rs6Z`chm##g;_7lZK$BVqBK%xaPkjHgC+AK!#+moTNh9faZ6LwV9s(J`@G%tIh|Z@VWO#?GUju{C)bGcZ#O!Z zMt5;zr4yZO;A05Ie&9$3C$4e+gVM^pNcn+&d(8u4$%~sqD(8$c;z>&~OdfUo0TKrq zdmEbmg>1Ht#hnCamN{POB~eT)v$by=G{SgK***#1QXkc$#+s+&2wYlw(IKwTpUoNd z)AU~jD37Cv@k-QmW0_xzPQ@@x(2&p+kipEu{Mhnch4Uvpb-=jLo%SBS<-O0E=&o-{ zbVt1>F1p9|hBz*NG#X{v_-rN$Iw>TJ6J<{}C2pMZCKqyt*0th;M>#y~Cf+H0NDPCW zu^XyDAIF-?B}Gz-g|hK_I+Td?F2#w-l$@q5vH-XrjbZJ za)mUK@hdOZ`IT%+iyHY{ij{Y2D!v)~BK6MYR*c1?3KUBSJHLOS_e~=9bJO7I?VZ^D zNP9snY|7dGk(`8_%{VI!3aiJ)g5`fvk;y4=vqOCQIMB)bG}tX_>!xJbFL&|k*8*zT zl@Sx-p#G<9)`MttBh+irOSzd0`=7w1Z(I2yPWICKAjzt8R!bR*mwem%)HWN!)|@O4 zm1nkH-=%0U+r)Tm#o#;Ua=DNCEr*kPb%Ppzj+kh1b5eNtOgmiGM=&`YBHJd!CeBgbLx zdWhLKua(zpuiCwP+N%aww|NCWp_dnz1YAVK$D|F!;2<$4l}I!e_J&_JSxEHOM&OWx zzWi)o7O1rYBI+6ul6xdceiFz7SLE?VWxYpSARgEuF*Ij_d%Jx12{bi$Vs>$CG?SZT z78Gh8v@0BC&)w`EhlH^a<}<9t(;3+1w=tPgeCVDBVtpELh*()|6^ zE9dwa-HL;Vfva;LzXz6Q(#Yy8S|6TcR#UOqV}_lH#8uR^ot@fr2YG&fRc3@aGQE>K zM#n$Dpk0!E)SK-$zeeZnSfM*&a+i;v=+3$2;?6B16STaZII;Ys&|hPItG~ zauH8_c6wGIWy5Pb_eAYs!Gx0=|4Ku3C5MMu98_>TSly*6KY}*?VzL zdge~T`*tabEfHv7j3eC%<)#bdr0xCL6t$gq#|}>369?HE4F0dRXaupuQQ0Jqzc}tV zEU-?fh--g;{!`GRaNk>9d?p)$go5ieVYb_owv13Q; zQN?Sc@WE*%6YL=o{cURgMUmMZaY&{bEuMKh;W>#l8zOx($y%WwLnZe75?EPVq>(di zlGs>8Xe5J{Akp}NpgQw}Wy4FAad4?6W`xk{xU!D*4rvxN-b#06(e&K9ETv3yL?}v~bA^z-Vt6_QB=WA{xm!R;EoVtF7+0 z-`{6^9*4p@E-uT|CP-VZ$Y+_klZ;(YIjYD)>d-36K9OmW&m=IbX|VtXPm7hcJhKS* z?rNVl-pXNUZTm)B`YN#LyLwZ&>Y+=aC7HN`(GP(cYfB@A#43=$YOj!5euOtc^479& zziSlo?ucq%D2XV+D_WO+)-3M+4ud}JWs@G{I=_#XXTCMGJg2EDHTrU0GMe|NVj=b? zJSolfF&vL3aGezF`iaba-(W)9YFUg!Xux)0w6m*0?hF~fb76o* z^tS-b6-JOPvPdPfEt_Hhd+bS|HO67{FtX!YQFW@&*xY3AQ~WR>+$$lt5iBVD-~+8(O&)ihXkQlwB~M2<%<9 zo&r{b)d|6eroZB~G1E$C9lSoK=26Ab3h7kqST;ixWPr69RrS}}?9bmcGul8Ehy|e5 zqQE4@Q=~)$`eXzIqht>Rgh^%52dgfO?4e3q{a7O#kh2)-R4i%w;kg+b5=vrRCDrhO zFxk{DC}{%I)>pmQF}WB)W40;()>VuN=Je9gmtMq2d!WBD!jfd7RSH2-FRkZ zwr$)?wGQd?ao?D-7}}m{wmW&|-SSlEBFl#JR_6Yq`*tmf^|G&7!`#_yN|%2<7>1qg zE0>=ddkYA5b#y5!Z?z>>%c1l|1T!#wA)Scg)2-5V(A+5Sc-hhN7!}m#vlBZzO~Vba zR;Us;Kc3bLRzXUpx1qp^R;^@|IMTwQN{US*gNJgjQ6VP=h19Q&Uo6)P`Dm3og*`Nb zg>3m4i?pSRg`z|Z*jU~7RrxIkj8p_-#3V^ zjWu^Q{BA>Ejem+YzPUF*BzU(vvE#3>*I8eqwHQb==x~2iu48;G*ztJ5f&L|~0&yNM zfx|4q>gf*Ebz4Urb{cB#=FvMw4j_!2Ac9Ad3W`|ELt&Rz?W}|HaDt0b#zx|PajG?V zow088q}&8&+KthVwG%du&Mt!2Q|ISBu!&GuSNUbRfVJ7JIo=(?-4Ofrc_B_ zE5>g3C>=+%jDl1*4mZR_tJSv>vAJoU%HHuV5>2Eft9s!M%Y2U(UEAEzg26>oLy5VT z%AQQ!1PwSA4_6m6ljoipU=iMelC3Fx^JJEy1?ulrZ0z zp?1!Tsk6&Bhx72$)W8zUxelguvWDtP=-Vu~9e;__2C(Cs?oGZEV=AuO<{;S~{>0v) zN*2#oe{pqY+S+nkm9Y55_|6tzXFK0}y)#~5R(wPI;L=GVqI$W$1Jv)yBAjyWJHEw2 zN~$j_wka?2(WkirK~WV?61Wb220VC56`=}{sJN5 z4P}LF|JreGI`acUS85z$p8h zF5@Wq{f&=LLF#i2cjRPIau!cV;@VfRF@f~0k#~4s}Is{|QgVyT zbM=QhRfcs)G3v0xhQ?+pq*JS9*v;G`*O`du?XcJb%FUm8+k22Z?|{ai<^(@*fUwbx zsrE+ja8VJ2t`7#2#zg_;5it?G^g;qhu7Vy)bUctD8t)fM6CJw65k#Xa?r$kRu#O9W zAM2|jor84*;sfNdRcA*K{o@^Tv=t{AhO$89xf@nASz;uX4of4 z(GiZX12d81m#|=se@Nv8?i-p;4H5;FAC{d3xjwq_eq;*Ot&~Nt!imy+WY^4t+?%3V z+>ia^c+2=;vWj*T%gz;xxmN{rX!;PW)F1hCMc0ZsjOZS@%A#MZg;G}OkcvOFFdFwU zyH@K?a1umIhtJ9@KP=5EufVPH*EzBTTE#?bLk#jc4`+uaFX~XG4*aH;-v}7STd(%U z=vkyGkTec|Fie!ob%?q z171vq$(prhX8pd`l^^E=bcs{rZ<`=rN9kE7Pf7wT%md%9%~>?k&M!0#z(hv-2ufmM z1n|<>W~LtM*%4vO+r;6eUH-uf_JX31!c-ql;^+W!Tnw2)Ux!9n%MuGUlEM`1o^E%A zGiHak$WxUgUusW?ubFeg`-Vw8vJjMB(Qe5v?FQBllqh&2QOLawj=GX^m;~h<>=7=}g$*y2? z$)t!{R;cqbch7yxzx(E!eFXoUB01Wd^&HOF4_&jH9XP^n@#%co0%!#jV(D{RDRo*f zzYLg)C1M7>%QjH+IV4ERcP1-Zfim_Nm$qeJ7(O3NwKr)Z<=YgyE8NcIhXA!pe`0cD z1B=9#4X4-nGnpwfDb%eOz5H}_{FD>}_en3hZ1pFPMk*?58J>yMR4|uac5Kh|bww|ZB1ik7v+pCH^{3LD!WgwtONTiX6ti|vh5Rl`5*TV+k*#j<_rv?|Vg znXs*uK^pYXtMuon28ig$B9~^WG)Mb`Z~M@~er~H`ae0CdIl8t5O*Op5)|poq(SZHL zf-c;~4$%g_jcH(|b}2FXR8cH65-J@w{o+YYOH0p=MF(i3*_W4{ zlcS?GcCZY@Q!^@{_l2Y5GPg4t#=GKz$QH$5pP!AKQD89pHUKd>Gr~g(w2KRHnz=rG zl20L>-b1;?ig;Y^W8VA%O9Xijz~Vr+T|)?Os-5CDX?*D%UTdGUCaq+!O@*1OB>RS8 zk(eFnUY|Z&3QetuorLYXWe)z=R z69F+mpb!@|9rfm9pc=%&IYKR<{rx~8q(%)vL?4}eKOePV!!k%U3+GHEex;^PK|Rn} zw`G{~K|Deou$$JySm=e6^Rm-|Sv+aNU+-ma-7@VUvs0VDmMBec^2aium%K72hFgP5 zvNFoXx)%8OyJJS$Gcs0HVK(gq@7&}@$!KqLX~rM?0O-9?FU4Q6V8G235&)1w&+5^gdl{R^N~bty(*8H|8ltpR@A{exnL@rKRY z(WRx{Hz&(|*0SYjnv}v|#c!vo&Uf(5ciZodOP#?Xo@W9M|3t$~>OF zxg)%p>?BRi8(l@J6Qv>_V{|^SRyAY~zS3CV_ajxf=Hr_8FF7yTSgp?b2L*Q&$)_yU zy=>t!(`}QuGk$ebd`08^8p(^`n+o`vAO&l z>cyDhUX-`Rs3MmL(j|i-zyIep2|Ilb9e>A;oPEzQ+n(uk8daF**63Oolww0(6q+Mv zW9~GktA9q7)!K7c{Q0#ra#ROJlI4vep@TZNzx+}DbWEtkZ{M;0(G>Y8_|xkO?y5^6 zkf;d9iZ};<_uzRkbu&EgFpXs$WSRG}tK~nMO1w_y|9bHXz9Itqm73tvZJmMz5_%)r8y)k#5)79t5L12S_*cB)JI|w6w`>OT+ zrYzlUzs&wzSAlo8f7P@yI!Y6!Ua{ zI@&f^@PkWK-w(??o_xE*K6XrU^B$4HA7LhkHI;voWpghtZ*6c+t>{aSBd5rE%|6M3 zCTs7FfNlmoA#wUq?*3sdWRwNI<#U&ZbTZ0wPYn$iA#x(?b3*n%jv0w)7V9$a(M>49 zAjN%F9p7!mE`|F394yyV2%ll#WLS+Ff0AF+6 z_5{rd2V-@s7!{-a3TzAYG8C2RB)a>bwrNcx;~+p4VuoN5Kb^%0NyPs?Eg9{!!QDt>m0(_ zaVat8&(|!Kj~9t9Wq>*EC_4ER9jMU-p?&Rh97d0s@QI6F&Hhb-llFU3=bcfSxbLcY zN9{ZiZ~UWL|9TpSTZe8v(7z)=g$W0=XBAt>f6*43j^&#tymj4-iV>VVo8&^Ot-o`% zv4__e`x4w6g)CW4H#+i|6H822t#<0jkGW8(9cSd}7JE8Efbmn3Lsa@^g4zj*AiHLK z4Y|_7tV=68c#M+${&>}DnTlJ4OqthYRAW4pt3J|Kc5@mjqYATU&0a7Ge7MBEC?d-% zt5p!-`jjtU35IgU-{nTt2%gq2W3YYVGENrS?+`6j|IX{wP0?2B<}7Y1S1DY3YkrygZlk}83w*k>oV=Z_ zJl$w_wMLA5T(o?v{0foR#XI4Un+&*j#W8yvcHtR~(~}vGVG;E0SNi_&K`$pwwKDD_ zH2D;nf^008-oH!@Cb(qS-?e@;8K?wTB0S=e{}eks`Mz?0cX21y$9zC&|KY?BwT<6e zF$k@i=njf=tXwq6tXj!WR3KYA!%W@licajH1qBkmX}}#!2*RuJzmMuQ2e0+~bRO-} z|IBD9w!WYnmzRH#w3=W4m5HF1ta?^;@BL3(HbKRoTZS(v-k*Ripn-P7EQAX?FRnf2 zIp>TD?O5k#VfGs|qVwl}$bOwag4#5-`A|f`?1IX^SV~t|it|QBo@!c_{IqzSRocvB zbBdZY7H}I{SXS`l$8{F2Cp8h1F{34KtoC*HZ?0KR5H~L0h*AKmm?cih2X>n^*7^`K zafT8${O1JBbYzLDF(Te`tnsB0K?-zn2%$irk=s`ZH|flxiRA#y_>t`!Vn5b`3389@ zrnAd&Ya((=jspfOHz`27!zT|gLn^>VPv)8Gl<6ipqAQ-HWb}3$!`Td084u~>qwwx@ z4ctmfT8OYHzA{~rhW ze}%41GyWpRcb$Z=?XVfsSOlDiTl8dR3wa~_?DFdefS54T)B<~ettt^c zIHb(qEQ{-8m!*7$?Wr>(x$NsbW{7}F*xR>d`eES9 zjf9~OA43D##8|=(TsS_id?d*SROsax-aETh-X(|E2ajws895KxNTefI$6@tyX}&a3 z`~gue{@BA3Mh3IIVVtG%#k%&%RB{n~^7Jql8?D!v2LO$_%{%)n`cSg)HPyr>&(#L7`b9BnC4G>j=h1ogh6dVC8U{aRjfj0-agLUWiOzoj7A>sw9D$18n2U3 zJ3PBJZC&azw|d8i_Br$xZNQ6z+-i^5ppIL!E|s~*YaM&8gmozo!%`g20B5U8#4y>D z-#Nv!Z@x{5qLaE9Sqb9G$Dj#G%M3aL6B4OgUvT^;_ZtT#rm7SnbcPGlx?c4)0?_qJ z5S^$Hb?qbfWqD;?MD$yjk>r5k1xrI~;!nVm9@{bMbEc)tSoI18`||e0$l`J$0Rh1k zE)qb-{RQ#@$9zjN-x-p3&yt-H07nASO2;Z|*MI#H2mC}mM(UwAC3Z&tLE-!dE3A5(Bz*jeJBIRj&MlIVD#31D$C87k~O!Db?iQ zXIbV+d}P-YFLL?xuQNgK4-b}o_7Ao6FT}p;$6viz*I&I@!{289(qK8_|2M_{zi^`7 zf5l*b6`F~-l#?SsX()pKh@mFAG3fn11bW`~I7Wi*!o4)>g2RK$&&Kn_{ z3eeY*cK!d#c~e0Geq3Kw{=tnxY{w6YwhePKj}hmu)2iB-|3UeBc4F`dt!123tI*l| z-3+?+itdppiT<7Wp!Q0t4IZ@qxZKd``#8FFpYIkWe9Kv;kcBCEt@Ktf-`m?A?O&7V zL=p2JJrVO?1~Nv=C#2}&yc|aysY*fq@?Q)SB+f9IvjcQ;S=P98EUwe|hl^qZe~XNM z%aEeEm;QUK;7Q)KlKBUYhlI00|HYCZIok6`hCD{T1mzy~ACCqS;r%6aUi1PEYnfV- z)>Ee=``ps0OHE`|4FL<#n33$=Y3|TR2PshYPNL9 z(^;Tl-`cIUR-ZI$S+|RXsEQ$>r>=9g&l^2r6~5YSf1E;^f&QwWh)uc0`X=W-y59cL z;5f&fN)V?0=^Gc9BqsXS1M45z$wU#I2fk7Oo=HZ4)!%%#ff;x2f1`*xNgqZ?b&_7L zKt=ySnc@T;t210@k6z-3(0MZbocGs*VvHWHPk&0Aho-$~^OZsTZuw!Ip0+@B(i+q zr?lkATv2m$xOu|slZ7{aeETx6fzp|&$kih1bnESz8j_54s(PytPDS|!(sxmpJvHIO zFub*bJI22#8NVBzZu3%mih7@bz7MoyHbGrrss`^i3RT#dk6$|dkX9wvr)w|Y*0I~p zIl){srfND(*DBU{tF)A`Ds~k!<-Wl%Vtmr+JG9da zQ5w-;IWEOV42r|uCY+&T1N*sqM9?abO7=(eAob8M?ULLMZ3e|!jK-5WRk}s?ZjKE( zlbK~|ZDO7W`Spi;X_)Sc)cY1ARpc9Ig5 zmW==U9-r?s-8(cImunkw(gP#=Tl|~?PWh6)+*`VkSNT<-+zoNm=#}>~0CCc?2cxB` zJ)&VUO4Hq0IoYVKnRi&YNuM$f_P2ZnO2x?zYzcClhzXDV6CI9Hc{p)pLtSwnl@NNG zS0(w%FYcUro^%ncEA%!Flg|GB4yX3pS|ukg%=y6JHLLH ze3w2Uqa$xq=!s?TvpDa>G#|e@I~O$5ZTU-1w}8Mss`*&pa!?$uI)p=OYFs5;kFS1a zJYq{nS2(kv1S4EQhnQ1Wh^bC_@!bJDOhIT(QLtTDeBgrkxl;*WDto-{9{8a?Hp1cD zf$GsUKzfo!^dQbtY{K*Ph*`Eut()?a2;+p2VZ9l#mYY*b)SRR2TNsfho1lcAeiU%Q zF2%mI8BfZzTsy+ISQ#S8yQ|z~<2TL1$S@d6&3KkB8;!)u&!u1Y`OwSy`2vpcFn?$0HQ9J(L4P9%Zl$`^Sod(;ba%sal@L$?|X@QwvR9*&)kDl-ka#)ESf5FdD{&EF}UZ>-vIGwEkJgi_ zPN|2tUWsw0c_#~Ul2es}xJ%t%CgmXC6-^)7wh^HPG1wssSVAZ$7Cujjpgf)b2c=%l zq*|-%AkY4YSlJ-^VJX-3#ObZG!h}Tp5Kkgv4Mahjf-u$RfgrL^ZIdI%TO=3f@1VIn z@z{)*H4A&BmDXN=-UD@4hHt zj;W%~DVtk1$2z4gjR<6wSp9%;BD+di`n;Isjqr?8x7uQtpSrFf*i8Upos^WS6gkE? zazpPM(jv4SSk#Hk4ChzdiL~0G*gY{cKHO0WqBXz&Y~kI9B3o`LdWmbS6Ja#&t3NT; zuAU%v3IdAb*d+<>*RA;9IU1g?W1mEsK42p~Y|0NDJ2uM1uNlcX5`#ama6qz&QACBR z_;4je)bOKr8_{nSYxm?L6p9H>T^W>XPt$6-Opui-@@6%oRoSB&L=51eh(Rb&(v|(`=fcj~v|N$T|FuzA4w9d?5=(JJ zYU^PyaX1+1E?$h4*tSiIv=LscZgRW_NhVhd%?we6aE`eVNgx zu_?TOr4}FfY6=m21BYTj65erTU@EClP3#F5n*SHxHM*j$k!O@dQ#Q+z^0hivhmFG} zHi#K;9XU0Y&-3+)U}I*6M9q>4H@}3O=_9dlj|8 zgd(XqVAG+xgQOSv=@-+qdF)3$DPjArHu*4{*z*MdqBz+hyI+HN8*scCb1nW#9N-Vk zlS{Id=uEJ(C)A=HQmLnDv-1i%i0BxQ0!r9@fk_QtDL8<&Tqo4aCL%5vm_iPPL|=HX z`!ze18~Rqwy&1LfRTT6VZi8htQ@+82axYSBt>$mfUmDKqlMKVd!jL$MC5FT?$C-9! z6~+ei1V&G>UD3X_aZ3uSDXMG8DUFUNOGW_pP`W-S>v>Vt76OW^ zxkh)5l$y)0xGus4`P1l1f{4_STZSN%iOb;t*43(=l5!4_cl~P&whxphXVValI0e+Yr6&}^hOgBNkq5QCRG({*0%)Oc^y3u{|2e-jabgW zJPW(6&{NiDC_a2S+<@QHpOS7FBAUGfbkIXwBy2Xngz%Wp3)%%E0X{q~D;=Ff z_$*psZVkyfsM+N2b@ak?>nMc*pAIR~+iIzAq2oIa6w(WC`k zb(%_ceB@z50lvFv(#@y$msoaubUz9h*)og7eT(dzB?e{Uk(B;Aamtp*#)D%|rMIyu z9q*-nz9Lj%X{a-A8UwY}WWx+S^T^|9+o|OHQr&5dfcvgnjk)%;r(70d9EDcoZpsPM za1bAMc<0qse}b?mgZ1SFV0Y*$xY^*y-#8X;M^a^(bLz!8@Q!)=iYdiQ44ZHCiHU9vJ2*x}fMo^d z$q@BHOz{xJ>LaMFSvvPPAX(Kc`B+XQ@;n8ITvcTHYA_CVZYgJR-o!YP8BiI zg4^nfH)3|rEu{qewgv+BPfL|c^ouNKIRp=|n}ltLZ^xxdS$L!w_;xI1(}Uq~=KTIZ z(qV;AYzb*Lyft+}QCDMIXbla^wf@fNg?LXMluG(B-k@!k4d`9|3n~*mC@Z5u&Qt8p zKg3)P9u`m-H;1Zx ziM-gNXmb}zM98=yGAbzoZD?w&@{*D%@^>rOb3o284*h~TTYDiTvg&vi%~CaaNKQ&j zp9d^ZfcwVPLc$%owY9NINOJAX4*yh`%9*0951O!2ReMveYLR(AN-+d16k_3G5#iE9 zyjaGk!q8BhR3Q)hW*2m@FA(>6Ni*8~+ev(T3$t0q0@_2wCX%=J>9@Er^%sjF0&AB` z{CJtvc+%0qv_c)mau)`LU16HkXmoirOjr6_VZL0g6P{h6>OC+3rG9vshG=9Sq3)V~ zXjd9~KBvWxWeM83Dq#y3H>gu0{~(e#zDKrtW!i6jYYMOAa?1~#ofcr3vuGX1Gtrsa zCeokPVC1C+JCv0&$c_okof<5=7$YqQuj zDe(8aE3+6%I!noj_1zWh7cR%vszGZRnn|CbF!su_N5aV8ZOV#?_CTI}9te$H$EMQo zU2-iG66rUex9u{;$;na`zTCN0D2l6P+3M27|HxZX5ehS@SF8ZqGx!wvLUU$_D}U$l ziN^?}#MIG&L%gD)xct?h6PQcO`wmSPx?u02^RjB1Pg?hSW`S=231%hYq$A=YQx%?b z2?o24l+iy)50XL=ak0PHb1oT)CvQprL3y3t)e?$)1>NZ>t#^Is7<_p@`jCs(rdNe; zbYBEjBPEg_7Jgbeu4$oT1>XYx9v9|ID<4;zE6!I}ncms^(yEY{l9~|zl=E20_FG0q zyr8w|jiz-$y{+xh#pVdVpyBPuk%n!F&Xa+rGCJ53JMCHSw9jKbII_XmRJ=}Z6gZKt zjK}v?mc*^5W~E_a_H64Bi^7@q1>RN#8407DiO>_eF+{DSleazE01f$9)3>{(C92gS zHWHV{Ix*TZX85SV-7kuYDk2-+xMFdz*?%cZVnZ!RTbS$_!fkhlR8#U#lGBz{$mg5vkK&42#6h{O(&pc+}-Gpe*DG%rx%=8*y z`gG*zNxnOdvQhr#9WOE1Hr=+74#!Ju2FsXE!HwC>mkSw?-VFJuOe4EG$G0tb74E8+ z12o2RN=#~;b2DMLiU6k2m|W4-g-P`TS}#tYpoeF*K_kWZ_)Nnk(q9I{_Nyw)kL6eg zpka*qV^S%C*1%qKP}}r|8YbTtd{8X~rQ~QQ(RF21&3$3j19sVi68Wdc8fFb89o)|( zDjI?sXeJ~|l<6DtQ!@0UaDQ?wE@r0i`EOgp<)CtQIxk_aREmh5*q&J0rV})Rve4Ti zV~#6{IxEcCCFHYdtQWMc*js7=cxNk+v-3mTOsASeY`lR!!fnpWVrnw^Md5&DTUDf8 z=A3cD4h9Rp8GVy?>Jlt(Wtxi(A z=;1{QPLUnh?%;@nVz$Uz{w*+e;lK(aERW2;ye6Gq*L7wQrW`iqqk8V{+|Pl`mYvzt zt;r+7Q4^!FYMB%YeYE1QsN(IfRaS9o5S7VM+)8yqA1D_oZho>QWZ)~mC0ZGds@AvP zdM2LrWCDMeJThExFov9H+?sA|LY#7$dp8#bIK7f}&~d3tRkz2D+8x%|Vjq8N^Fr8B zDf7KBz*Oy&VvO~p_tf`&L}}&pZ8urglToiOE_DhAHv91E+a9s%+ycXc{KRo_s-@zL zda-cXU={VC4$9#Yqml`+GcN0qMX^T1Snta-zW#W>SFa>4FEydtsvmN5Qx%P9XDN%G zQvf=cl5;i1bVmHN9Xwj1*tK6SLFvg=Mc(CPm1q{xN}xBRGQzh7C&a3#7S(jkL_~

GrG+&N>eG3xsD2+^2*<&J1p5XI?I2v@xRapse&lD3aSCuUJ%UHfDN zm|@7f!#*>eqXJ9zc*pll82Xe-G*KeZ8xfz76HM}QC&b_)J}K(JE|ErngKzxQy7IZ@ zw?e@&h2#nqvm0!i$hT#%FlQ$r2I<#a&yKH67S{0Iz6LJ0M2?4gKEXw8ygWLNjMLqv zstyBwOcjc^n!D6M5mXUkMCfF+RX50FK${Jk@~a)=!G=>)Pj53uc6AGev%gs0;FC`{ zQ@(7Rcjs%H@F_ny#}?&~V*`)o?#>{&O|a`WgcR$xZyXOe3X#Vp4ZDl-kFp532$HcE zDjBpuDhB~S(*E0@xm@s#!yi3nsblbCsB2AuRNjlVekx`v$c+KqhUG@!nLa`B-=ep_ zr_45Xq>!|@Eh(a95~6i{Mv-Pl(S%jQn-3QFmP=O&yPY*p#=(kQv@c|^ z)TwXmX)qT=54Dj;#B7WtSWJ;m}4h5=R($-%ww8&6xQrF5g}_g~vV?w?`Y8NIDhSKzYc91lbXGxWJ;?z3fqOe1I80i@r}lj0Pq(X%k@80`Vi zMsr`?5|5&5mBohdd>P$puUakQFdiP#E6nVa{A@F}On%^k_t^y{rr((Z;z2?tOK&8J zd;lh8WaodYoe08=uz^x3rnCt2(h`uMvF-C0*N}!Sd-P3?wf_v~pvoCr@K)XwcQ0aj zl3cFJzy;ro2H5G+gq4i0$}|-1SnZV1!?-xZpZr`^+Lgeo8dd3mqy@5ZVI^1>n8fS<5p&yA(&Axat9D z0s8{68SN$=S$QqGI1S@nkgUtFnd{C|VLT88PWF}~3S+gi+uIR$6P47lI@!#0m(XNX zvFM^N1Dzp&_YSYEtRpaho3Ux`3QreaG(8_4ed!xv-N4dhi_A1nA5GaoYCRFs zf-v5KsjYTi#^+3Ell2x8A7Lyqj^;S|pKHA3##t~YP4Z@hO|5jB;;L$?j-H@%ut4#A zetnimk*@t=3)tKS*6lQ3?|JHkm;kzkU># zEY-hRsP_)o3}DggAse6)Uh&A&?bCt2X;?XQhlv*p)refnY!&Np)jjJ{>V<_1;$`u$ zA;!M#z1WeemBc0js&0yR(h$5Qk<}}aXrH!|F5UGtJ8PYr=!?VWBwn)+PT-+njijh$pUJ^4 z%DL+%dbq)ol~$u85;437`8)iH;#cT${lpcgv=L&>0fK76;oN&!xt_@>1--GjyVS}Y z0;W5^+YBI_B1;{SX>6n3HJKp>U0V|@gsI9`X&(sY_%hu;PyHaNkLO(KZZIj?J^XU& z?vBjDyDt5xc7Bc~YTPFbe$U02WH)7UElT8*g=KruFDKE*Y|`vSUPNm z&SpRSt{C{ARLtQRf02Y-Dn;7WFl29YPP+}rwxk3?yPtWpq(~SnS6#%9ErJV*D#)y* zvYM*;3|ExI!ms#s2PGCs(MA-ftxx^se)42Is=0LvWQ>*76o`&1(NDOicf{31kKmE` z>L$&;kwFQ>#J}pPedA`^o8USC;C146BeK-7Z*YaaQl=afi%wDk98q=WZCBV~aok9v=M@!lhbi%a!Pq?J6_*)#vZ^%;WR)Nintd-o zflgCAkYm;AVv&{Q>ewlzD6wmiofpf+F*!MgSUnP75N8+^y(YG%=oX4NcLD%QHPipy zU8?7C@5lKtvL%I1gfcN;JZwQoFxWjlNX>6C1V-jk4Gjs{+ccZ$NHi7GS*_s$f5?y z$s|n~wRHME-U`v|16CRhiGqSAg!m=@EnA^gkmx(dz*XNZ=@NP`U`);QN>75j7+DN}QWQMW%bA#AE1;ykzqT+otvVRIBSZ15SkFPw{?;Ld z+21Wg3lg=203(R_teN43BGZvifvC$Ql$Y;oNlDQ+5?YLg3)#W92E*A+facwIFkR&k zUrCd6OCwy3u@RM#ah19I7-7DI_hL+9Jn-*$1z0(u!uwp|xXrjy6L;11A8%!P&7c6% z^Jl+aX5!%oqH{K3$0`?$4n)JZ-xSRj9n=8_{%$O>)p1GQMUPpM!>MN;Edk$`zuk?4R{UH#u#HSoJ9(v{z2(q1OHsNQeqAu>`prB8XYts$E(Uf=fbAAmpGQqYPXn$(co;RP_0yNXIrOYhEPx0cMx(BNfA?pwJiFm%)#&tF*D1% z)f2r__${1*MlxGVz=Za)EsBZE|~Eh}7r2LUEw&@xE1?T2O_WDcE^ z9olaSw))VUzpRTiPdRTl`XQLY9Xn1Wr~}=&B}vSuuvRaY&r2_2I_{NcRW{&ORsL8#I*Y9Rrdm%rqMV8T+81i$I)J_*R3X)pUNnp7x$W&B8I!!jjGhfcfAhfuyO-$s}=; zAIwEY7I2m_K3XY&4Nn)J<&dt0a`;o@9s9Z!S;enc9vuRs?NkDv>r8-Czy?xS$l%>> zJr~KBn;7yF+jd-fFp-Dw!ldsym+p&2gljr(wY?vXUFAWp+?bBcd-X-JcD(H0-nO6r zopiMbQsksVGLY)|K=BFL_h?Oa8~Q z2{WUffZFJq(Zh12{7I@aJ)k_OM#&3@Wf}WcaP(B0KRSv@Y{}rc_0wj%8SslUoEHYx z6$GaAVl^bus)hX^dex`6Y;9*utg43E7e5WUb?Lr^J}XYh?+fW8 zzXYBMv~!S*-E3QB@2kcHWb&^$!w^l(=HXbL3FU)77c8{(oBA8HwZ%Qgwe623t1h5H zIrXdJWIIWUw+)y@R*R$CoE9=YhR&r?u8iAO2kx!toC-F4C6=#OM~8;LF_lOh?*Nww zr(~%1TQwSP-hFk*_-z56Apc)W^GGSGLb;9+#P#Nnp1XCVT;y_Gz0C zM>6x<5EAW5RC{d`0>&dlW}9LVjVULbLsk{}N^c{adlr8C-+1|ca#){O=7y1pcpntFAn07SXmZw19VJiQ4Q6iR zqGi%OMO!2MtrlnV_a+Wa-lEDpad`E~IH^R_Uj|XGo|qLwrv-&jhV9*+;)P>m_Uo#wvmGoH7HVfD;+ z?P0S?SeE)5Y2ji{8v%|kUDSg~bt)yizmCf04eCT8N|mkR>Vi7Jg+55dTSrXWRS02~ z%-&tXq-$-;peVoq>FZPq*%PY{HZ4+D6y|7D8H0tqXA+hieOS%jHdm9qeol46^_5C; z_*&k7RCRn=nz{!EU{jr+Fii>=gRw)Zp~ z*u%6tDs-}Qdg!9j#^dtgl$`Cf7p#eS@g}9}W6hL)NS}!b*_6qaEMOr>B~tjw7u&zc z2ngu?2O0)LrZ&mpnwNdg!{{bebSx(HC5J&dM&(43I-p^2_L)dV+^wQBqu)6XCc3QM zi|>_VpLe!EMDUu}Hx(nIGT-#b+XooaBGz1jR)zQ}m@Mmp7G>j&2?cMC{etsV-_5)M zwJ>&E9$Jhzm&WtijI^2zTP0ine$G0xOg4o3(J+6v71egk&H}%V%TMrF+`GqfUAM;0 z3b8>BNHV|4AQ-Id$O=xsR;N)56HG;h28kO;lDx;tg5IF)CUI%FwU3Y@(LobbNpV1J zUQW#9goW02D&?Kg{DB&IR9?`A6je3X3H?d5TGYnYQmQfYh_g`2@GcrGy~{RzPuqU_ z6~j}y2AjhMZpUhN%|K6*!Ms@Rq2e#;F1Ut8V2jpRa&@(_$?M_i_tvn)x zEOFNc*e>WbNsD4e!m)b&0%cEh>6zzY&iGp6H&zQ4bg-2I) z7)z_?{ILS-{;`!S*M*W~#TG`FGbUPELb1k0DCJWa!?Gxm4nsv3=<3HmhBon}AEz3k zpc&DFGh@~vCw@qxNtix=rAg}Z3InTWp!6fzc#>S;Z3#(XZxrAk6vmOpF)x9*sD(s_ zWKspokSx@XjdJ~QP0%>K2Kw;CsyvGfQgPfO@86tlmSBvbsh{^YGHKqeRMQlkMdnkh zXK5DrcJ}zEzR}{G_>g2NO^IwUkX(TCNe~ubu|AJ%usAa)sU>~WmJCh%-R{~%Sc(4k z?A^8wpA-vjKAj|f1IhF0-E7}%-GM#63kxp+x~j6`J;5Jcx+B9B8{EsZk&AUMQgqq5 zFeA7(ya?rBVqaXY`;M1=gaLb%uhEwr zrAkr{(5Bpq2A5biK})C|oxRU6hJ(Ntbm#)S65sdCUPF|;oJw?=d#C}(lHaVhg~=T_ ze52nSqcBI{MFL6}Xzkm&?*nQ;pqYfX-o(wCK&Gpz*ySZKH8X#pDq{z+l;S(?B=`Qk zd&e1@=AH&eurWjSq&>VE&gg1n;?Oib!J8=@rTY4Ll(?b2eNPzeQlTYa&60DP%IT?S zA!_;nFA0)FM%o+fdOgQQlh{&pth~|m< zlQgFaU7m2mq3jdtrMrZWIiCb8xI?k`&RYHBw<&K2? z@ix;xrYzma;_n?KgUZ);W{xMhH;;Rtx*~EYG}1W+c2@A5XVr` zU;kg~D@Q@t>Vf!gjkn#%kMa9S?LpHq__r;Psp9!7om_ z+1Gp5>c7Ifq+{2rb0pPHu_3=`a%q}yfJGn`_omX7*x=G~vjOxRR9ou6B|`dl5metL zx4}76Cl!!_cMVL(9PD_>j(JyhQ>2W8nPcJKioE>nM3310@tg(#9DC`9Zh?(P=8+YN-%kI6;cJxK>6DUo<@Z=2Y;v$H^s%jID~M+r;kZm~&{~Pj0 zt*HIi(71G9Z{JzF?~hHNBSVPgB5_Acybx>UdMU2g994L?Zo?1o<0?@w=X zbFR&TnLDrVfZXc`bAK`dy6b-oijFTE8`l}-M2SKbvQI|!^u=zFD#UG zRlF0nn#Fee0);7-zVYE@@WDQCp*r2C@uyHr%nhg$BbpyCr;z!mq2or>2ojd5N zTba5QV$s(GIS8$2mS-P=iXZQD>a36hx_}W+tOz^b)Ah8qfRt z%kArL9Cg@fGqZ9UM~9z=E#id3gdt@4A-Y<6Gfjni^xE%V9*3vT(j?o_NPqf0)?n^o zbw{(vN;n2)bB@L@mfItJ{h(YXG+n%j(ftEPs3p-UIZdTfAfwp+d z3V!~=ydoNa2AiiYWg=b~Bgb0Of`9kfcT`RuCJ${J6zlpx{O18+kS=CCDwrXif(uXk zdBsnGEnzV`)l2qN+(AM;H;cHWI0jE^rWY{cfyXFJi^aTi;jFaY|DQ&||I~+;|J8?bAzmp|c{b@t-R*_{OtD+R z-(0K8^SkJ~QEqg?aQPqHy=7EeU)$~rEl}JgxVt++ixafCyGv;yP@tqxT!TBo-3eA` zDei7XT8b02Xwg#sJMHuC_kEr{#y;oc8RL8a7K53PHM4TBx$brSuItqF{x8es_m@p^ z{&iY@ZsoL_c$*wx@DIx2X2$8g@F31S{~ye+b8?*ioaqAD6Z+E6yZ+NTmV7-kD9)DN zVZZAxMxW%KB<6Q~-LJ3857mU>{Po8@cRn?~6)C+$C8flRa!rEW+wMwLhN!+>{u@Fb z>qvG@;K$GON$b21)Yw+SX3KhCjuW>uJCu6}w}!J*BKT}$Ai`l41;TtI2rc+H+V-JP zvc2$KU=gOZa=5^t2G!OCf0)b6cPeK>yZ4%@?4XLA==bm_ni`?Q~9V z_+oEH7?m9-W0I{>G@|k0@J0t7oVp)ibgg#x)ZxSl$A13&6_2OZ&*}+rNy29Y-Q4Z! zVvPPo1^6_o3Prt@$_`I94x-?Vj8RI_i^(`Qzi&igioEQ{&fahO>CEhC6+Vq8KK)YE zAGxtiLOLOp6!aqH@;JN2sN-SuSY8>5K@>qu1Q8}Xi6X`&BDss_-yCF+R}8=T1RHjT z*l(Zm{#WQNZQ+T`*th?|roL2NB^0e1*A*{HuPzDkp6v=YZ7(f9IdL z%iV{XqQAM9$PEq!;(R{w2sBQC#r=hUS3dNeHB;E&&!6sxcO=ME%49ExQf39h^=QZsBjD32mfP?T{1y2c?qnaV9)@7$D7p7n_8YvptZn7io$}E?`L^y#&aI@``Q%Ol5wXlNq=*;KgF-VE<>hr zt_)oTZF+S(Q_s&pIY7F$-L0~o^$F6*gN*?h#x0rlH)&fC={-{=XgrgT*CX?3feyLz z%IV8M)(25<$KOe>o$rRVEFqtsv2;h)Yj!u`wG*3@ARvhA zR<2eAEe8ll7BR05i4YDOQ2G0YqC#q^f0Vh_pl`tR)1+0k`~1bAK+F1ttu~s@G#JxA zDC`8V#lhU%_?H;!3WoJW?CgjFY$GE~LHj{_^rijO)FI`AJFL-kcJ${^?6(c%JZ4jz z)xGYy_!ZF=o4pRJYN*%#sVSM6>189}Y^-yZ9h(W2@X@UE(`!nZQ5EebLoHu!_FMJT z$lYSmx@;&X)_Jx5Y7a27#26MMY*^Wm@d@{MC&-pPdlId_&pZegh>pOz3`$@ez$jKC+c_1kiiaXMP_OTfrspai9E*6$axfDQq{MvX z?(FXCQX8D5TQ$MBde^aB%e$A6rI+#}QAT9nJ7spO`7$caBG_^)Qeq(MlvtUFt39EF z_zepi@FawpXR!?}Lg>W+pMOfZntVo58UgF0f@LIg9%08+^OQ*m=l;6{XwQNX7*ZlX zPb+Levvm^2ox|)RdyxNPzp-obP;Y0gnRaq=+IL~v2|fMclZ#_=TLRM5_9?EeVj%ci zWUtf_TXN|gSE|Ce)RL#FYjtwq_+CCTt>2V_$Up!Y#&k9hH}mG2XylmMM#79_W@ zH_#3r?Af=WhSo5D@let*Ys_jA08+gQo4-E~G>;o;_3&tI;bV|A8rCJ01g`18MxP&u`FNtZB3k!ei0wbt z*1`>S<jZM3e1X6yPB0bx$ZJD^FN_wR{TuUEzw!t zWUD)>V(OKS?WD(pt)*Qf6(i6xo^#496;-3}h1hur*F%Xt^idry)%rNe^rk@2DH7o6 zw}0SkWYol;`p`N<9d-VlVM@q!)^4tP&I>KiJ4TSQ&I1%{_*^k=C;r7qkd>Jo=|uQa zO2nRb@3YB7ga?YRw^Jx?6B{a}uJ%Ks<9srTAV9Ozkzv8Ze$zZ@9hF zAY$-kOJx4b2H-o7k3^?IQGNCMe*W~50nY8f5{C=Dj!TtWS{6GsE+-zpbcdI6IbykS z*QeI+QfUf|E=m#_*Pa!Q4HqkN`iPr zvrLNiZLZYIll?(KOd5_(AFwmQQ>lp&4s7h>0TaAW%JAXbe3&lLG+G={z~k5MN){W7T022@Bk9V?GMuz96 z_#$sq6Y-KGBcgU7+GcIo*Qy$7!%=FRBHp9isAx@W1AEKetCyv`h^JG{(Ri{QQ~A}M zVe;<{rPvJ??K?ut^VKRDgD5TKLGt>{8m9WuTG8^FED5jzE{2kqpvpjMtQ2yY`Qg<$ zA-$gmoRScjAhw=@k(Ig;$#c92es1(621OSa_va2AFJ16lP;gK%99=Oj{ZUX*Oi)ms zzIgha&hhf)6nJ&-^Wbz~9BhxeW6FzlisgVM1;lP@ov&>o7pucnpnMRF_)Zq^`HxeW zA05sdO=B==zM*W~+&?IjAGt0~#KbomB<-1hE|JI$k$w2(q`}{`3Ul4LVM3mdz}~&j zf*aGsX;tIumIG<5!y~yK$6BW^RiE+)*S-8jEfH&FkK#yx{j8tFpw^Z%I$r9laM?K? zxn9oFrA`?hTwwg$oAs={m4~%J;5Qb_X0Fc-({7R`WoD-w zqw=7ao55YR$b#!vTF+!EQsAMFYldQ!IA&jVgdnFVr{oAB+5+Ybq)32K)=Iil<#Iae zJPN)Yb~Z&kD3OA%3+Gwzkc;B>ZP%N1C2fCQ&NNq*s(-ej6v&j>MC05;xON?iXdP%F zeliQTG8p29$F8cU0Lsd9Bv`sL6U3WG^agRhBGeGXudrStE<;?Y0+yc6ONU?VjXs(o zwE=t%$KD@5Mo|^k47ftF`nkCZt0eM)meKkk+@Hx4L(RF-LX>C-_BNAJ>3K@~@70mi z)IK-o8{I0aIY*6fy5|>Ky5Sq=IB2t#a9Z!JgLu)WJKi20Q{b3@oHCV*aMD#bPp=Nf}vL1T(LnoR$d-G}knhB+; zQNN6>JGuIVOsaR%33xAkg^S`-ZFjc=9pj0E9frmhp^m$SuG~IX#*R6)?x`OkRtfA# zd)gs}0j{qz7KqK(7vqYC%w~K#DU}(qrkLajn26*>cM!FZ+52lHZ%5sVZE3l55T^;; z#XmBWH_Ba@$GrewgHTVizZ9~&_yor-ElM$pZFO^&VL|FZ&o=@$Rj+dVP(B4RS1bp$q!hhKM8 z;tc2!k5=t6{YdbS;o*C#BiQp%JlPLJX0R{`^=zFr`#wmkIfFhsTl;WKMO6l$Q)xI7 zWElA+vh9u1jcKvJgM+Bg<}SSJt^aoNB=qF%~ zb^el4W=1Z_Pt#Pay1gdLdC%NLT!3dHmFq-6-0BL&j&sU!k|C>?i0ZVxg*g(W;?}8I z+SE`?DIIo2Pc_I9!RSKkJQkX{6>j!AB=Me=eMo_FJsc&;kFMEd)WLOVWkltqvA8Cc z^gd(}wE5@?4mnGFSQR~vCb8KQ+DY`;>Z`EkD3P`YiVX4M(n$P~dH57SpF zzeqIcqlj49Ll(v`u5d+B@FfSAI@%&Yk#toF~KKKwqe@{EAYFjb*56T~&44gE* z<<%Fg3`eeHb*(zB6nBJgVXq5jJxm|H7K|Gx{Ku{9jtPzppRLU{TS&c!wns9&^BQ=} z8>rD6oW_`+u*3c3X{L9ZS;gqemn#tgTW{^x@pjmtqe2m1;ZePN$z@LF9C%8vu1I;l zOJP2cb8Xp*LF8EYVD=S#(xc{KfZWJgm9eAU$fDpEGd30>FyT6K{SnXVxKr6!&L~so zBatd#Gh8h|Y{7c?g6eDp8c03l(417R(THwl)E!-MNGV^frs^;d{@KdNOp#!GC_G@M zWUyEsGFuRbcMUd`lM`lr(>j&H@8W;G)iJU{*MMFd#2=frV!#9uX-WRvl+{@m-mJmQ zPcPjFSRPruzO=dlBE8mC=rh+W&n(Wv71O-mR~2Btvmk4)=hfF?WvCG;n=UIZDTj#_ zDy+o0vF8_a&^d+zw&I7U0|)E79qc-=F;GUYsrYRY4Tng4hZNaQ=dnYSbCRO36I^fb zf>@C&pv{x~anVKATv9W>-GX!=q;xm*jY_(}O1w-6>v}}hY>`Yu7*ID%;I{SdR@)V9 zF_KR50|IDVyVWOd8FkHaz#QNi=2(vR0XkJb*a_c4PG?Vh6yUPYwD&8SC%u(OZHQzFVsM4;(&EW` zkN^B?-yr_eFH3S}fR`-Z0IBI4nSlUMw#&kV!YnTME3PfSLN+fe95dV=qlE(e?GxEu zX^%-?N$P`hq+l7_RDQo8rULIf<890*Df zh~z41KH48@A(PN25`weZp zt=DF%CY6TWz}X7Be}XC=t|+On!amgzrHs3txfgXuH&$l#novKGd1yHmMKi`iKk6Xq zNJ!V3nO8Ro;d8KJnzJ0kF8aid^aY1U<>#m@T6}KC!95Ik@7KgveE7QTf`dLahe(*G z-S0R3u-=-5nn&z#U+j=l1W`g1F^x)86Ehu-)iAuLbpi}>&)HB6VF<-uTr>QfI7;=! z$-5(^Q;RurV|qdeuc+*UfFdc0M)=76Xu*%~oC%C*TDVVK zSs?Lon!#_8x`AzE{|B+9@f-J}ANQT>IGtOxnsT18Cd5I+rwxrM6RV#>s%=Kyu?sYuvl_YVRabZ z6=S7cM&8}#wR+ok5THUB4$fx#B%c7HYN36eAW=7xhVtgSc7?Ev6yKg;vh)~FsYFQu z<~OqZ*AP{dKGUPl?YOPTE~ois{3=K>=e7;;I8Vrz)Mu(#9)6z)rdgt0I=Jn3bi^DV zu5!w*xl?#5GO|N;yShAjqXsn(b2t&QZKdTQ$9=9AW3i3*p_thfi)RLT(%)t;i{B2F<5a2}7+nD@eh5g$PQ}Vh)wcl?L1v|&q z;Rl&o^}9P~FEgdnUJgt6znSi{6#0~zI=#7DXQHTHSSRVyr&D^%Qd?c3+nu-+&4KtzHdQM@j6967>1?Hym467pHShAcpykqjYoPUB8J&0(9~I^{2rx-a z*LeudwDcM<=$t4 zXfy*vrTm85{+fUj66Sd>@^e+j4gh>QtmtlTk|&jR`6P*(I10A=z1y zE#NV#Ff^(kZmevyY_+v$pu|tB%)K^aF=VJOYv%X4u%j-iqcDjAVTj?Z%u_}u@Co`wHtJxfk(hz#l_?t@F#IgQ?mj8)&iHtDX=MTgaRnNn(Q}Jyy75Usd z#~#&$!`z|5jK%N7(|7@i4Yn`kQoCYkv5<~TR2tNp7nlshJlm)3zxAcFK;f|mZg7{H zhv>jY9lL64%iWF-0!>wQ*PLtL6dimaFL|3`RGz&9-sq!WRH7o_+ArLik{wm}5*alM zG0)#TJRzaP#sz31RS{no1?$|O6Ii8=`_llyXobBS0qHGX z5+K1>B9qchvaY*`)k{T0@-7s4nuRQa^CmmaBHvD?n{JD4Q|8x`&&!eIMN%1c=YKU* zcVQ|msn}cT(mmIgC(L+0gynRR5-D6TNvFSCaa-W^zk~W%>sBrn#CCqg>Rip^1j9tG zN(cAokOC9Sg>{?#$6o^!ZT|sEnPLnb+#C8JUB_Wwbza3hYR89GDVn;J&{mr50Uxhc zkZs?#XYyzHzHR@7h|{3w?Je_OkKDcI#pL5#5E<+;zBx}?z5Z0yY&_^qeT;VXg5Br7 zRm51Mlpj@@^u6{RI@zvM1d; z*&L>}{`Kk=lSD;>Q0XoBdw@W^ip%o1^;``TbPH8K$)Ycz>m4&b%aFUP7dBSI&i58} zVrD%3VftFkKWy%gMW_1rUS1J@l{nkd?tWz_N;2<1&vte$Yv>Jven*p}L-!PnlhhFr z60Fi^Wv#*skUf}At)!S77ow!wSAl4Q0cM|)F)f{PBJ)!`6pG2g4g=LIrUpKN0VTzy zg|1X01dPh~P}%kTAD-OPU)sx@7_}nw3`VT+apc)(6~4FPJWp z$E(s+er>%QzahKJC+U?C!gW`wTioNGPg;IAn^vTb1$v_le^jP(YqaZkMU&-8lpwTq z{?dtgURKhZof&;AxVzPl1cQYqkrxB`Op4LNxSPj;W{Bj%_r`TVcf0Nsnl|mZB9)7j zcG(zEA-qmJl5M;1om)UZ5QE$9izz1n!8rFaQgz)AdG6)fl_R@0mbOn7yopUH%JLwW z-Lai`Dcw1T|Fv9)f#K*~-@VRA#*Ekb&I ziV;5m&x(R#Uu!c(#5KMlA0)IGRveHBN;bK9nzPtd#Q4OChv{3sec@pAtxhCJU~O1% z<*Sik8||OboPFnJ!@`7{_n&D1**PQWSeO?xcIz6J7(6P<_QN0TmzUZG3mYUacn8^$ zoKdz;96&j{&oF0*HkKynHzBT*QE5QW-i1ecO@Dgm889NH9Kd>3Y`&b-qUHd0wu?&rCjvTMFEG+)Kryq=?EX4%m$)7c3wM`+1fj9>XaN7V|o z6&j_cZIi@S+$p{;w45dq5{rDV0eh-Eg1v-YQZsp19~v?6v02D|;cT2)rOHbk5reoM zSg943HKrh@TFjJKCT{rmtyf&9oYWzPVWc|n@h?i>PJIxf_CdHv#CU)f^p%R>12>^SS zW>Zp4Tqu+l&t6eg@0y~ljLQLRKG))FW2j{xQ|O^v~drS~>_Er4Qla+{Wn z?$c&cjL;LXA9lAifSGJTK#Z?Ht1Hu9DysUzSK@VCoT)Ir6}RR@nlXV7gN_DZ&THJ` zl$rKnj@Ay@VT|w+Wv1L5}=x|gT{C&YkCGkl#_gsrgbJybLo#_U8F={jG{Fq|=6lbT^ zQG5n-P9Ds{P2yCz=6FuDJcY!|=QNTa7~lrgiE_U@Vy5-0!&NKj<$x#R!OGkn@Bx44 z1HcZKK!6p_ik*kwz)3XceR+Hd!Wwmr?TO(Oej+XLUUx@e2+_=hFDf`$ zBNq*FM&rt{4zsp^>#tW$H@F>95!1t#^hz2|FUqX0Tjc{a=PXvBG6T8F^jU zXOF}?XQ+lm>s(M!S{^Erfd+${m6a0#OTvyrE%274KvrE#L)m7Ql%WZEXgRda-GuH^ zc-6|11VD8l?r}6G&TK-fEXrqy?9p-{K1iq?9v0ZSHr`9RgOvmod9o}LYtkQA<^bD- zDl|mJ)} zYe;m_OR3K$+#K)`%LISJ8Iu-i8SuR+-V?0@!!{OMD1cK8qIFMpr`UQ>P+3xKgwsp$ z_3qR|(s8SexPC6LS%oM^5a7))wN42jTz@XZzmpf?60Ub0ic-d`jG)1jrRR8RCBh1D zYbm%){QzSDL2?d2)d`7hxLJmta@Zp1#WGiMG*&fRdi{X2dr>)WRGvRfx@G36`(?)9 z$cal&bGcLM@|1ehkd-H%+Wm^xA>bfSS5>6D=)iRYEb!4;=xyV{57!(`!i8L9P|~wx zT5mFbUb>~h=tUak@T0V7Tx1bY4IwYEboo;R>Oxeq;Jg8Z6VIt|I(~e+fB0zwGk(#t z{0V>S-YH;J#wfL_KitA(G|T(k%*mm+J+SD>Ta%&G#K{{g6{$O9qv6#y_LiHrP=A_- zzCsp>LCz0Zs}fcU0sko4Odr4m+8t-MOEF8!>N7eBR+ga601`FESU(IL<~$U@)!|3h zfUK8+N5#>|8bF0W)&P;*c12^-Vm>BC0g)#orfNa_8d>b&6VDi)VuK0^6 zyAhe*{1;kU(@2%80kRBu6a2rIfd&*u?xf>9vl^iBXd7c{p?rQKvF)wrhbOi_M>&!S zfIrF?9I^#P`8nu=Sy|~+oJT}1Z6~6&9LRS&%*P*!j5QSF93bZ8(8hTxO-0OpxTy=y zxKoNrO;Owup}cfN(?`ml*9F;Ra)M1C@^-G)RKWafX@qtFaSi|<9y6kbtld*QX5(y2 z%LXDSFGeff)mB+q5mO)0N;<_W@^I(YSyJR>n>o22h{I2x;r8m-E$l%aoD`sS0n0*K z?qH_sZm33%G}vb5FI;V$|hq|7gnSzm- ziaHvnDG=x{l#+sM{Jmf;E8A?t#U!*vM)O2qkWk`NQ#~~qiflo7GJlaEziST&EiMt- zi=WNNnH!p&|HY|mX{=j2&N8bSBbYYZHtGI!M_6s^Vn3^A3G~TXTQ`8Vm%Y4v3~93O zR3RbrrIl`A-u9=@BH|8El%dI|`bGU86wd=(uV`i$8{&~0ndC$3Q}v7D2alWMC;Gn$ zKUQ!Bn2$}pV5#zfm8it8m{L}@QbAhWaIqPPq@Vz`d>Jys8FrC{z zzO7ul53o>-S3b_G0~g`F*mrH1oyNP56L4=vrJ;RQEngmK{W&Rx-d*%KTLW z;133o2h!ZKxfiFXeXrh-K-lcu2)A`5ffQpmMTO>!bq$`0rV0V2^PFhS0n7!s5wNBA zp2MxFeb?hOdg7No@@S!$49j6zp(3H8gee*8$LjBY@};DZDGGH&SpSF;86V1pG`x>)Z@SxrT|{!GoR8HK zkUFjtU9_ZoxaXq3qh8%aw*>liCW$nuSPoIHu<)~O^3JDBL&%FJvj#NhRwZ7D|g(I7j>z(iMk_xB8;s4o=Gl31ui<*DQ`28b$=Q!7SP z6$nGNah_nppQ$k%p}jEw3HKMS>c+eQr^2)7Nt(CHn2Jw;E=35Q`ur{qwWLPz-%jrussV?PM zwUABuG3}xMQVb0ikC`egm{%eqA&`GL@6J0E8;0M@$rhUcaB&&9x{dE49LeVOHMouN zIR!&=mUN`&a+5}u6{)DHsTL2e(w8HJjY(U*C(TW43i~%%D8_Cpt8DuB3*F85_QWt5 zqfIDA&aAc$==Nyed4C&wmI3SU5KWE`NQnpJbS+9;S_;y_9bK9dTbp|_g`1OY+dnsu zcndyv-+ThW=t`y8S~&*Cdy(^3T4*l0r3&{EdA-ia0C&RT<7hx^H1YAk012f`dREj- zmOVK!YFCNr;EO ze^(Gxs*6=A6Nh$K&s+=o%a6VKAYSs5#P-IEz&FsGn}1NE{Pbe&-0?1bT)qzn4U{E$ zyqZbbe64C%646T=b7U*s#D9UBsvv5>ME{6`>b`({CGNH`cy|^}&SP-guW{5jLE4Y$ z4@mFX(e=n93i>I;YLU0vTS45}l7CRj^cnIXEy+b@Q`L;vOJpZgp2)2*__A6<9# ztDhLBn`HJnA2!0H+aN?6)pDS_*Z(z;KdyIcjo>c!8zj}y#j4}@l z9Z_!HBtQrK>kr+3prMLC5BQruVot`_ytHAY@K-&n^Hayz-;n-~H1^wXk}qpyst44a z>MO2)#Iyf3#7jgfIJWo)d%&r2@n7CV`K$KjS{s#?edP^0WP0ksP>;RrK4{m7zwqs^ z*jAFc-*VnH6f-jKko3dX>;rxxK#YG&IRi^{tR67?DQk%?u{@v+|7j{V46facHjuk3|_KSPfGN|f3EOyhEu=ykB^ zAJ#}U^zW1!6zu2MpVj}JX!d*dnoi!>|HhG2Lu>xTGnD_86C?i-^6kw1eKYT07y55Q zD*g=li+gZ;Azv|9vdAaD&=ro~d%1(w!*!pn{sqez{1xc8{h?e8=@01YH`>(y9uk3n z4~f4oZKX~1(b(bBWd_#ozO2@iKSRyMMv#$8$C*Liy>jjP6dAvb3KW52kH&JRCKO0p zj(t3eCHKb*?r${BT_zj!Qzl_IZ}rZ8{b6AO!!rEFX`bGV69%2CThado6~ymLWb;<` zes7~81>lX!yV|NjnE|hkBoa4cuS_444^NPL_vbhozE`bj=X&+$@Um#id~x*ZwWZPj zXZ%3KudrXSON|G|{@zC;8FIUZV?OltjbnpOMd4mF-JI(9Pjkx* zUti<)rPJhUFE!F>HRjV->Q@;acIlYLR8z|5r>FJHu7NgrWDg$XtR5(ldVS?<60b<& zp8RoV_)(14fGH(J>S;QkhO2J*p@NB;{-J+H2M+G$SJI+{5?xNUN zxdFFvSV(jINlR|xRIoRR_xuHkFtPreKB3dj9xJm_Ms8YeM)dOBrO-T~#mEdBgNi*< zFBI!I2Ai4c8d&ebpz!n; zP5YW!IIRo-eSBo)JKJfRn!xF_ifu{=s#)#kM5XP6FRvi`u8kv@`NFqW;KBV#P}%bu z8>|C;os=eZWXTI5sqny7!^R~aH9n=r(XdFvMB_CZjya?X(@oENv~|MyWc{X10; zCD33QWWJDBRRf*e`aAL1YkF3lT^jehlc--%t=GYZN!<*T6SS>BXCxqD^nV8=WKJ=Hw%irGyIxMu zQD8n+5K6FPu(0g$vJ$C1EQ~-f@$tN=k3|y7Uu>~?k^FOkS&BUQj2>oNXgDJlAGuUQ z11V3-9|po$%eKoCO5J|CT&O&lRG!lh`A#Jc0ckKSuezx*eZ?k$@$oLF!stSg(KFrZ zNS5N>=k#_ayvGyuz*OB9{y`_04yy{ET~nsp|DZGy{)6&Lau4ufmaDt|k=y8#W$CHR5f0y+U;b6ZJ|#L#yHDrk z!+$^$;f%Zv`hb5?AQsflSR2Tg=CpJBp!`Q^y;UB?sAs!B4@?yZ)6Ts*x!S=wm`j7h zv{Z!U!zRo7GQXw$IT%?vO)SGwtox1GtXH041jBs3EJ6RU;iGz7%|E&7gg&SzB3ji& z71V3AaxPxj-?jXfbs}Ab1v3ldi*&wX7Q~>@VWlHA5d?OQG_Lg_vKphy6BGzUn*?`o ztR`QxpcY!IKpEWTYP}>~xnHj;Brpw)8!H-XQJ3-&wSf+HVj_0y0>hr>t(u{k=&tu3zW`owb4Dz^6Ya{(KKq&?&_>(yGiWo5AJ2e3%zIy`6q9quxP&h@Zd!a z={MnrKo4v!x!La%UXQ6~RbO^F2JzB(y61)4B!T3$I9O#6-dK3FCV9TCi2H=o9|RNQT2zQTHAWF&1B zJWPtwuw4CV#fvb_a4)rVxe=Ag7n95}kVG#q%-5B@5a;jh8k8s3DkR~XS>&{!aGM2@ zWlu_Z9GlYO(wN3xw3i*tCbMNzUf`YN!8r})0+s|wA~>$M{o_x1cIob=3sk7Gw>qhM zK4q<#l?KJ;;(&*senl@t^?7bu;N!0_2GyM4EGxmACM>J*3#!P3FPr z7pS`BibPL`OiMZvSo9k5%Ck`=6m!9BL&E}@K$Eic1PjaLQwfRu^hkzbRJdiNi;y1JVusoaJE-Uf?4gS9 zmBc_=b_mr8SV>3)5plwP&Arq1-_kY5?OlcyVt$$2sT zuu}AP7S1yI%b2~&cw{(As5KuVBQ`bzUK_jpU5M>7LNzgPn#bL;wDkwgl7kR^6#^)yQ3DE(tjy&zfs1cV*fW;&d$qJ0)HbL~!Yg z#`8xWUgqme9kyW0sJo8}XX8vcSIy1qKICHg3DKv=p4bDY5N&;kC5!uqoB2Cu@3gWJ zwRsky^?C2>MGo7^_yu>0p>=@LPyG#QyMAv&VCtiHwve$c@ zNLE4oA#U{acdD`G^zC!|rw8Xk8;eo&%<7&SC~SyETDH9xhh~$;RZr6bxZGI6C`V>{ zSuVN&Q>=+1!z88P5?gog`5YV*v$>}*^ja(`W1{hA=g8cg{m=Xl--;VV`OAg?{Y3W1 z8is?nZ6!b2nCgZi=qhwCL6ii*I1Dpf82eqBkdd|uftZyQuB=CA3)`pcX|TrXjT9o2 zr#>&;ur-|rpJ>6*KzpbYJ9qbUgI`oG)fNlvs&T3 zw^_E-Oq)vnd0}9?>?_JLT>*jjGjIAdcFanu*#%~qSyR)#N%*z1o3Zi`zDuP+CWPh| zf&IqvCqy3sTjm23KsOR%}g>v0evgkFr1WY-IHh!KHqX^(Etg`1Epsy@D0~$SWH>KXA4#^>YD7*9( zj5DXziBTkHjKx2wn3m+dD72+&u?7de`v=9a|1$X`U>F7|ThE2*h_-R9!aG!#IDNTz z^*PpjHEZVgIDIK!CF5c}onmn#K4)dy2ylf6V%QO6WI~DQ(?iHfT_7hAj(C6f$aQfJ z3F##DX|pZc)0pF@qk0)pz_JpZ22=);(#uyb749iu`!XP!VqLp5QD>S_Z~ZxAy@gLd zgCs@^Xh?>+dlC@=69l}jc&@oQTJ>jG%oA~kjoQKkK$$@U3;T$QJnj0K3Thy;5cPDV zj_6Fu_=G%*n4Pw_8H+1u)bCeJjLCW7$KIX9bcJoN&*w5JLwiDMYfD4dDNBOKjFVss zktop~LY1WFjPEe2%PZ>2D@$raxpLIo9m}iA^To!|m8Fk;z=|TT&{LFoJi#pXA?`U_ zbC}#e#gpjtvY7eOKwj*kyh=kCa~q3i9;*|AQb@MKW(x9iC`@P!7TY>%535kop{Q|z93#swibUeVdWNPyY`cGE1`VYJTPgetBCN1 zk}x`$Oo|K{yIb;EJLCo{O%vFze8#trp;jreVJNzraUFsE8bNyWm;-1K5p zQ|x2n1kRk;wl6LAy+saG*6fPp=yQ~?fF@qdvQvH{iuP;Yg%L)gZSF2@M<~dIHO46j|GH=TL^R)Q5$ux(=H;!~hqoLbkcud9e+=!rC zb24*(dgQjBLP|P%Q;LAasgKQQPY7mbCOHMM))eEIHa!g=;3cn?vOJpL7hVUEV-^>1 zm;X9+{GJPmg_oLD3;sm9wcUjuo4IS0B0TC zWn@lji4pk#i0rdn_~Y<#>;_JD@8)`FN-w^oJV;M)hj>-A_yE9Q{fVSInm%7ykA+S9 za=W`Pb`dY~WM9B5vYc?m%um}yNP<7S;tkSQTHfW?PQ1!RuFHRMq$`eTQ4oU<%+L;x#J%V6c&ylfdzc2@)4V6_l}F?C;n(%tC|k*aBo zeQ13y!Jb(TSH9CzgLx@CJmPkP>8W7gka3E|JI?{Me%L!ck#wjz|dR|d76~^#Ei?Nu- z1NI!~N!1{CO^ESkdaoi8tkgwuH`p8`APf;*fhO?(%a; zfadNewsvoQESuVwKltd9q2hE#1sCGsZnEk;KPNyP6?fmw!sMip@Q)xli?#NYV*{`Z9^Le2sDJ}~zrH;cn z%G&Bfq|6~ilHFTqaYk3ZE%X;O)4iU!3yq=}BhuYAx#opAj`*pOM$x)H9 zhdm}$GzFE}KF8?U6Kdu`*qe?tPhB6HG{-1B8%-Bm{j)CKzNYz$cIa>}c9c0;PHpn_ zH?1m?GlsE#pN+<{;rGm%qH|zmFT*M+BRdn8879t2hr3Wa!KRv-oe?{7alSF=0+E=p zR{;M(U&4XOfIYzJg)+2b_!DtmSOXN*C}VCFsexbOlm)8I;JL{ch|;gR#jNecP>0He>1XcM0r*>@9%LPm({S z^h@CsXtYZX>#9`N$rnJ|BhgvmNTYuki@cgg!IeBIk{ZluP$;)*zYM6e$lyzp zv<}I*zazk(#89Zdsmqr3*ir{XOUj$Q;fkA6o?M}uT%oWm$JJ#*>LI96y>rT%?qaBE zEr%^C+Zt1qoJ+2VqcP}Sr1iW)M}duIjS(U)iDNe=bLKJyE|#=0*uW$d#ayDWhJk!X zSyofQuoRzM*a7#SOMeS{K_?zX!KJq+Ev@nKl9Fa7r#r62EX0fJ8fzC%%hK1rs-oH~ zf1~lZE?~FFb$rx%&2M|A(&c(cHx{o?R)u$kU^iOKXafa=<&sJB@;S1y_{HWW{4e(2 zGOEh2-x@}xq@+7Ux;rHVq#LB0O*b2m2Bo_@rMp9rlH7EsAiW8dMjCvtjsJ7s_j%8A z#yg(x?-z%|vAuGwb*(vnzd5IOcuKx*_>R{F63yy6mnH1%$dEc}hVo9O@-zFiwA2q< z*#77flFXsRRdPX>h|($1=$~fG+(STLNCnDd;&$|iaW7+GGz7jVTHjA6uSMHETJ~9S z#cbo9j!=os}Ppp#p#`^ zC}|W2`N?$WHsIZKXc|Vqv7v#nqxmw7G(%f~QXr&ZKRVcld0_YK|x!Ng9M2X`;vrFC~%r zLExzUd5UDf`X!PCWgms2s;3J$u&_HA(WX_b?JzL5 zA9$Lh#6`kwkIr~At4 zWMq_UK^44F`i@U`3G)WXM99MWdcljUKfG+d2&Qs-YbjAkk=t!364F$yikAtCQpqQ# zm(K2*Gt6G|E2m}Ve5%cv7#u+-16);`$WE)K;_>O}*ym|j zvaOM9Qt@V0w7LwVXfoI;tGe@C*<+wL7L>!QsPIbMxIDiT7ti3wB2wVqyKJ8(%G-~9 zSe@VHU9S*&F`J&v_ycs|PyDqu0&@duAWfsmps$M&k0$_pIxoIzi^xst%7`dluo!Mb z6-V&3uw<0Cx2O|OjAXJy$>niDgmykJ?4YLHJ+acg(7VNhl2ML{VAZltQo8Y8)|7Ze zIjWuF-?c%kl(C?z7}IJuKPq~cgV7MeZhK+wH!G6x=Ho2(=YUu@=MsCj6{6M1DJ3zo zlDqD_?&+02)kgs>|4GM^(1u;kg6^hacK~{}&GICEU_crkD+GdOXZ14jd_nwLTIvQ* z=_Uc?ZkSE+D>QmoL>HdOib!flEHxMd9^WU-SlWeB+&C1yeRsoMcx9d&!vzq58}wx+ zH*TXXhd{M9WBOiJr)hCU*Le-|Q?qdI6m|`JdS!S&gJzEHoE=#jWt#YDjdalnL?$DH z`#caFP^Et1D;WHiCM>Nk!A7~*s0y8bsPCfWv8&i_n2!xJn#${ZfPp8d5Vfp03BDJzze z1Q?w0w+)Sa4Ma$7ploVJ*-b(_3fjpaCRPy1#R`$r{j5w?@v1mF29)P&%-*e~viY-x zM4$@4GjmVdP{NaNj5HmTHcuC;wT|ax%CAmuY=NZ_Ho-+}Z^nZ#-Q0lk2?W}aT1Tb! zZtcS`uiofo-QGUC^4u;scMI*T3HoC1!tai$aa{C1G29Br&qOb8Vq|vTrPGm#XQ!b) z9$v@e!TvR(g_>w$*yIr%$5U>CZxbOqE3D=cujx;SQHFGPB^7n#RjIU}c$(qVONSug zH@1#h?}Q3`YSa5LEsPqgG8SR%F;1kkw1u z(SJrF&qIVwEtWoB9=Cb`tYZh}I7IJr9*Kh>kr=T)@kS@0s`9Ke1G-b=0Ii9g7*RL8 zB|I5|3F?><0m(r8e8)}m!H{=y6CM7H^&%?6oE2_6hz12WnF;G%L2qhYzlt^qiZ1TT ziWVnsGbQ>?k2k2b_Qw%=^i!siEfJm78>6(}t1UQ5)w*Cerl;AVb&cn`ZyVtGacx$J zbPq(}vA~PiRpnx7X>lK>J;#ET(vyt&MkTUdSPi$=hyJAbvZuVKrQ)q&$ z&_OnN=_k$ZjaEG9=?MZ=h%(TmFVda_N&f1%+5=STQQNvVq_lEF6v9dfVa-;x^JA23 z$yaU~%o^BgQkU2}Qz9ucRNi4BULMb0#`;_1sH;tL&fp|AU{Y^Gjr-CvGBTnu40Ux4 zQPE_kcBuMYD8&L%Nc69O#M?>`vHmV%fM)vl5eTbekO|^?i!uxPS%}GXAW@<2$um<8 z(>H9qtr#D6EP0K`9HNI2^*PIuEvBSqh6ezvN(IQXzRHh1nTLidvk7QL{cP9v*y&EXj4 z9uMK;-P&fX-bq>tL$t6d-71B5ODsgwPhZ-0lqUrAA{rXkb^4PtdmLY%MkPg)Ofj7` zC5~2rekG01+VCTG9H2opHA$219BuZRl-ZaN`BFnshvv)X2vu6^eAh%!Df>hsbM(^R zcdhnA$*%MNz$NhGJd83+A&u~o;i1?4uv7DqlZ!|<;JKSSFqME+t@G(_%W`(MX3fwdnMbV`4 z3LNFLvbXlO-v&2>qwN%ykXVfRE+aZ4OdQWbavGEV79`}q)YZ`cYB0xnp=hEHit1?j zG~S;a%|m%A9Blod$(+w!0MS>tHFO+Wg3oiXxWZ*|DIGi;B!a;2aosLq; zlYt*j-^z#ThDT)+Ow=`$6<(GW^zJW0M5<##RM~y0gU2va5L<&}HudK_;FkH{@Dc2b zPr`ht=4ZNFkc!M1LI%FY!fGV++8FGs3r+onm=YPDMK#uK`qRu}@cNiyNWu$y-ZWQL zXw+(8*mWwFl?}1!g;2`8TOv_z-(u~U-2b{=(1yw z#rJq96N{vZ6{QzYz5IblP#W$poEa<3NyMX(udE~xZ?OX!*by-q1?BTg*b-U9#11vP z7Yu1!Wwk`{G)$Uu$8(J4nQ@MO`(oWXDLh(QS{yqRR$PwZ6xS{=`Xsohzg;5bLEf2M zoxKv98NURru;0l?JYQ-gVnhRhtCY#Q*JjHuDamd68laMSeq!VVt*0ms^HFr9HUgqA3rqxsjEf9-Ej=SP8 z>9aKyJiC5BoCUcS;$u%2s6I70Z?V-j0*$?Jakk{={<5-3yooRfzh?-Joh<`Gqy6-V zhc?N*g~}qjh+P9joOaW`-E|HKY3<<6*99B=9P za|JK$S-831HO{*J!kU>DL+1u+AtA`ixC$g013U#~c6>mLTtA`_KwFaFQ+Xx}O1{P# z>h-L%vk_V@o9TS3jU*_<*q52#n6SRYD-extXWAUn^*W7?%(OCF>(izkiKZLz=ck4i zYNS8Ssp-C^W`=GTQ;KMoFukV3t0S`Y)NxM{t_nx1ry||}8cMuwlazNtA7qUkZ#mn> z#h{J|BSaRN2>tsU5#f6!<)@q~-O}8KO*xkDII7*GDCJ*z)1(;dQZd_^WG!T~I3^2d zZ6k47>hBw*gYW$e3yb=!OMP81=rqD+X1y*_(v!f#m4!VO`5YoBUhYWgx|ee1yHeZ& zx9z&?(EEr3BsgOa4*vwnGSe?n7=E$T6E^fy3hXXt?M-eU53N4)jD;v}KG|XG@ExW* z69+t(Yx(Ze4P;|D10zpUbuFj{nnE>yqQ!qyGFvX(pMFZ3)F^XeJW{WlN=HRMP|w`w z5j>Xq!h5p=P{n+y_JIb%kEWY;!FTVSWO!vwS4#Vn8ORHj~R z?UdoJJ8BF0VBgB|TVzK+JL|Z8D2)THLrXG*vtIjCFJm2JMCiPtJPXU~tl@Ur2ue~z zgR`@s{!N*J2GZv&SD_fzW9`lZg_90X4pu`^0|W=on@LE>JilCxU8H8|GrN>t`dhC92CTd7RA1ZJ_2>UUDP8m zskD_zt=^EE4St`j@_PBFEdN>N&jaqSDY)l-3~Y%J)wNOHHYiZ*pEN7@69k%adA zN9r`Ei24|5R3zc53>&YsXB{)9ETucqh?a4v?e1y5@}3bn#nR9y7qmVr`7F~&eAg>K zN)p8pkjtl%^1@OU^0^tLX_v7qhQ;UTE9F(MU(*40X}P6i!-F!z;;x=$OG{yD^$C;O ztXDHQSZf%7D3s_sHr2O*sO{iVqE$Ts$Bf|Zbp;a}+q3&Wa5SOI*sR`qE3=k#>lSmD z;|<VPh9hVYM1(-A58I9{rmEwLzk#9VxajaEReJKxxr zG5^sy((6R_Rxd8rDh`X+E?pw2)tzL&$`R$XSRHyY;G{lG? z?2yi7azt%K$VP18gbt0Jx}HxVaVA7$Ve2W&<7AxO+d`FzvxPMmxS1UhZ~ndyL!?gAlH?p>(Ub9TI%f-Q-nP zwn}#zD@}g<*AeFJN|^-RKMdsc6t>3P0=H85tP-AYip*=%I5s8l+2Xs%o7s{t-@&H9 z5-=X49J^5af&q=zE+(K8WGQYWHg&QHl~81FVUC@ZVOIk#nE*(COlML`%+9i^H<-*a4d$vz`=dwtZz%NC$W;AK^aukts0 zd88ct5J9;@&XU^Al|>t#(%L{vRW%UC$YIH0qy1IZWp!6X$50;Xz2YEjpIC2!6kqFc zY=@FP)kg-kDKBR6wfFsJOC6x z6qlbp{gfr(`&jVvyeLP3Mdt~3p6F*;y8;6X)S1a$V2NSc=W#TUwW#UMWGdWPbaVdR zmxXDyQ(sy}pdD@oZ;446L8m^wG`}94>@&xANt?iyu|Q6*fd!IQkXqI&1&!ZycsccO zC;BTwlH0=8V*2{J*3_ptDv}N^0!07^;D#!>n&qJ;u0a`6GYqyr)x^KvN2zofan{D(bT(-95{BjsYSWYuz;x z>pSHcqBYw-BJ4jwbNCEj9?*qmj?3Kh!*(~hzPX%Xq{!ZoM7*1VBk1Wu$I2!W=K@t1 z5ZF{tE9>l(SRv(It4SNl#$XU4zw%8VA|wGc)SEO4^iP|NhYDkm$QR74VK&8E&=e8# zQ6X{@JzA^u#Ilj&@=sEAhb`8E#3ClIR81sJTwVbQu{?n0bKLgZqxkWxxvdKW96O?f zldoP2MIsqUDwsC!s}j@{#zB9V%fh@MFbQ4edsm<|-id~JM8}3g-Mhs0q!zl4>_jOM zvItfm;ntyhQTDX~PlPJWgrj95sO^Qh(A+z<;RiQK26F=gQi~WZftiL(HWA`LdbIe4 zRXlL(VA9eub}J+cbKCye}ITvQ!Fi#Vf!G_9s7!$fH-`MNi6M-eXKlw)>X zu}yo@c(gl`jROVO)8jZ_EcPik@1tEziexZ8PH#Fku9U!NIQoH$;R1dOri^Yhlp~IR ziLI&)dt=C|&WY^3_39gLg1ea60)n@0uS~w8%1Sn@$;8|}$W|vr?MkQGS=XfA&I^4K zW&5uo5mOCL7~!Qact?+SXYjO4x_m@LEsv5b*vUGW95u|Xb+mUh90+f1iZ_-H#3uFh z7!4(dmvPRkI~W5;50}s`{egSti?*m~Rou@YvD1A*6>5So+eD4p&)~ps_(QPT7E~66 zg!zmXq)eowT%Xn9HNGF*eQgozeE9yl2XnIZ-k@!sr_Ea6gsFu5mr<{2Wni@lzS7fx%aIAE~ zN2TInpBNK?Sl+}G;Bm!op*L-(TF#%Wlml!I8ih=U%1TfpZ2e@7!bj23~r!VnJ6i1~_lBG{s#U>${ z9(~t^gk&tg>4YZ#b;ePr#iwgotF4UnJr2X^Y4n)O$2v^=3z4sP6IV|JC2 zZx(t(H7;dqx5Y-o`f6MuPRtG&i_B-lTN@mx+;dJWLRut#%Kd|eS1p}(bmfSW?n#{P zRgjme=^2{#gvbwSt&T&QaT>3mv+W%IbQ$3kg*4|+iY{+hN!XHbZ$(wp?%jPwORJp6 zKgZvzL!O_ezk7D$MP#T?WAycC*yruhWl)m5!APR8KX3CLHv3$X0AHitP7C`3k5?;=@=A{)J4Wp>9q+GJ(U z3H|^BJ@xtVWx~aq_tIX&@`^FNX(t1X+mB)uz^s~K^Tvzkw@;Y3EqZ@U^`Q`UidY>| z9)oJn=JzmIzvz>?E^$yOKak{8%~M24DFloT!kmkO)Cj_s0Ikji+4g6SmIS~lttLP_?XGw5- zv6oBkFlXH5XWp<-{18C6ID&bmpAhj}OY)uTiq3*j4`6Z!rX76Gdv6Q2dTf7l>|%~{ z)2^T6hc7Bb1`yhbJ87NVyxyBTAy1Ab+&5+ncC*RCMKx>Lq(5wUx zLa0OI3$u7>0 z=~jw@!!G17G6-mmuB-cV@5kibSMr1tEDg_D6_#!|4I%|uu8UdV?iYw8?(()o7du?8 zA|T&glGui6IqxIqfpEl{T9$3PxRl!*Ww6SRg5vMDyJ}~I6GZDaqlADGV@tN}@^#9? z#7gdcf;h94PdeZL)&=yvUa~OX4;?MNqTLA{DtNHt0J@&R^8q41D?dG`a-{w;{d^}$ z;Wn@0PoFk&!J+r?Dr@zvzzfILK>==?Iceqk`ODjuRt2IX5pEZo^+M3C2qGdh5WJkgF z!XG$Z{u&^oMAq_9k$iB9k6THx>&!f&>o%d0?eP@Rz$tiYHkl~gc0}(AKra@@Y_Af$ zJt?+5J9nv}VzxF#fzITA;K~%h!NZty>CPK~Y-Sg_P;e{@Y58H2*ZRfoto*8v6Y<8|Y_oy&tk%!Vwt5Nen*w1NT}%y+-*AqT;@i z>>E#qO3X$_;tx_#{R6jRo^SN!mi$4D!~d5}!<{UJ+lBC5q3IvE#W_pZX;`-FI(O-z z@eUDIuJ-pCX;7-9_zG?dtT~~!sw1x!CNIdLoXDW(AGp5utdM9To_^xsCMaQ1ZY!-f zWr-p|Vov4*eoKM-PDG>5rP0>JuoCNi^t^vK)nOMTHz~{DmZb6z9JaC}txx*r6{!9f z9-ER#w~k9Khu<8-mJETKHls#%^4DM7V!r?MJiJYiuE*beUI2C*1D?`iuH)S+dwEf? z#DzEzPcUn!)Ov7ta3WIRKZa#B^WToIeGLZzDWUWi-0Hzjr{qZIlg+mR&4P0iBiOb1 z4!o)C;SY0}Ty)Dgex|Duo!h>@0^1Zx5<6>l9m9W%WCIVgdw_1g zYbT`9{q$K3;E$~R`9eVZfi^ad)csnnzpHK-a>Bi{%^a}ilIp}UhvwboHd`#9234>b zTS~IOSLZ!iaX|IJxQ(x=M?BzmP!{|HmpwoW3_E&R=|jnf!6!3P+rJwgF8MS0{=l)f z&Br~wsat@u;rl_DDEg*O5n{R+JQ^O)n6r|YKy=7XN@$NCAv&&3T7Fu|V~I9#F}mF4 z-0p>c_+lmE+Mi@KcIZg-j&#$0Edxd8HeQsiw>Wy}?7^W9IJ}c)0&w(+|2q1-$eR-g zlnd_xVgtO;nam_kZ{Km$ppReZFa3TRR->drLCNtI0u~?tz>Q9{U6SeJwc%X8Vl|Q$ zZM@5MypK6y0}hEju@dU(g9Vb_1~?c#k5phRqske*^w>I02PP`0$$!)88XodY3B4b5 zYf$kA4oBHRYk2IEUy69ObW!LXgvqBGU1x&FW=UP7ilSwu%bvW~vYsTh(=gZT_2F^i z&|S%QjY}1uD4&3hZ^_SweX{PqiG26AK8Sf)eZ9uDEt0q+B0c64D(Euo>CZet)y4T>32w?6|Wci#3p@@Lr=`hmvAwB23_{BgaiJlwmh zm$UqkZv@lCwep%37Z;53oS!>OBM4a}4)tR`=fm%hUf?SJ{=XZQ%{yZM*Ngr#v)-JrLGth$n`t{MI5knG3M<(iOaP=(Z_JI3$azqjIScSY5$>EC(dh_~Yaq$ z+qI5DqhUbebhFv1Mfsjqj1~sB05*r|58P-HEl_)pyarwxBAdj|bL;drK zZNAK085j19(*`7HFJUHOfU3x{a1)Z49Z0SwWX1Ng5lHo^Hc{}V{B`C#kV~>9)G=k{ zCe+YH>8YPlu2EFhc+IOOpm=IVm?83FSQsnn%+p7hBWlO#J4Ra=4Fg7zYH)#pttt`MdS)T$$3Wf?1 zZ;!_gZdR{3-K%xNmIx(pvhKtT*U_?+tjHkh3Kb|-PxLNkF&PGRB~f}vPfFybWPPs5 zXA-fYqxz0tZl>1h5OpXLv;Ieu>!l5HLtWg;5UybwR7B;0thCH<6hj1_1h=c5GGc0} zYeDyH;lg%qEYwT3OXdl)P$vDrR*_SSZH=K9Sfgf)maL6JIs^op%;{1VFKX7?L$|-E zqnRjhB}O4}Nw^k{qZe$pew*zpT-hPFX9Oz+ejFev9_uZdw>hnTuyQVtOpC+^Fdr2p z7V8AC0l9n|Nz5!w&=-kMHIj=>E#1;_p<9G3}OBV2|yKfajtM+n4%#}D9 zHEABXR7oN+WGIZo$_*`Sq4addWn&dURU;e-?^#3?$4DJ@KLMX2dJkbi66 zzjVpH9oP8-mvO971TMJg8}MrldBDxn^WDyTIO=LLx*2GXRlPP5zCN|Rbg$7nRp2AM zdI)xWP_3bU=cldg=aX^&{pZ)L;>ep!Uq!n7lH{Z7p~&3_gBufYCy+drEk{REFWku>8Z0MiOI=-jF#wMAU}IK3bM{N3 z0sL4?r2fD~^ROH&FKKU1yb#%?0}f6jJ^(%VZmTJXoK zFmP|n5hVTT(0ZsN=iIR3Un~q|hasfNSOQj(39lQ)!29Pt4-i$~1MFeUUy2d3L61QkDgL* z>E$mW&$%G_#ua;Fk1gE8zlC;4{r*v{ft~E^Y0K9&m&3z=ewK z$-`bx9>H9tHL2rlPS7)-jpry|AV&Bi^x~MGPMm!US}A)5zDL;cjsdutY*}5y^#TZJ zDPm+U;RYV4uQj70?`w=)T_E#ntD^t;Fzcv9NF~I*f54oY9+(Dln8ElvG$oqOZJS%1 zFwLmSKax;6YdFBh0zl~JMeX)`3tcfhu*)+!0v>4-JVPxoS-N5o;9=3aO9N1GR^)w6 z>>OstnG*kj;z!#dO_`Ru`A-|p4t<~Gl|TJvhF#3N1hJhqku6A1R$M4B4rSD=>$+4| zvMP#e*Z8EO*Rb^lR#*AY>C5tv<=+858RL!2fgrzNU|5+-TdDy$U~uO}{Mx?+Afz@r zCUwmPt7(Aih4yM`5Kw`&5#BHV;-WY(1@IBxz!d|c)G#$Ala_DTng9jy87v<>aG&L< z>eRB<5WT-AyrQR>GqU&tc>RTWsKkQj9yd2ie!)qUu!t>ETLq1CxVqh0Hn%*^OJNbjNetYJzl01VpUX1#1orp;29_61T?gJ6)jM%^DlqIedLCnJ9SYYpb#e%(o|&y-`d{?H~E};(ONEjG&&~v(Dv~sr^NGh z1+W@Ica;^wIQ0jFp_d3pN^jEzxbRSR)NOzbj?c_80mjA*Rt)L>ewfpyCf$01uIRI& zPITdT;#V9GzQV{UeILXZH?Mp$jmwyGjC}-xr~^sq&-YYzsp|6b!6n3Jc(T*XFV@Sh z8nDtx_4k~zF$nm>OaQA=qoHPX3;Cf%DP-;W`7mG4h}LFW1+`jU-L@7hj}K?`oBo{v z88l~nNj~#;yICAQR>I>2TwW8Xw$(y8L^1|=EE%q~;{w0I4XYFCto>7@1*ZX=-U;RB z+wG1tIyLMY85BG#iOJ$_R2iHSwl8E*0cX4v+-P<3>4v$kOlKc?l6VN!#*~@hM0L@ zqkzSLrHvN0(5d}hbNZ?0Ng{2@h5-HbcgdbDzj3~zl00IS1YkLNq{DE4RnIGhoY@i3 zdFLL`TPdAq4uFpDi{Ab-Hr3RD1#i~9&EP2T`QjqduxP{|Z>ZhNhk`SztUEopO178ji#Nrir_FzRXO5a9#+m}o#5K|0v> zG{SMO(Z6(?VENJNrPpk9HIT2qiqt=RkgfI65hjgjSIT`9bk(E$D_>hwpclXq8cZ{*Hi?)9V|8ay;0NW+mH9nfC{QU_(%wk0qmgrY8^)D9~ zJ#^Z-S?_>O%UwUlvj1><(GB1C%Wc(&+#~PYY(}wH_(8T|W-_~4w}K~8Qq}$y8`pH;@n|HwkF?gQR+38T{F!PpU#DeUtJ9A1!J0XdXvH$Kz=Dduhv5fEcIV;Ccl9VM9T8 z2=?3m4qr$7DwOlGGX0ryFDp?K!Bgjmkw2S&_({Bj7@aj{rx&}-Vz(U(9=@=I&5rK> ziC;7CRdbboojsfc_>XA6IxV5f2YwjPag9;pLXoyX)@ z)(VV*`Pbjd{v$0`Q@QhciV$mFX@5HBi@=WlPr^K@69ODD8u8&ZbETAZ+v3{NdnYK90=>p)VwX2L9BT1QzV>!)@!7+$3n&8 zx8y}>uhoVggzZM#WlOoR54*4GBo8_t6bj~qa7PI#NCgcK+`$dvG2O@Gls1PAK*;#se!_gl^GVSFiY zreV~)1Uuw=p7l1rXDw-AO-h%&8RrjoL3lzHT} z<;E||(0vY2$T*}oU1Tst*J`o8fsS=4O%)_j^bQ;!Sk~Z7!NS7;XG(}3y-iyn^Hy}U z4tT>sl=gayvVY)uJcm*r0EBhpGyDah&tMi>U2q~j$`3f>3SFlFIXttYv2b()n~43M zk~gB#7qq!Y-=OEVvw`O=16`?vx$9_+~>)TP~R#Vac)@uzcDP&60V+P-nyD zNbNI!6~-FavhbKhTO|ruMrmllfZ^EmBXZnp_Mrqof5B^;I%~#X^?s4V#`5;d*%s+AV9p{obg$ z*;^%VR^Q!WLV-zjdK(kSpaS+lX!ao zy(mGj-FP8v(Ch-PEr~LsbC(L5SUT~-7zbsZ-bNAbJvsX2>|5ns$ z|G+7(2_2s1cOV~FR$kT=E{aZOvHfx*Se;@**0yfxvKdx~?Y|f60br35Oq)p-s$3-i z_{Fq*2Jp*sN7(I5emXe{sjK;VvR(`n`+C-6`n-#ePAF2{<^I9a*vTIp*rtPu{|h>o zDHA)+)VPz{0z;IZ^7$AaiQ7pN4eTMz{8V*u;ER@HwI4Vs695eH2qx839^-BWG5-UC ztk@_cZr(G70gD(?WaI(aFo5*Fy^i|_LO!PkfLKx`rDXAzLnm(;HI_XBY%)CJsYJ!- zo0x5=0S_!g5!nC7Y?UF&bWVLbB6)tewxbS!C&_NIY#Vy1AWBLAuNnMp-S)Ei3qt+^ zn{7&Dc~x;2^nu9)7_4hc02(~)w!k<5g#d*1E(WlD0@y$~F??PC>do_S>6+HS5YsVi zZ+FEA;9YrfT!4xoXUz`+VDTeDf3)Y3CQy4P3wZvS0+1O1P=d+8)BEC=Z15L-$o&&Z z3}g}0Fk(5I0a?UB(X#6+&tSkd1Y)8j>s0r>A$kyYqtPAh&UMJZJ8G``KH%7#^!F-f z^;!9TZFzZN6`=$H4+z*zK8M#669-{bkMqx`RqXx6xBtM~a+}w&{v)e=0L)c+=hU*w zLkW95)A@g&q8t#bOcI?Z`zA8rwczC6+~QaS?hgPkRYtt-5y#c%iKgOTZm5z0kYb+n z6z}Y>Et7e{Gk5sUpB)`TDHw5+7VR@jq#j;3mr}B*LG{S9C-@UsaT~#XH z#(Is`q|P;9@E|WAamo`wEr^SY_c#cfVIdmF#edNwutJIf7!Gzw2ze4L9eQD4Hu-Uo zliJG4>j2QbU=0${`#5N(S()7m<4vW-kvju|>1#^t2DvWnT5g1i>hR-?Y@vKz>2% z081k4QP%}HA~C=o<0R8kE*I_PA$t7(G_U#3+CDiDxy)W9q=i815fS*rvK7d&m$X8`J!tN zl}^6DCfsCXhWgfeF zs&0Da=0KnWRivd<tfIYI=VOU_8dteK z1G&}t>4=UZDvE@?y@v+0vC(a=q@6BLI*n#uIh6m5x6Cc3pf)n&QeKBOOQiecW!|uH z{KgHqeYD8D(*J+|%e8+_ml09yuc)r2w*}GnM13Od{^k9TpZ~J1Ns_K04G8S}4^*n( zI6Po!;@t<6{IXE3*V7vrz#PF~NgDpGj;~fbD-^$R zM_`y1pVmD+OEjGMGipFb?5hg&6C^myEc4|)aua{NsdxqsCRBG&DYgM=UsO=kj$o_i zP~Rx{@)gBYv4OuQr)k?|*-u*ItUPd2BG?ZUQ>;%wl!|L^=bDUBRO(`N`oI#_yZwWZ zbnif2_PI0MSsLlslLc*|-N<=k9V49n+DoZ#!@O#OILxNn-6_o}NM~s>7}qN`&y)f| zbzZ7#;MxnJY+EzOl=rs#LpyJI{r9osfD*r^H+f7)y}$?;|3czQn5!O8gAKlVDt?nR zq3TF^vYaWF&h5HG95{754&9wU9m>`#_jzsb5DKYz5FWYwan!1QOWvn#=8|HVJ9_!* zwf}jjjQdJyM{I!XZHcED;GT2%_KWE65TlQ{OwC>~o_Mho;DNc zO(ngqB=;DxMt8QCqerbuw-j%+&Ad~jaz}4pdHbIS%et==3&-|2a0-u*NbmT@D|X$8 zvwgk6n-P{-iIG0Es=Ifei5d65m92vIitx^t_HvWlqdy+4_NZ0%mZA^qQu!2!=gho| zaQy@K6EW_nRT>OOyG2b}d+uj>jko*)X?(ZU+hE+B)OyPvRXi|G+_?R^+ydox)S+ehE!M1pBWb zO8$ZS1NVtw{TgLIrx-)7;F3FSEte?m*Ry-W?WvJ9dMXVmyh~1&wYC+4!~Hd||2Eo| za{{Z@!>!Vj!GXHWYv6)J50!mBu1q7!TpPKqlSa8xT=^LV^)VHDWeTvHw>GAYe>Cwi z1pR?qR&*>!O48iJ<5!B^Y-!fJ^iDl&ydPSQlYMaYso{Qci4dRt=Ec}=P4|^7PlP!C zBiZ32piv-=Ioeh4%*QCW5qK*(0(`L&)^)y`GZDl7x7G!7FNWXDsR#KEM{W@u0$mMY zyKUl4pT>y=anMoIT)OEP0h8-sVW-DVy??_yF<7*66w{aPIYRft$v9M%6hn*;=QAQ8S(I z5Ki1a_5GYrg}?DN<=(@%OMcG1xH$bjhLV`D69{ z+66;bD|F7c(;%jbm$3}8id@xOroQblRG+l%3{SU>cubcT5`9|J z>lV6cnZlz$3(CbG8;>b;Kvy{wNSycy^ST)cm_KpGJzU09@&1p+eYL;EeXsw&xc`4$ z+%M%k`cN+LZZJd#Y$BbNP@-`fn^5`5n9)GI5ES7WElp@5I6xksRZ67Ih4V^I)1m-H zXe#w1xoDi3G8H1Vk}X-&x*7zLCS;5$@Ks1>iVnG>g*53&$00BLQWbpWQ#)=1!M(yv z^qLUb)Dcz+4O~0?w(SI@x@dxz2IDVL_Yk zy;w26)(dn522^71k81@0#M$oKdKzsnt?u$}K(3Q>zx$%@mTr3|HB``V3i<=SbLB=E z)NIp3w^5=@K)ms7VzDjIxNpv;brF`}w?#BOYxM93u07jD?>>{k3s|}7!>gd6#LwPY zGjIH_+;YEvF2j&&RT8ZCqq(OcxGXx!>1A69Zlp0z2KD%Yl!PV%Sm`fkHZ`7uHF zf+4eqKbH0?q=H=A93VMwzK@w)^|` z&Q7TJpk<2xt7L<=<%vvw7 zIa6u4c!%~=XRH)511_b`thJ|zUrEl)bZ$`&k9`4O4=qccNeye=2dRiUCqd27JCDYd z{=ik3Gd=W_@Fk-K9J!>IDxWc4{GKU`k!)2JtaqThr@=qk(CeAJq|GW);p7_Tdr4&6 zpX50z5nQjDGMa48ggwbHX;#dLH;jk?aV1sNDH!}(CY4$ul~tx*=EA8~Yq|4M_H8U# zR7Nz)vwY#MNhI9+F{Ah#^d6V#*5})lsYE|ELIt=e-}Li`(s(lUI5XA}JxMsREY}tk zQMDw(;N!mD_TUFI51O?jD`5GQg|l3L6Ln69nj$tZyn{fyiPV`cU1oa_sXZ7;y$Vm_^jnUTr{=)%DJUqDe6eBR-t0m z&$BG!&UjB+Ji3vUWLAL8``+k@Sp|}Gj_bLc0k)x^g$=mGuyPa`C#{Q6<5g!>xMfk{ zM#_v8Zj?Z1sn@De!PX_Qh{ji=!|UzeH_d9Izc}?ex0HvQn*YFcLFgL%FMkyPZG2`s zyLVYfF0pD9JMTPtA5cJX4;tf@$q^I}G%qfjuKUIBNhn(8PAd^1tarJhn!}>^A@ZWm zwa|BLK5by%G5-s@0-J}anau3dg8OkEImg@d2Rg5~ZLZ{k3To5bX%&b4%C-K86N*~k z{)5gE+3p(tz@1+wyWKv(s3AXV;n)ci3bvKFR)g)>Bq`qE0qHtk!l8Xge#zUODUO1Q znJ$`ij)63C`B4NtRCVj!v2wx`H3!>qI^%Cj@ZG^ssxXy&zm!d&nxGF6+;kXpzsRn7yS`6P@ zS(h827AC~#lRPbnaWYBKJR<}A*o+B~T=Lz%A%vIzWw=zoVQj5b{hL2yrV*)|eI0RL zs0+!w`Ka+oUO$#dtp9CGuj%k;f^PI^SO34*hwC9c(!=eK^l&B2KfM7~sL=TrefE!& zx*?>j!;i7xe-dPI6i1zz{a%2$A95PQb$9vpexSHTtGsI;A**W@JaIP{$ikG2z9+6*HiXy!- z?-eDkl$qAf5(y^m+Rl5R1V261k0lZs^I(~FKvmTc1fv&NqX0SrU|oPNgM-z-L{3-K znzbZ=&S7PC92KD63)H3M0B!`PEO}MUtwHw=!M_#0 z5iB~0or6W=M7~PgHv$%1Hyn)JxBCwjnyuN{SL447*2+JAN!~&zlU>OlWb3@o)eg_he@rIXgy#&TOtoS}x z#(y0Puus@ItZa?$&YPct241WFSesfHNNS{^)E4hm?7wvfI301!gq_7h!36zfpz#BC z%_T8B|HZK+z|K9;kH<~<+$M$q|JQ$AIMA*i{&MjK`FoQ#K)%t-1APn-3KUau%oq&yDs^@xi*0V+N`qlt|D~6uly3mMEZ2hz)YGGZJ`35Gk=TO3`JOux z{6FlyRa8{%|2K>+QWjkz-Q6W!(jC&BQc9yB-Q6hN-65!SOQ$qLNJuw6*B-p%_mB7B zJ$eqFwQiS#_poPn%IhHk>YJQpnLdjy_MCGXhYKD|cb6oHfl4$Va!z{@;%C8#I^u5kbj7E(y95GIN4<3-0^{q)vr zH0#6Y&!zy8o&L0=F3%Zqh}U5v05;s-3BNqR$?Iju74-H2xasvT)&~-`s0o6^ z?jxWFk}EU=T8Al)@}O(XFBZ}!L?X$m6kS+J>Q!5(;GS$xB}Oh7Bx&20u>v^oVe11p3LKOR3>!F&N>9Q+kbD9Fo*a-vd;_8yaJWKS;q-M68qT>B z=qG{7Yem1M=K|?ZdnUj(h9JBnDS85Acz1jQS``lwC9f(A(;mRDuBaHGSAwYWYarSOT(dIZk_Cumjs9QYGsrBj9rFTO z6e+koB=G}j<;1@1y`?2dea((0oXWG4Uy2aW4OsX$jzmVBkc_JXXGjTPv_LKfKcawy zhwHaU!Q~4Vw3L(p*${s2q4nCvl3)+O=`%*6ua!-;W1mD0o{|8G3WVbffoO7Ipb2}* zFwHI#a`Dy*oUjgkpb$9%JFTb$zrdlW_QwFw$)f(ZMhkHL@K5x8bs&c^H)v~XFW%-+ zh*Gmj$`IY=KBCCw`J=y~4YupL(q%F`5G3EBK`#a9Bx&p==`77^g;zTA z#mGxtH@uBnY9UrUtUs=vcE2^bV9DP(56dY-<{VoNvp}9ZK40M+yiCMcgO(B3 zqJ%NCQ-c-9u~oagZqCsX9%F-Zf@FF1j%cQYs=cOHeACv5zNumJJyHYbVI5_U?^;U$ znLc~5qO0dZit5`Lk4yhjalFXp)BSFXjC1Cqe(1noYq4pz*r^)}YF(`rPldQNOSoV{ za{&cwd)9JnP0Bqc(^g-8wG3XMn}vY-f!_#6#P9mL=yhPb-6l2{%v3u^2$ul}_I58v zPaF2)r+*_L7ufCm&C^wkTomkOE;`R-ne69S+xCj-t+y&4I0Gw(4&_SJR`ooG`r6fl z%Ap-y!igw^h%WBm2&pq!XPy<$0ky45JrR44o#av{sfN<3`B!HRP_qgjzV(rZnDXie z9!kzH9qVS&O)?6!tvd!bU2N~T6Nq;ik9T8homJHdOFI3PzsvjnqrOiO ziJel}RlioI3!03Y%7VU&PR_Qn{@J~f+QJonipEb3Np8oiJX8Q)+Xq6A~>sZ>zB1d{Jh5}%T={fi0S!@G}rI^PHLOIcnxWT>6QCT z25XXsMSjJl*KY*V=ksm{_1N-nKiAo)FuBw zNwny|iKeDqP)6R5uX4~L)nuL7+QJrZvoHQtrfZ?aV{Z15f-yVWt_8AFG%|d`&8B-p z{O^h+`6qae==`{5;%W7gt}eCG&iKoRpsNoIB8|7$$!I;3WGTSqS6)5*M5@N=TE3F( z$6~B7=++kc(W(Zcg!}CX^~5Rb^77Q_EmA^@8mf7*jFX!q!=FF!>t8&+N5+5UVCS=T z)$^d>@`I@zMNmV@C+yyQN^oVpYfN#} zJO$R?F00)ojQh=e7x=4uR^+g&RL^vmhHv$k#BT$x>yt_*<$0B|T@tc#Q)Nu}%$D8C zn-j+v8&65>Q@1T}#;TVmi%@V7ket6UG&0{!_XaI?8+g>H*_Np_pIXc)*Gxa{4fCbj z5KBqn$%(9(#HugDMCT}3BE^4x-nRXpE0_v@z$c3V5y;#q^S9JW<)5#*a{2QC_P}OP z8rAr}S8hc^imCjg>pm^$E9XBfA{M0Zai`iU;x|IfB;Cb|m*?wjdfKxhK3_Yli(=2& zQab4#vF|wa#p9RDs@4D9ByxO@W+#^8-hS+}%h~8gKInjH+Z>9zb5{(UwWpw$iP zP7b}K+nXD+C2T?8sQ5>JLw(&}H3xU%7}2GT&Cb^sa)qhwm}k2m51=hj4OZf}d{*Z> zU-kPHV>*SpFk5i|-t|JW7S{PVbQi!E(zdnNpOAbW)`vkg=`#G^Cl z@o>4738c!WIpj+l?(rs#5^tO|*Dlv1*$r)QMM|9Pxp_fa+JYyN3sn9e)p^q;50 ziv{rMQ7x!sRW`8rda6u?D=$H(Y!q&q58+}L+w)vi(diNA5( zKBWeUsdC@~w=$rupxGo?Z@%}1dg5A~0V(>$p!cCN2MM>}BV*ypwW7+nVssH*4Dt^Y zK}Ljfc-dAuxcB`TL!02&g6coc11q>ZETa;?qt3+aFf?sO`Qrcjf!C{uKKeJp7#scJ z4EL`Xoaq6QlGXQ@`JQ7U%aUO(IX>OfzXAw7SeBhyL<;2+8sRxj5qpjMML2ln7Xl&c?8y!6_Sz zypn;K@=K8-8rrR4hg_)58wr)f_(;kt9f$SM;HzW6dGFRm*fPqkwcGsD{dJ*?2QoE3 zJL(Evg~A(n;>@@QT)rI;`}Fwk&o;#@JUrMym{g#t zp`6C@4LQS%)dL9xXo}~T)NDIAu-G_72gio;K73%r85&HJ3km`P2qQft1R&2KRuIhu zD`oip!&Nlv-EhHo+lX8yIHEv2+a?Cd2q4D*^?eC(6&470P+JNy3%IQ|3C6qJ=%X!M z$L-$;1Xq>h-eAlE;|TxwfSH_q!u5M0k>0gGUL6yI8%#@xKoSHR8VzbU zzyLQ{5)#l|4>uyL07c6I7~Rt$G0lDTyX}`qfA;S`jq&n-buC>Fis=yf9Ku%#sTUGY zCL&=oHaOwHazc7r2WTnaCX8!mN9bjs+2105xOji5O&+{o?dhNpgP{+9i}-=c1YAGY zL#%9(1kTWPZ4@|a@R?gyc^E#6>;zmR7EfioJsE%T8LR;w{qZR9G*lD;FlH`I588c5 z1_8f3RTSdkmu41pZC=m!yGdJQ`RmY?4=Eww7Xl^p%0V*W4q+e6c^va78dy^Xb4*Vv zvl_K@Ot%FifynA;TW#n=riIEMkyYfbL2V}RnGoHVgDx#$Q=ku8hl(~wT%q?3zwE$9 zjSsSu@Xk>6lVjof5%haO>uckqdlW1ov|M4H_+9?;z#H&1G8*!3Ksbi2BmBd-4YBt# zj!yaD!uAGrnt^D48B4Gn9YZt5W>0W(+-5n>SlZ)VFQfj?x| z0SZHyz8159YwypgUcOf9T+i!@YLk-ma)+2Zp}(VbYBHhuROk%=oQ!cijqqzX3Q3QZ81fb&x6_&~gLPB*S9x&}I z*n58$vdHO$gYuPIE4&vJ$pQ&-q>i= zLp=E;x^;JTrF3LWl_|k%&xYRLn9?dHNHH$0yQ^Q{<6{}T$ z-2VVe)JZ=+CvNJ9sH$_eW90I@7&H_zxpK=ds&+B*-bo;z*!O<})ORzAf%bo4Bz-gJMI6sJRM{}GtsX9Bjso!9ZzRp-aD z6B+AQJfSi+7dcFnL!SFTJSj=QCWY%Xc-7|!xomRKTIy-fNsi9a7-PZvU@vU}OQHPy z=I2+1bVtJOVcTZE5q1YUfBr_;vw5DvpJAka)#>@{roA0(*~#8>g*8GOKqRR>r;2HY z#D_R5@mrRwp-Gyr?h=;I)z75PDQUCJT_%6)Pr527_|af@RhPily)DT!>btmv7qY@n zo}lrKA>UrfG(Lw+Z5(B(mlL&&6jg&~h4QeMS}}hZTOs;AH#v^E$grUKZOS^+2tCG# z1Ao(1$f*mB7YsdeyA zV}$rjDT}AZt)IuJHNF42O*JAbb#U2TsPp3R7~M=dFGZ-4wjE#kC;nHZe|ZtmhyP_# zuyjaWJR44Z zg~wHj|4f8B|2(vGaG4&PU2*F(DQLZ}ea(iJoT_m9$F=&u8jH8b1JhsiT2uu0g2UY% zs&Ff89wb3lX_F|aIO_qf4KE&axEvXP2@po%2w@a?aJK$WJ?`~$;0t7SXbr6T94u~? z<{*m1^66jR53m)Ezx@cB&=3nZDPVMo?eNS}UGZ9N{3*`OfG_-Op!IU@^;oDG6@y(`<+ZZOWi7xC~x241Y}({k4s^udjW^6(eVUYCBX%ywX2+m35#a zt?(w!#>Q6lUys$h-L-A*7QoDTJ#MJ{djQg>Yfj;#YJiS2hRa8_4g5Bpiv4DE#eWY# zws7M`68t%BvITwfqb@%T3g3eEX{g;w!ElD$vz%eWm1|K^MaR;>b?99^7^< z2y8a)DE~wG{i}&uz5ZW~*6^|Mx-eLk@E>>fpI6$uKE8kJyY4_H$UZ#D zhrj#Jo?Ql~V%S%%=^jgp+ApqE^xIN*hd4SN`Ud%J^XSkwO3nmU>5r2ufIm*RM{0%t zFeK>mEhS6J|*?!LEBQiDyM(0)TB0W<(_ys`}~&YylZekKf|Z3;mLQniZ`?p;N$;9L(O$~nHPiY)ac+U0d@(foS#O4 z#|GnQ4yNJg2s_2W)7givFViJk&98zIP_O=XrFKa4Scv1M7@j^KfZqv^WPo z`?PJZhWc?$7rVx)rs2`7-WqZpADSUtlfgXV3feIZ2lUT%wpHLRe8qI5`ZZ+sqlB-R zz2LDHO7-*`ag#redi|_VQre)Gh@P325iw!~X0)`3 z;{XpQ92W|?%filurzVdB6E;ZhsH_ZEAzl@4zh&*3_}zuG6-BdTqL6w+lu$xlOU=-V zlvYGOB$K6c%IPC@#%(VgE$qJAUU$Bhm7lNxL*Ngw$dA1p{lUT|M8QiRJmJ! z&Dw+?@CAaq_gJIWj!NPw;%J{14jzetIJm)J`6D;@#~|~B@tgd+(!%D8JVmx;R>va%slVkFWY-t zqekAiy-{5nlTsn9PETc!knbWvz3`PnN0Ss!p&FYCXS^&`I`VUg1>)*lGK|A?q!G*K z% zuv>`333n!{&2M>Y&soCA^xC6ZW{zsLRYN>(<L)_ZTUj~gaCo@KaC0r!R1YmT zmzGX9QnVE0<`?J2Vi4GkefvxIiNp zQ+@c|baY*nwb!A_IF48;&K?gBujQ`GZ6~8*kUA`7DU^J_jJnHNW|=np!j+y8=ennUTP({_G)Xl=2(h0j#uqerYW6*nN6*&{J@U0khN0T)?lP`W}-)~eQ2R7#2eILs8$ODovPP(5NFyM!^n^Yg}J~!cmKn9q&ul(gS7#(F|$cHW+f@ntyvorqUc@ z@;))w_s*;`;SVDh6pswp2Q0)3vQi^WS`?ow#P|+`itcNnmWqBGN1=V)pvB@PfHbPc z#_esa#g>Hgg^}u|CQ_l91A4SVwOX>MU)fm2Vj}aKzT16<-#d{=4G53~u01%{m_q@1 zP{`6yxmm8@;~2OVW4&9J*h2xmTbZd!s%s!ck^1Q=R~DDaa`TNZ`vDI=RC3mdUx^w# z6@Y(Sg#wl{zWLw~awlNm65tKOSkG*`_!l&zbiUt30TYK=fqC}?3IR8*p|YJ)`^58r zO_qUx4{@Kr><5?j-p3oXxV!#&yK%SYnCdhvGMS%Fd1ZqJqg+e7z9})QwvX3Q*fKQs zfwKX_13WQQd#zQ;`F-NdJD(87!X*aJogeR|R=XvbT;dp>^UcA^Om-gcrCjtj_wao5 zoT_SGrtP^%)ordWlU-luucK!q1m)qBVpkcREd|IOU0uxl{Q9YtGiFc!T%9IJgxp>>~nwZs~6R$n*bPPbY{AI zs^aOQTO?3ylJL5gAz3BeH({TTLj6|a`b>wUTOm!v%o3h0)7JsEihEUK5}15_%Wt{$ z8`BJF=vkJOPTc8;G!+9{XgIW#83wB=)qF%qL^(D#ewv^<*s=~`D)C^(VfeE6hDi@O zirh*_OOFF@i>a9otNQ{K_R12l1gqVg!hhAdtXHnZ-H^rQ@YfxGc#_dfMI) zGiM6{TMG?Dvc6j{zVpvgtgxRZ{6>g%2F-p|7DaY3;cf-XzLE)Y-DK#{QI>`u<^AiU z{KTN$Z+XozFj5o}Sd(}8(bPNXfa8j8ok2l|>n z;-#60yayJWr7)Rt?_eY1fdxFo;4-D)%CAugDJ}8(HCoYeNe|Q7naW^hk<4;>FYhy^ zi7-6pEfc0FUBuYe9m7U&cmw6U$~F;S+Ha;9~+L7*SeC%)A_ys)r&?dogwLO6Yr`Xxtf4$zgcN2EMbX$z)V(dO7MC|vml!LhLP`#93RJW?Tts+>Ci9%S;dkqpG zW3X)XMa7}0Pp(kHS|UO+wXCv9}{`RV7{=+xsY{MxkMH|tCqHMoJsgTB(I{({-nS) zFVCC`r$gwzMG7)~I7_(otsqPBp&Q?8L$wc8jqywfzx;BxOGQCet7i1oE0@N%vd;{N z87)<4QekaLTy3A!I`T6mL2%Fpn8iHgu>|(E)XcWF8Mq_y_$KgIa%gFm-G_ub=7P{# z7)oaK!9)sKQm}jI10}Rb05tJ0H2Vksj1y(%Eqyc)tJ42io5rT}7-+?wJ_Yz8K=lEV z|AUMD!u&oI!xEjPhlBzEPId9;Qx5<@Pud4j5Z4|~8GsF2(*neCK0S7~rsgXE`v5e) zyDkN!WWbL1Fxp2K@&!`*PS!ivEH#*$L#Dq#Z_&^Y&G!C2ZGhs+Ln!V&Xj!rjV8-?z zZdMX-nG<;PAUP!~ho*93u1fC#8s_j|Nx?FZTHXEQ1e6r?@0D_C-wrL@kV3s3JSF-X z#M7={^8@0zxCi7wNhNtL);hdNcYpIINaAT4oJjfjfa+o??s@wr{x7)1u)q`!0jJh7Z?h*lC2e?6&V;_48UZ=p840yY6MC`nKf-A+CL0RJY{uo zlyEF?o?;e)`-hHX>uQjG${X!7uQqW= z9vudEXeQY;^OPa@9uotSP=13}-oUOYnhC6F523Rh%6lI=MNr&GN)}`gz7{~neki9A z-<(P8&{A8+|H0!_$fKpBw!OqZEd?OwA6;PM)wb|m>u1j?^afX6do)F60m2*yw0i>z z`q!!~aB5QFr-sNaL5>jphK6Ei6% z=4E!sC-mKfB>aZVI`fGlFyy!(Ir1A;%*;hMey#J+fsz(*?OYkN(#-nF7vJ{$VbVX$5M=LojLboaUp(-LTAm?u3Kdd|` zwL(di6F5BFiM_(Pi4sxhYUaP-z|nVm!kF}4v8nk7=~Ogo7Ct#K8hZw3N$2MYjg!O% zpEE+?^ zw|XZ6kBq~E)2$P*gt#J!44aX#6#tD-e*;L#MPxztBKkQ4Tm%g#<<;YPmED?nTrE%C zX+iM0TRiK}(Kx}zut+}HR~9Sh=N9f5?T4mot#M1zbGFp5pi6MRY8Jst=OkS6?aWKg z>^axH(~^^z<}2dy&4_>C6%}JNWi-05W^NuG*>mQo_P$9s>dsSw<(09fSIyNmgKlqC zLW89}*av2$3lY!fH24odEaXWjaLdV@f^EXUHe)@nh*dX@{>g+fd3GqdH+F7ck+oxU zncQt~#EW%*oobIy)S1<>Bz6ZJ1_J_c7@lgZ9kFoU1;M!M%PaihXbq~{i~GG<`j%;K zu@~O%zxh?>4)4YRl<<<< zATjGY&R^1QOVbjhYT(o+Tf+EKCS9_AqlEYle_U?#%~PHb8ac(be!e}d4Bd(E^&U@r z2Xzmg#%trF6|I@-K0PRPmN4FvYsbMFpyr9}Jx6mV33HxW%ojdrmYvCOJ3!O1RZ0Ix z5mhh+53Mm-_@6^-Fo&0qZ;#jog)m&9WJRIs*T@^*uizP=n8dy>_k&qJmp2)3mh^T_n3>{#DWa#>Y%mZz298D8xxDM?PG1$>&Y&y3hIt-HA@ zaf2%z6iH_mloTdJA4DtdE04+&5rq#Ax1VOmEZ=l4YwzG4yFGz>(}B*Ov<{>FR41QH z5xI*Ag-L-JTe+1Ljq+xg^eVy<;_f6WF)ZgLd)2RHQgsZyK6`zUhekaaHPer0qL>h) zx+s`Al{$(+S0GAeVwFqE2g-;Zw1$0b4AG;0N_x`F2ZddJ>9yM?$Dx<@?_EsMNab@!yCl%W|3w zsxA`un8i2TT;foACu0*SWLR}VR32E?c#rh9e~x$LL2a&Gn?dh5chI_m84V6`S@&%7cxkveJSQvBDGY=U;> z#!r{BoMP$3Y#I1_Mrc65x0Mp>W7#X3iM-a%E7AtPk8;JbeXOV-(~>Yg-pe0=e0-&%9|JX{$lX30pu0Ea6fY zZOuI63CT$?lJgcOBjHxU&Vlu4lK1GXu7)qL!96!RQQz;9cay+k=jW32rd#TlpPF%e ziauYeDu^!o!$un4%;}1eUv1+jTzrVCr(l;!4_}+#m!0!-!Ooco!|!?ZSzWY37#e?u zMq^l%e@IbJ4VBP~(FB?AH2eyOKR3oOT0>=dLnG}M6Qx0(ZEdaRdACH@YFUTnNp_UY zU^T-PQ`Nu|()f3{rz^M&gj)e~2Ogt>WKuu38eXmttfuR~)w}Wo`bb*m8%H(0rk_9e z(Rd$m6{FoM((dR=!nd&7@6H{pV2&8HJ&UmnndCZR-`7D`xSQlz;-*(|mYJEHe2%NW z6S$7EbJfHcrgLs&p+WZ2Vt%@R9lPXeB4zT@W-3p(E5-@)7Q6U>=TR%H!Bfr(iB-1e zu5jd792L82otHrI)aMcm)v`S;ygPMnD~687p&2$K&E#2@t;p!k=W`Tid4%7@F&LN&5MVi80ZFX}#N=0I)Jg~CU4)d%hXv;{R5RP}xddhmqQhoovS z;|UgTNE|GFoRQusjyhs%AC5^uqw6#^zY$V?Sw$nD$%Ic77DHM}}wH z$R;QpL9f+#qILD37CD5+liQaNM&D`meQ))4q@shkw&2$ZZ`g`!%*#G{zKj%!SMefB zwrE6KT4Pw)2GmUsmtx6`%kW?^F9s5iQn4&Oelka8A|)Iw zdcSeJS_4MfLwC!bqd4}>qh&W4m(6#5r$()}nwa)HDfpDJdAl=bvBLavPr9X})y*?0 z)6{bYNq;;VE+hGPzb61sNFKPm26@u_^&U!brp?#Z^p!DX_hp$iG8cO4!|1PHiF$#Ys1j&r0fZ-Kqq0%BLjS)MUcW z?PJzSq^p3kd#{6|U%w@`Fex71IwNo8H_>SDP-gf=a++cSobNHqE`0u*N*`micy17w zXbgI;I1_wK=f3+LkEq+3qplu{Nb$mD+IW zlKF5C8(S*Qo6MdzQy3=8+!{(SUrtGA$5~`sUjG|`h_jQSp5`|~w=2Qee*CI_QOf&o zt@i}SO1oKrbAfZJI{b}uw6%yLgV`>%I#nxu>qFsNj1Rmu0>~yOYw2`drRnOZCe1Bp z#NW*#W7nD87sYD+*y?QYYQ8==c#F=(Iz=HmAv{;SS0pAmGTHmFwz-P&tzeGtWY0t_ zo(qH^ZrKx}Y*{4E3m_EFcQ#YtNk>t3ah{>P9A|KwkvogU4`N!YoUBFlA7;c6T=^EK zjDia56EKhn#6@+v-AfivJwB7Y=jywc7L|b9FfaakQKo0u$Q)g*G>r{eqaXM6!{@E3 zAHFYaHQ@!`BEviO4_`J~T`>?2Hq6v(X&qlFEJ%6%Qiq_+$w)@MSKW?$)R-qhk)?{U zK9rn8NmY;O%4M{@I8t~*uAe86Em7_R}hv%wOMO8Mqx(0#+?|v}*=Z6)C zv&LDJS6ly2@;W+;-T@cU!a3^yKOWCldzv6(TPJm`xppKj0#0GO5=LGmivv%y9MQL@ zquMg^p=_|Y&aQfv7pyaEX-Fvr+*YYSKe!ZW{2l^ISF++Yt+!5V-%V@8<`%(XGhyTRA$F&A1-#7+)u{4 zF{Gh!^l(d)n1RKKNSTwnZ(mp7O&F^h~t?40BM0 z(_M&#*MU-!>X7#BL*>CU+|~Q=Bq&?}XCay@{%wf+lk%7%qt`h>#5|p3?<#9uS-x>hd<*QtQr)hy5qY&J53&&m2BooS@ zP@|h}lA!$DQ;ey%96U{R%1EptSE_T`CH>N0+f;0)Z2F3!4EvqVjds6X8Dg7Cm`gNUtYj?GIIETI0a@X(^h4N)8!aCqtI~{eVh`i6l*Ybampyt|TG3RS{%m z)He}u-(HcwD^9atHuY8O=-htGP1-lK{^vNxr}k~`{En<|K>h+U;%sH(LmiAyyyz%= zUY~1vnuomk-QOpjJ7MgMM>*ShkQU~8n(-e1a;PxwKYx~P^YB^J*+&29+S#PBr+zT7 zymaau%@4!l^l$;jM4OSf6eWgsgq;`2`9_J7X=>3=)HKPz6Y1wyQiPCcTW6ti`1!6! zRB)D9xHni^R;e=6^xl|60=`$WhD^bRy`S0s>e~|95YKa{rXI+#L13f zIfeKgB}-%e%FPs13&PPAJz1!_oE&35-*N;KOa9eO(xg2JetPthgdhy;An|vw&Yn~3 zIN5r#)`zv-qsB3QX6Rg(b}o0ALPWz8^G2zj$k4<^hDUmx2IR#T-{^+{x#|v4k^K*A z3!K&P;fXcVywYw_#VWL#Huap8Q&p=p1|$7#Z|6z?qBJF0fX z$}$@7OT^$C7)VG-qNLOx9>_zFl7mU(OXp;0Tb3;u@R4L6+uS5pY|mnas&@a@_l$a# zn&4kqI7Xo`KJ5bkyr(Cd9`?Nsly#kqI6qlZ%}hB%E!#|wqs3@uJ{cJo$z|xCWl7yx zhCMZn1dVcrPLb$|2Nn2zmPf?7Qo>K}^;_z*g;C?6b7>E zBUvShN{+OTbSRQ+<`%kL92yukPGd&P+(j7>`s`u?7f*7RFveh6 zt8WYhq=|Nrcx%j#@DeBfCnaxDv9m=!#g{7le~k~-LIFJx7^bzhvuurce6=)?VhE_Icb*yQL(Ifg4M3*FKJKCa%d zDJ@B3g#pBj-0&&tP7+^LOI=UaP{YvU5PNakqqLQkqBjZMIeAfxcAg)Uj9(I>hq25% z(C*cKWD6LX`5dfwCLcYK`Iz!S3m3h6 zpHa6=|3DU2>aus2^I+)4Re72c+8VnV$@sZ;*xglc$r8$As?Zhia)L)% zB;rlA_H+X|wI(v!iS!x_3R(gzOI+`9rH51~G)Sep=_Iz2b-c$>;W0Dwmw)zBsx9Cq z&;3FNj#2|bXFrUmq8#^_Ks9GCkQG2n&JS6w;?Kez9$Ko7Jrh9iy8rBEK$%K7i7^Tag-6IMkK5e|@ z3nHatzttnP;54+`7~F~#eo=V8Ot%%bzaZ?2q~)OG6aTersu5LUIWsg-Oo=hrDc7(B zfuXJ?X*v6YmsH5M7Oe$B-CN3;sFhh|GlqPv7R?a(4(}1Akzww;xoJ6(VxwYD6%hO0 z;QLqgTIieUhD1D+3QAtk<(F0G?`A{kj>R{o{u1-i#`F<6Std6Bmb*$qe^MlfheztO zU}8BPT<)3TNsE0r=h-y-3#Dn&DQ@EGLY9d?eN**SSE%!DJlVUdARUUr~K zHN*Uu)k+TEdz{-Sx7Y6UvyDlfYJPl;!&8*P%%|0|oR{c#r@i3Ul)44s(-;K-DelFP zLo_cOCmrg<@7ing0Uu_n*+P&sJi27}F{9`QMNE#fAB^Bkua+xbv@Q1>$g5wjdsW@7 z9eaCeJ9cRuwWFpw*sO>-^cKM8%^+IJ*Fxoh$-M_R$_MR+ZXm59#jJ&5rXDh@RBn%( zzd`Ml4Xh2q1jVo~-}oG>*Z_Z^fSgz0^pnv71QU0(64AY1&$uSR^3WIE zL$FQUN4<_5G1mGy*|EqnCCG_Bg{{~+ij1LDTF(PN@sMMPjN6Tu!_o_c3vP!;m; znj<@k70F&W)}VZwfm3XhRyOeX$@iEe%nu_=96r1#6i55qtC0_-hojwuW1l2t(@&H>DRA z&TCLb*OA%u;zJdH0C{$uPL>n{n0cE4{q3FVcJaWJ_(% zVlVDtK_6be39{JZm}0;y$ZG2|`|kU&q7wQIb;%QJ$Z~_g%6NDFs9*y7pyvknVsgU~ zc~R?c#qck>cGs-wlaH2LG>`uW%gv5`h8B1kQhYw9tQX;_eae2S9M_CASTex$ho00c zn^29K0a+kYn|8oi6%EWaAgAKY@$e$|Z}u=7ghmV>k{9;5Ffq!S=oJUwICK8bkH_xw_D#?+I&w& zX=|^9S|~s`icbn~0KU5R|G6msW&X{-kz-nbC`AyR&jN`QjA4{IMt;bCu;2O(?DrKGl|D%dRqH|{D zFAshNbHS(amT#V}yn^mvARY)X)NF9_JtR^QGjx3 za7Je6XeU5VO{2M>H=KrkQOhm(njKg-vw&p|l-!_0JNMu0jh}#y0CYT22u_sH41D{I zK;|i|@3AMKv7w3nZ~Ic&o*Qjf9IbZ-O4pok0VdIa&P#1^zE0!juPi~m&J66Ir00~i zbv*+V$7dl6gX-7+W@%cPb-#3j$(A#D8a)=}Jg|6lcHR*G|4C9?{fneVr-0fkUR{T< zDg>lWU#NJ*X>ujZDDU&`?7M-kg}(|H{$+6JfR{6rq(j zr_F2i`;eCyZg9BHf3Ji)g`p-0VEY(zxp>eC54Do`hep+eOZIaiVqKFK?l!)DiVT^b z&92gaAwI`iBnlk~=1USz=K}vX!m`LKNs-IXNgd0Pz3}ZFx%Ca3*V^OD^djEup}RhB zh;fbwOGgkX?dFG<)5pvoYONL4t=>-+jKjHaFK`pjw8$3k7yhC*WP5QP4>##?%eq8s z;a5_@e7!7)wYox0#iZg_RmEzL;P9T55BrroQvD>&OQ$v3+h$t(v1ZFT%Ps5@PlB{I zG_Vjchw)`-1*DlBG=&yoHo0k*Ttkq0u#F4)mXd1??pHh_>`!X9=Wvlt6`Ai;BoDcn z{C!T~gQTQ@>jdvWjDr~GfPM1u+u+LRDlP?WQJE*YlHFe{u^19*aN2L)AeTyAdMoT& zycT8;(ux;lQ_IK~Ce^RHS?TwS^<4NMcLa4iUw*GleF3{(sjNIngtTIqzCA72kB z5}86O+74M09op(>%zxz0JU**@GWDwVI7W~@+vU7_Ka22;ALjA=E9hDgYNTW1i?iJc zT+%<^F1gbsHnh#Vt7WOi+z?0$=u%6Us^KbZd9wgKP9t+LC41y)zB7Xoi;WA`;DM(q&qUp zyQcK~e}1hdVAoHYy?(KDQ|=^CqtbL<@Kq<^)3(j)>tCuF^sw>j5h!YOov)pkjl;iT z8v&VK=w#TlI$mfQLTLa@%2fwP6wR| zt{56{E0*o9idJ2jc>n$b1a=sb^<0G8Z-g$Hm$pZ@3+}xf%A#T1QVv#hAHDhJ=qgtMG<8D3!3!IFgB!@}?`HlvHnJ(~~?$@`rdZ-0FwV_7esA zntGghg7V6vH;mqI)TZ`sq+ccLJHA3-2_D za%4mwC6uF$R#Ws*Fz4c@(-*6l!Bi>z1QK8OggZF)1Bg333fPB4Vr`Af*_o<|;%vE` z<8gRG)Y{Ur4qFKD@VeZs4a1Vh=B88lCSnRGJJDj|6Jz}~^rhOvZmfKW#r*2OW=%tf zVrynEVMc5Ad>jF$WdH}Scc_XnGPrUS=;HjPa#j&99$}dT36*K(Qy@C=IkajH8n5^Z);&VO^f5$;|ATY|F zm^p3H+%|Y$(p44KPkaBJ_zC%$ScrgwB@%XvZ7r|8@2Hp}uH_^S-K3tg8BbT5lVwEh zdJX3%4y4k$dX+&Db!m0N=rgVJg?BuSaMlKWkq5luloil_RF#ANFb~oDURb^ad4Rc zcA^>fQ~A;+36j?aSYy>I{r3I{H!5THOa5sz85s!SrgB@C;yQ-Fwh3!Hw(3G^L|%N3Uu&`^RFhWKpV{d1rzrE@dZKEhKrT0U zIZKIKu&?3FM@R9bnnCmMnv($+2O1y4ems>{#|J#t*0!ju8ijh1C|~W>Ig#r-X6xEQ zv6IJ6Bt~_qKX4($EqevG~o{ zD4C>*C)#(;4qWILf|-4&oxT-42os8O>ups_*Yw(?Va8G z4?i){fO6+*cCXUR*as~Ro4n7L(92)XoF>Gvuqiq@d+kC%e2%J7~)*^f7pA=s4DmMZ5suYR=T^py9A`W8>BlY z5>iTccXzjdfP{1-B`wV)Rl3pVo|CorT6@3m|9zjY<~_H54bz9E>8y0?3G2G7NR^J{hN@au^74{rt(5M< z>GcQ;BRYigas)8 zzN&!_6CjD3XgdOv5{ypSV&5hGOFUE68{#2qy=qmP(E2I4X~Ou8+(GRq=4(_yXY+Y6j8OK@cO5LmL#v2kgUkG(&_ss(<+y z_a70AP)tYLXQKP&)LY``5OT`je`Kv&3EbQEC-Z$DdS^4wJ|Gf4_uzTOZI}K#v(xMQ zj->3sPnUfcQKX1-v!wDS${v&h-<(g`6mkhqz84 zhSqCw zSv6$aX~E2oj5fu!%FIPm)dORF>Jl{ z2*DVpuBn=FVc<5)Si|B0jtc>UdEPa#2tooq#>^KJ`)3%S@sJ^qg7uLVH*k4)*>nVw zHvoPKw$|W)3}&45#K?ew;R09>eY)T2xkY-*V;>ZVf!+R~DwssquJM<^k&70=Of_8e}j{G|%1h17yGggnj{t zaD-8#xIvrDeKxIqAB3cbC%OwJ2#R3M%we;5U?;4*xZTn8_}R+0d$}|C^tsZ#@j@4c zTJ@=i-!k5W_=xDbg=%hqWaxKhz9u|Z)8enfc6f7tdpfp#c^__peIbXHZ>j9kZqt@S z$d-vdY5fsc3Dk5RvySQF9V-~Xvsbsot-ft@m?F zr;7>iX!IIfjK6Sqnq#U2&{%)td@&+fpuN3xHC-|JirMSTbHa_jOu?I&TR9CF@h_uU z?AY$c;b7FPi>NQkb}wWoPGheKCfds@l*F&CmJ zC;fpP?Rc{>+t!|O;l5jfi@AweQJMd}ZG@3sBXB z1RnauJwNHB*>8W|o{&b)g1)Jn313iw+K~s6&8qo6k>{enThE%Hxet{U(-*HFE&(?rpe$G-SPA5;p%0%+ERv7clYd(D zW({pp+1t+yV*YY?RsZvO&9Zj*gVMDH3-c(ATuGH3RR~hM1ctp6%vptV79o1y-l(1< zazSNrfXgHW3(1ac6N32GYe34>`H9?D3bDV*9zODTl{3HJji830N@k8~)OZZtn3g!6 zLav{>7A-X^fI?1D!kdDFsolD6Q^K1BoSQ1HZg9OiUzZ}n%l*TdhZ!UTGI$|F_EL?( zCaP_%Hl633|`!P@f%=5IeL4CBb-3z)RIwDNuVk?BUZ zfjFbs%T>>2@|-*7<(SIp7ch5sGki5KZ2f#*zE=WW5k>z={k9t}7hiN+_&(zXm|kv# zNEy8#dgXfWuwviL_t#eo;n(rp^L1VPRnIPQJC1O7X_K|^0)zO)ceG+z5$mWoO||FW z^n?&Bs4PYlnV;~5B34sJ1*6O%llNG0*Xd0mR)ye?Ipu2C^Lz5w>@Y@27)KTd0G^ee}U6^0s5faN*x7O+#yS-H7 zBb}HPYOE$-VcXzmkdgnBs!cXGNDZHwYM(~>JNQauS+{BEAHz5tdVr4{2~rk^;(A_w zsy_1fqfIBBa;JS*()70Z?}Xv}nepxOVE>kxKQNZKgE2b6tr|Np{=c-?uT~=UjD}U4 zk%z|_R(->7Eok)hAfZ%|1O49DC<#U~yX`DJdmA!h$iy3B>{P z_NvBq8o<0%I2bmhlHZ6cLb(K7`WbnGWGOQcS;%1EhiSgRJWEi@6L>fOtrgkSE|)T6 zfW7#qcbJ{tK!Cw6UmU^Qd--r47!1XLANC>J0iROF@}`{~=1AUUR$OAcu{Ty9+`T9@}*gpG#*E`czih9q6^S#pDoS3`X-W0XF%-L zmbA4#@`}2~oL7>)5cEbo;&Shd#`CfxX;o9_IpL;kAW5ash~`sEDxQv?A1IlYf)HFa zhS^`XPHvY^M+MR{^vDO{DS}2f`G0w=x-I|@@OxpjpIF`OujDT+1nxqlj8AC=^k15W znNn3f=e=8e(i0ipav44u$vqL?(LZO$ zJXqvjJIv!(|4rLo=@&xbt54H5!Ff)KeMe|`RaM!i`_fp*d}=1+^cdKu?I5^6BS$b& zH8zv^3rwLV1F#w#x6gs0_L1-&F!FBzP|f*&>ALg)-~(iUj1**mzBpG!{suCER*3^9 z^=LReNB?Gm7kzsiLMK6o(B!fR;(C!#^Ed>p4v@^mxr-_ZRQ{!ljD~za>tC091d!|5 zfB&Fjc-+Y74S*>D8T(_XKpce->UY8;10TYR)_fM|5Gr#*BH}v)5GMfc*&|)Mx3bD* zps~>!xZ)j_gLJprMglV-(`|qo1_0rKk{e(z=ch)ZWq++NzeRERkSH1Hy@5LzgU#dIE# zWjkEZq9UZH$68uwtUdGy+k<2wn89HSYEUnBLK2n%lguGuiq50qvSh=h(Td0=XP5za zlcs~At;GYuIS5i53giHAX=<{M1U^U`Ev5({wGFDhAhJ5R9(h?`$ZJI?2>(S@Raeoh ziKO~Fv4J(PFgEb;_>KpiGr_vJR9jF2Q1_RR9$;VSzoV6{Zf@!qT+ zILSsV2#7>3K|(S5PJP+{ikSQ#0*f(!10cZYzNi_Fll~p$WqU`M_<;9K*C15bm)(o( zjvQDEg`580L#}Q8ja;K7y5$Xf@clmaXyML$c=GvL(f|tCaXNqYYXdt5!Vho$2R|Gs z3DSG2>5{#fx_eU&p-C}{%2#JA{A9Svxw`2@DuO_GiWFJCuUA7rJ{7O0f2<;#J&u;9 z?5`!d)&K67X{|wKOHe!$PA}BL%oL`i`PWtl5k1GMGqbDf9fw1lJDqC3LpO3O7?Imf zvCqdwx(xcdU`^#SPnXZD$8+_1{LwZdhtd*{j{r!r1wrb~JX^=(=yw-I_S#*`zad)w z{xHob7CIq1b_k;kB9%}B`U$=lx*IiYiw=jEW z;0E6Uy@eLBMA=mZdSmt~lYx%|tw$s-Iic+L&HllGX%EkOhgpHe`?W)+DuT?x4y2&} z;jX3*oYcMNp!emb_#EUNkPnF(7*Du}$A3P4Q}ZqB__3}v_orANTxiO`!od<#>H$9t zO&_A5{4z%_f1BjaK5pZ4RyR z;g;RtsW|&ljV6nv$1oh1CE;$gOAt_<>lGyvM`r}GqKrzMEnc=6mQY5%50Mt3u3Fh} z`by;~Pm8Cf*ZP7sE#etjpdg9lg>-kVI%9zr69Pyv*)CVnPM^uNfytGS0L;pkVriDT z`<$F+Q6gD%t|IrRqW1Gc^$)ynn_|zP*THX-ef&DAT|tb7G7iO1j~WBk3kBKq*g*oYga* zz+W@5th0-x(kf`KkJ%l|9C)(rm662f1fo!{$06-wTW{<6nh~G$erSrEEzV#Fq!t70 zG;zIb=hnEfP`!WPy?8B&hWmOCowUd(Y&<3_36;5$*-~mhIx}F%~_rBpds)mHM5EhyT-HcM`Q!?gIx01 z_l0ix@gNDCWole{{NQ8?n$Bn6B$QRi6O< z!?)c|k5-CCA1l>1U%E~jzPu|K2rJGTRXigw#E`N|C(fD|!Hb4XCw{;+>`{B`=i}vP zp8aa~G|%^p_&X+wbr;{>>G5C^?v+CXtR4?$;)E)ekh;l(l-?GX0P9zD8cP z-KAG{@Q#{QU>Swb_m{Ifdc#_;nIo><&S1v+ZlcOhZL*5RBf~iyP3P%c@Mv-+%q4i$ z6FDbtnLg7=01IXffTHfmZ#ZT|h%uOWO|#OZxv^FXPhq3KfrXx>dD$B?$vm0h z*O2w-OvLEv_OAEg53WVYp;6dv;H2{Fj6ifj_kQmh9=^_(g-7Ea-h#%pwGB`%WG`Wf z?Bt~d_*dZEPTNz`>3311gvZgaraKwlR@!FsxF-{zew-wJN;k2*&_Ky*f`KLG-eiha;vFAp4{zuUH#(TD z=u7LP$exwj~@)D>P1IBx9deSnj(ctv#KSejWU5eq6`7H|4^X6 zA2q4X!}4F#QXS4W;n1 zlIclut3n)Z^|I;IJlg#4y2{V2CBG7+S_5lMkC zo=a*H8JHJ@8H=Oh>Dea4K4Eam%5RtxifnIxnsB!YWiQ9%Y6(s_- z>-63B*M=_lAKxFSExyOIuRHu!<&^$S>M{Cd=EquFY+@)locd0XV_t;ZW-SNtCF8}d5P-RQKhO;}5b3zuN3Aiu`+%%H z(cZaRwHz>|b`YWQ_UN)>#4ILH1w+$-~E~&ZyAS3vp4C3|BS~#5e5RA(zv@ zu?B#v7!X<^0_Pg2+9$L_)dE2r5*29tn~|~<3!$9{0NYuV`Sd#CkpRjAfUbC#1J~}x zg`Z=wfIT<0M|}Fb8c1w0`>(aOzi8An!15DtG=fO0DAjOvwgTTZ*8lz_ep}*z#J9Xt z)irr-tTCSN8?sHgFBa$brT3xzFvIkS7Nd%nz*2a8E1F=>J+b|&iU|SX2 zvA2S$re~ORqdJlFC>U^tOvu?Dep}F`yytE(dx(SfMpT3JJjE87{O6G|V;`OlboOnE zdX06d4T8S#ZimQlgY-J+Wbpk63Wx08P!MvOno0tBE%Ks6lMxOR{2b&zUCPiqKQw743T7ygZ(w@-UkpS1z_?4~z)qv=<# zT~H60maoD6g+FGt>#J`QmZ2ZD;>TOiT<^ zN*l?4J(<9ITg_5fZEg1hrPHuJeSaM!sv%!Jpx}JZRMk6jUnw+?-v1?;>ABX+v?;V& za!&o;iwygfVK_a^SB^H42q*LULKDIbF~w^LsH}W0P|OzVc)S?7Exo_YTbtVg+V0nGXsn6V(0$ z2DxyA#7l@aI8?0hr%`rHrhLt~Y!yBA2${wo%&?p;6F|t>%7$W^_@9lssounSjoa4` z3cVKL+qQ1c98El7wvf03_Gyb0s5;F5>kHNOVm_Sc#9Y|GGV%+Gg&U?@t5cWz_7T!E zG@>qHMz`MsHOzr(!|q(&^_0(*HhkVJeI&=V3YF`d9kv{OR+r#aQ24r1xId3efDH~5 z{k=H&U_u(-EHHkhVY;N^Ok5vvb5E4QGlNMxWpr39CoK?Y5JeV8W3^?`dniadw-cE6 z%V$Unt);GGN9Xt36vu`1o$gV)OcE?gbMc)&>q^@Nc*wP7M4j8WSwDsy&3g7I+^(~+ zoXtG#nQYt=H8waR!fiI+Oy5*k`+6zD5*E*l;kF<26(fvTimN*)GMY;8ofV)XQB%y6hMv(u4*&t=rHCO;JfvguYlkS?xiCFe>a>Z zS=4$oL*r3Wv7DS^wo1weL2^+>c4}c-PSpD1k{z0S5xB9&KHN|aCWK>C5iA`i_}Y*w z?k@r22g&DAHHBqGUf}R8X0-WnPx3L}<};(Ywq*yqXuBOh$#G3xQr?~Wd0EnwRJcCV z3SWK?&UvmHw1BqMT(j+QO{^xYO?koLbl963@R{-su>a`oQB@s$8n#Ut?d)KhAI>2v zn?1ls;mBB`8)|NFv!WStpAX8VMU$P+sGZ3d0_2wQoNq0AKqks^{T#o_#;(zd3vqGHJ{!*qNFDOD1t4UJ9%B#SWzzKh{l1-t7p@* zZEpM(Q8QJWi(b}2+w!exfa{BBN^P&73y6nx9UaAA+YbxoaW1?geoBnOb8stP!zZUA+Z(gj9D`os8> zb|44Jwu~0wWn8J>nNX}lk;n|2`UY;@FA03sO_m`L(L6VdZ_fSpSZY@wAtd=H=E!X< z_2CEn($d|LL*9jH4z<}udI7g2nlFiQ$*BoR0n2A^M&-KA5ng6`=h*Y9EOdjs;cbxf zZsaZe)F~FJ`_l3hBtpYzEea;xOr`bH48w)Iim{-#ckY4jey{!TB@$E9^&*q|^812=Q?8Q^?Pm!YkWbs}-HQ4Q#pq>C_ z$!G0iK-y4>DJkjM>8Xi{sj3MlfT$}F80omdSDT@`(cC_JhO9;jL`kY`Jx0dXwt;F zWA4pGC!sVpm7{YPcj0gk;*90~%$vC2qZRHU)+8Q^lJ4@sLKU{5v`{oAd?I;6^Cp?z zFyss~*%^z9+Qcvd0#kWq0X-1u)FokdqZYK8jNIr|J~Gb62nZpd4Zdt4k(P{r8ak#I$vzm|)z4ME zm@nV2z!(uAlA)T|={V7~BIEZfAqMRdwT5lB@75=Ac{UN9ja?daByIA`RCvQs|0v~1 z32BLgHIPV`3cPk}zSCo@%_Av!Dmrv&{9LtWF!T>hXC>E72U5>}Wl-2q*#xj* zqCdM0f1Ok0FWHiNWutNb;ruOtpTZ_xQm?|TnAA^Lc(R!BuQW>3ycD?C)V+d_cQf$z z^>)gO^uJ9{EpF+mexG+6V)FSlpD0<+Jd6td_2oLUtkiYVT3QnZ;oNIZl19*=Jf$ zZ!9x=ix|6?g#4n3HwGeOqKpKX6!NdiHqDI8im%zO~_p&883i9%O zj;&hXgrmB47l9@2KQI>;u1wij>Xr)33H1$ZA_cGJBV*}NiC<3%d}kjeBv}pRB92`1 zD|@%pE*ihGuP&j=%q&*gV}nxHQV-62IfkbQZ!fe_#8D7aQjqhCyPt8n;7VV^m^;e( z-d;QSFwGX;u{4y_pmeaL^OF?EkBTAr@h3<&r|2|!gTYTNnLZZ23gz4$(>24Lp#F|# zibWTFtHJt8I66KydObyEp8X3hS4s;`%X6+!t~kX=8Rt0!0qqf6+i)8OvWgGm_UAK_ zs>s;s93*G3;H^^%gZxj5Tcl;=hh+eI$iOv`;Fy+m$R9Km0ZfUif2Ro_XE=VUcVDNizEikdYI z_==PQT8oJdl}L5vjinjO59uo2R2FA{C_bK@kz345&hH*)iP7Tzfo9{d#DXx{WRa{S zjxY4Zi*I0IIy=fGYx@$f_pRP+QPF<>O;Mj0qV`}YlG%`2zSJoz=#_s$#Ne4MxP|CY zEArfUg!edfWOol4{rA;?aW;H6zj%-H5$(&oNVMXZ1b*x9RMAoGq3sHnXR*Q)yr?kN zl;XF-2i6n(xdIkh{f6l$5m}}cvBVK6IkqWZ<|UPmL~_&#eS%a1P+~x_e0(d14VeGZ zfKoF-98h0nfRN-Ja0L(X^8UqR1f()j9rrHfj}n~gW1#5)5YYiaP$Q(Xf+Pb-XqN#9 zq#lkQV7#phN?^^)homfLZr0yV=!+nPz?VVfs*vPH_}&vLoLUD!iZTGP5E3x}F3F>H#3gJ$V4~IMF1r zzc@b-*a|@;!cI5Dy>}G6A_0;li1isG3yQJKgw|FbGc>4~RRaQdk6}rGRyY!JpqNTw z$@yunXhEs^!FLH5LoM}7K{fyi(yJ}T{W_Q`fJtT#QXqlg60v=O)0^>osCf2Q;^||b zW9Pn-P8=f%x-53`7K^GC0;d-Tjy#o!P7sSN;7Eo58N)hF^MKeZ+eZ9{hgN6tGl+V2 z#D;(ACufWZ)(?6FXQ1`t1_*%w^yAE<)0b~cFYwV~g{2O_9|3WdIptqbQM4>H9b?`c zxCKs(zo&aqV|p?q`;%WL0i`1_l*BsuxM1)o2oleL`k}Uqw|pmpu(Wa@_%s& zn4!{UsM~fMkebQL09r4I-s%L}@F|_EyF4WJZhO}*QvlLF1>qcSB$ z1kaP=Q(1C5ys4%MXIL9!>c6WlMxFVmD*c$p%ed&K42B3IeWSAS{nd(WFv4T%JHQf# zmosxaetHUbvp@g9@%$oZ0ya#;)Jd|*38K)hc*(H13GNjSKU9)F-HeA4#for?NtoZ!>-&vVG_ew0=OzrP>yMkMPm?7Kd@8M|M zb|J+67rREdGuKaZE0*1vmr`eIM->cK{S5PP=Iwg}NMa&p_gZbYFPb}-1G zT>w5iCO~&^A9MyyP3hEb?TjXdEM0eK6^I0F>MP$h%(X+)KT>G=_Yv2Xw5NubZ|mh- z_3~xW5@AF%V56JJ(PfVOpj6%6U*R2)0v~wns8Ou?Axbfichpx=9q=w{)fVjRCQ3b- z7+~n{ieTrgLroghnfg1FY@~I?=yB{g0aJW+z*=&wEs^mcYo|C|BQ+JeoKh^Yy|t)U zcYQIU;l8_m9`MC~?%HIzR8v+Lp+kizwRwJ^Qux<{VuU@w-Kwb8?VI%AZDD{Wo=f@L z&x44VTz*r1D@d3B=Ct2TCAjd={j=jVFdVcj$&y;RpVF%cK%&sS+ZmE<|G-qUomG6t zMkdFtpiF4i&yJo$_Q%*9v=(i`DWil$qXUvtvp?6HXMdT^KWksvd4c&>ZC@XF6_7r6 z#H`?Km?asgaH5}9B-iez49gBnxrYVZ3uM{Y+)|3aMfX+D3HB$g5KXW=9JG^4`~#!9 zc_-Or%Pb`c=^s57lk|N&>PG{zy?02l_CAeND9~BUQ8%rap%e|1X{_?k>e5L4r~3XR0suJ5M@ndNWH<&Fj#QO2 zfn6qP12kPcyv7}~G3Z=S+h^~CXxZ?xE(IpFKh&=(pVTNZqSNv1qH5NSt44d7BPG>` zA~_NgkGu7YxZ{UYswbh57dU2fax?52R!7N@8HvL6I~NH1OK=OyT4Rvd$0ZiN%D|OH zbpPoKxKR>YWrtcw3MLbF!xSze-wJ-d`;1nj8EA^z?yk^+OeCm ziU|I@im8}?{9ESG){djl{llx`tLMW_6D&TqLZhubam;_!Oy+PdWy0Ld5 z2E4e1A#E`xKMK-IemKMN=oH!5$Bm8Vm`@2OuqBQ=x9TI-t`lXrekdC3_oOIfmhG3U zcH34(5ewTdKrOE4rVr{f*G_385~!*JZN4{cV3vcU5jUdgUtmRFe^({PLsKFd2Q(!D zu3pg7in0*t(3o3hpx~E#9D-hm5QYbn5YdhM(43@sV9YxwtHIl$&4O*-;=A(K@8p_b zkn-2KA*QjQ1#&aLZ0gj~GbPeNXW~B|5ANs=;^=tqIwcz#aGI%Ds(#2Gs0-{n-A4<& zbN)2kU>PfNg>Y_L7#T@EDk8cDXv=v6IXP&E={jCoZEt;hVxs3fp}_wp4QAvrP&onA zVHewkZBKOd=DI_pO{i)0W+&nPNXv_&q!dgd&vk0|qx``m(^bI|oE1+an!3N-jO(px zD2dE<#xzBUZ>KsiILcB}FQv4z8>G907fR3Lcr}zMk(A~<;S)Z#s1ws{xje)_*An-$6Pw*r9aYr|a z=%%k@GDDFr7+#bAtky~iS{8xh0dvz^-n4@G&)CfQCxMaz^ZfBREBfdS{hrXmkqyRK z-Fy47R?%qRuyB-IIh0p>j?=J41eY7uA06Gc3xN}R(h*HZROv_Z@-)PXf?^h?n2MO{ z8aKQ-Jg*`SostBZUSmRP(8fijH&}Lu@xeY3ldQY4oPYf~ z0+PC)O#kuMf_KHz`}5She_+m2UXfn9Xzt|5|AA2ff8AL%{R4B$MccSy{OKK32+%*D znFWvnj2Mes44eqs)#HMwiU5ij^g8~h5WpMo`A)#=vR1E12Epe8h5Mu;XhL-F3ET?6 z*l(3TU*@^ful3wtKTrC%Jr=mQw}3Z6V&)}$d(tFJsRkS-9}gfHkbN_GW**)je;Z_( zAv#dtM*&3qzl@+*DiDtrBfSE|;5ws5#N@C5U9TfYkn>z#9wfxZ)!uSSX#hBC8@}p1 zL1usdIrtJeF1GlY*d6SP%d;AnNMDS~+h3bacKJ(6XRCD$5SyZoQCyrs;-s?0y(q)2 zV&DT}&+kE1-yax26R0WzI3)tqc|hsAr}S){Bni3<2y}@c&j*m4{vaL#4psoOf553R zFDP18uIGp;{x`uQ`|lwg*8A;}n$Q0_0bixFW+j#GaMFU#&eTphICKsXm6RqXq=T91 z>FJq`O9#?3GSZ{KVyUUA0fYUqB#etlSTq7u(%jWln(q+L{HFbs+oR?o}&Q0t^g>Y@KoWHcQc6R|EP@({|!-S2mAZ3sR3u zaZ#T31pf&sV5Fl^Q+OERe*tW`%6*+M#`tXklD^}OeHiTX|NM)%@{B)Bu^!v)4~&s* z>i6BLd4gK)0*PqDYN4aclheW8tVjsB6uZB+@Pu=DC214)#FZ z!O>Ri2UmXhVPUvS<8I&1Qk#on=592sBq?fpN(7RJ*z zpLB-?lS^{k?z&ttZ<@?gf4dw5Z;DrBik5bRas)rm4oPr`Yi?q@q<+rL%QFUH zWP*)~4lU4lJs3AD{Y=kSq?DbP9ekQ_hN*fVg?mG9jb7LX?g_f$HS4Re`5tPDYJZ$c zz>o)%9F*`T4hH7!FzqW?S;mp}w4L_;=kx|SUqzloOQ7&?(T{RXZ)i^SW2tUkdv7Pm zlEUf|xzynb|0D-KMJ_NHje4m4bALIRYeb;y$*lGAE?E)$hO+bKbT@<}7Bcdi{CFz+ z3#^zLG%$f-)Z{VdC8XtJ{bb_RBjTtj#bc8G9QCYdSeYwJjeJs-ANz0k-~te+UW&mH zzLatn!|#4BX7n&*#afkTexN6hW`U$cqw3sPNiHw1)SrQI-ImITn4=iCp|RO?Y^P$v z#hd=DijeQ{8B#l+qCI=tx>tU@)QE!Y#77u&%~!aK0o~PE$Sr45A1EcladxBOcYB3C zy-Te)QIPt?<7$V^pV2JKP_IyNLs|TyBEJ9Z+ndBOusWO46+U&ZEmZ*D_GwIEX2$j2 z)~XtAS{*Bskuu*Y+;h2|XPF_Tg*qb1(P2Gjt|theO77gyRW-;l=nQ~vT(r@Q>YeGc} zC_&VFi??HTY1GHv7r+x}%JaTmF>;4#=DpIby%Wopr@~1~(^zfKA-$8?_%8c&&z#I4 zA&4q^B6>Ywkng4PbpG4#AjU<8UKNTl?S}Xd2l&#}A^de_@;zw|AZqqyf_nt)RrHt4v$7lF=J& zf?5tf-ub5?w(k(aDHWwCH5TaTmx~C$s^U><&Ns2AaQ5jzaLYZ)m z|CA%%v@Y)H*uC(4Rey-83zcI_6ZBfpe-73@&6~rno-9@lbd-i(t+dX`6NSL4LUWrf`g7#x#AvlNdz` z##rqA%-E{^-a*9Tf`X@<{J*eIBt^qFZZo`#PCHF?^Z#@SQ$wBos)oh7}SQgyRVA^dR5MD+8Un}vkq!ttJz zfO{!;VQwB)32tL9F`~ZKi7O@-vt_K9J@gS>a*qzwU3){;6az}q!B(~jJ|!JYL4vAY zyd`U!6@6h1^f|S!VUxY4(>Hd#94`(#n zxNR;8Aq9_6w52%xn=m7S;WQdSOKQsJ03&;JTs)A3NCq(8h0NFgAaU6t99e_eu0)=r zPcueWt7lQ8H7^{VveED=!nD zar9DPWC_7~C#S^PP;M`G;_}Gb(kw$P`aR~3!uvD5xwUL>)}%!TzMfnhOAQ2srU%UA2Do&H~Cxsr(ZzpR;xe6E-0s zC(W)v#M#+wbawO8#hEp(y6p|^iEeM~QWPE0qqDb>)eQA0mDpl5OB&2KhRIsQ*6VWFA8Jk@c zUCB%HSMxZ0@3rehe))v{wr{2X1Cymd{yWWZ@M&eSC2DZ}g~ktWYnu#koAuKA2BsJs z?o$Lo1bCms9S;^TQjYrB_W3mqyAYL@X4I zYnH*_Xjax!mE!5Z>xen_U+~q`UxF(xMnO?gsic)YPUcbU(~CfEq*z|?Y{G_YVUI^( z{_}%k(pxv7_;sv{#wM({sMUk=>lGn#UKzwuTKvURa&t11V4T*X#^(SHijiy~aJkjV zEbhhTyZ8s3O(5^o3QyVPe_g zFCq88UqW0CG8-lrn8h0cnY=-g8}k?%0)qF%luH9=QE}e-EgwLpuMV4wLoPH_<~D`W z#NgqHH60{CA{b}z)I#4ly?CZK-wbbWn5~W3fF!cV>sW9p)|n%iZSJT=$BY!ZB(ZaW zZr{=$7?0-HqC5)8{u3nVz=ayK&mCF1_!qtzgzZ!P=Vno)ku>J@#Ck$UodkW2dPiO| z==5>*Fb!qP)B9WStwNmuDXtffPlRI9JVi!t;OQ=RUh5ZJ z{yZCU+hO+_g@)y%`xtMJD+5!f_^%0~cD{g$TGjc&_^WW)*&i6YPGdFO94GMaC1rnx zn-`2bzX7j$r`(IggCZ7ml(XrP#d2^;j_1wFa9;e0+yqFTcsv2o47-l6p1u-=Mq z_9oakxIA6h;gS4(G#>h337`y>k5sLG*_~$I&xj6<0m}GA$sHV^V$TkAi$*T4fAZT! zS>fQ!-@{fSFz%?O8s#+L_Hc4ZevN6rt+`PDukcq3an9`8jjfyg*>3+ouu2I zyqk}gqUvo?XK5Viq^nhR{!T+e+~-W8$#@}4Aj}_MI9y*x*6ChIqNiJL#)s1rvX_5C z#PmhUBH@e6nC|r)&PbLSy02hm`7)y z@Fgf0VMtdEMUAO7VpxoaIJqvYao zXDw-t4-s{JS53*c)QD$|z+1_kn0=-j&&iDP`b;mVeI?0`gL06=)QC&y3T*I{`C+vc z{eIz8`wtACj`Radn`lyw_Nk)5ke1p$mQb1b&yQNUy|o`G<%`V>xK+z01~?O9PB{|P zlKEyh(d7wtz4+L2e%l!w>R<>O(S}X)qV^GE`wCh*pz0U6y;Ti|3qV9hv8Y9R@dBKJ zbNA*@Uomy!C_9*;xj``6keUB6Y(&cErr@4qoUn%ErA9WDz*aZ2uY-lA*}|&5Q$<-y{+PqBal2ygQ9W6z_pe%M z=8g57eLCJ->{*t6VG|;SEiH>v6v7k1mkf~*>JDIMno<>mrHW&(eiQ!#6bR#R1Oosa zVstS8@7ckB#c;Dg_}cDL4_O-3dn8j_l;xB6415m*YF`kG9S|r2fm;O?0NSeudTvHc z9;D(0kg!x4c&rRQK4A)!ng?*t0T-W2WF#>Jpa_j|p-0@+3@G=!>=%@YPLixB z?Q%{m@ku+)ttuZu9uRV`azk`~P(mQ|ZW17!waukL*9^cB_lWDe#>9A1-}MO2vlS;| z;fdOM2k9QhgxY(20r-87GCXmtBq&h3jGP(rFe-Ym^nj!qc=)ow&vl>RrHY59(bT|v zVxbILlN&y0vm-9MaNIlC5|;~HZnU58x)*GVJ8|(f`y7iMLTqRj!(Bqv0#b6=iVg>o zL?0~uB9O9L7tf{u}Vu<-^Uz#yu4k-GAxw3ZNcFsKgNR_FLZB+Cb0P08%>KO#385&95 z@!8vl??xrN__5ioXQ~Id2LH{*Z{{53y?H;JI)%{HUWd~%H^^`K2ZjxwVUjT3xZXDY;eO=s^F@^TYnphkSn5A8bhIM>l<~nQ0DvsaTqzong3{U! zXkQ^E{qEu_y>nRpAxRc*cdA|b@aIp{ioXqf3|kPCl)8T&uRlVSgUDI{cB8%s!m%c4 z(f!^-okoE*u-tKZ@RqySh`niMwfzu+BdYP#Un zXi^=I^@*Vb!cJ(fA`>!@)9^2P_c@>$N~eNC51R=L0mfbTU#-A@;|&>RAh*_pb^;?( zbq|jj$)V^gS|~v)cAD2S+2L>Kp?zcL)%bPg<4yP=L`GG};&$Z}m!N+{0c236k4jTN z>mAT(wd==Fjye4x>RO3ADeT*?7ryV8+o~D4N7A|3j09D)nVbBrX8TWOVL(hy$rb!B zBe7iO)6{cOlm}HE!}++{5=+fnF!ePdhQV6OTCt=3?HuN{2a3__zT8j?t2?^cu8G#* zJt$VLmd{kVx8dVMlCANsLC^Ukh%VGuBezz8_KPqb~fGkh?)1&nE}1=v&%1r zAY+A#t7JcDgAfC2v8`WCDv|uf$i!?Z^|pn{YWEbl5P>Z)+Z6B1ZopNp>%ep2IXhVc zTIAB4nu3Is6kzSp)-oU`2SI0_I>#%Q$hjBG0sd=(F3}u&H2ef8@D){XE&EY;i7e5a zG?B%r@HTgq84M_Em-tDwHC19BjI6O;B2mh0L%g=Pe1Xiyfsc%3tvU>-f7sjHT%y}v zcJB!8ENfmoDadD&&x?&3%!3`u){l&+PBzW+k!V`A(~|qwm*aH%U-}!3j*aTF$blyU zVc)e?wq6#M6rCEqo49DLeVM|ylR$)#q0JK##XoMJUz)+lkm*cSma+nV9zb8{9Kk+u z+8_s1)?@X6`6W;-_<#LR@ChGanb6^6z;ky(Hj6+>c8}+p2kCN5*w03}PR}V#@8+Lb z?th%@S^#fv_C$`e1YL=s$i%H(4yh#Y$PG#}5>f$M%VtYNgItGbb|2t~^8&EPi5)D7 zwITcDs*?J$jI_m-MHzDMGQ75&-CVncl+5F024VCr`S^$>{ob}JyV~>_E!-BqGq9(Xa5dDNz7T zyTL!~7>zvSD|fZ5a094QHr|r%!kVnFc#i6(=O{fc6D88JHOB>JFfl@*eauqyj4+aI zNB{Yzz$I3#Jigo#VvkBU46jrXu^GJ{Od;F)xg@f zp15jk?T7YR(&R`_UEYpT+vp^e=dhNG=M-O+hP{Gf=z1gNOBd{gzY$PCihEpmC@v;b z(*J4nZ-fK>c`qg^6kYpLs2T-i(q-#8G*(Lzog7e6pVG4*XG(>1S`zWdbkkYKA!68q z-4V9${ThJ+=_AtEd7L-cxMy5HGhcleAV%vZRxHYNidPKgM;jgsCqCJ-;m0Q^A_7gF z#F*&=WA=erwsjxPq*9KGN$KfUjW=I2bh8S#h$`*7imAKFs@VsWglv4dmA&q>Cno4= zZ>5Nf-b=it>U*m%GX32_4w5~BM^tMspyx=1!5Bj6#DdzMBDLUPOD*fQVP z%7chSFANruLX@%h=90cM>y76dgqBSQz4jr*4*9RYh_TU)Zpb{Ewlr^DV3GA$KQuOv z#J)qTClo8YxYZa#*IKZJwK;Vz@2PCXabqtz$hAVHGD%RMm4JY zdx#jV59f?)3Hcgpv84;X<+#zImckbt;Vc_Wp0Dq`!#{x+WS2J6=Ib@UT^2ewVK(;K<0w@zKDS1Dge8pAaMvvAy=lK%#J|Es;c! zMxQ!|ou?e%kvwE4L4CtsyOC=>)yR7|oZ{ifK$-N}7=cI>WuzBlXphd2>ZLG_o|x?_ zGlVdw=D$|%(MIJVaehoIWQ&zo+l^R(bZmH@x=u<)w_5l$5J$wsg&&Uq=LS`!@LR_D zg03MLhISXHF)pq(FUyxTTd=#}I5ia&5uYA|&M7o8Eo^7(Q>i9Xs zmP?sG>6^u7FiLuPvtejI(~<@C_kG8_Po7Q=#%E%(G9$K=&hp=_SrFYd^5=~XFvvk) z*JD)V3L8?wP`ySWSv9Si5S4?+DwYLrc;DfTNriF!D$iAD)>e_Va9Pa}728w|Ig&Cb z!i@?cANJ?z*c86e9fNVG=~!(3k81P!L^hGIo?*Ma23GOt5)JOVvFfTTu4j z_1?+f_x27})-<$01vD5YI?UnR^O~U0yEyl8@L#|S$?t1yFMj;gpV$Wqh*rQuQPDv* z(a+VeGG-P2`xx1R({L*y961h=)xjX7xP9q#G;O-+$5C!QRsfqP)lESH&5O`hU-0)^ zvB^0{Qn8-?lU#Pc5mr8&d)FGT5M0pdoi6s-f0EhXhz`A9x6x$9)c?WlD)lUJ3>-xp z^`1?|S#^=1`WyVnz0>-Kn{Xdyiw$Yg5gwR7CO{8&03I3OeuECDKl_%@{q!2j z2fsXt=}_|y1M^mez{tmfC%wt}#X(eH4!AEI8ypMh=QY974F3kLLog6J-%w}KFJjJo zU_#UCUsw>FDL?!cn$vmM1hL*_!Rf~X$oy+lQk-|~0m+&0#}fuu=}y?+z!5EZ031Q> z1P$WXl~NQ;5Df6!N*|aam7rdA^*?|Z5j;?>4nKI=9K(3>nU*+zGVweUIyB54#3sFZlX^Fp4=bI&_U9bK)@RCSqV&hG_}_x zpCx5D2>)}N2Giag|IU{y(O(Y05Dcg9=Skf8YoP`~aC`|cZofiNhYHT;iy<>?--E~G z77~~3p5P?~*>a8gVBaZDA(!s;&( znCeO^>2<0va!=HBtsv9#?)D#tuBy%i{5%*1jiKQbDh0>`d*+IU;04zq*^L7{&=b|w zYoPg^kYpOn+1o4jf#|SiPnRDP+JGh#FUL104Ca6EMB0mPmHk;hDgcZR`Kh6xs!sP; zL8ngx1+@zB!j~NdFr3a-!y+)8`VY$_gut%?4+G~C-H*wGM&G|J=v2$g7Xwcym>$h6 zf~Pq0q_mv*>t{i5=9h>iV1kq@vkK`4kyv0Eck1h}-IBCV06ypZpm-=xiVeKu*9qCa z$$0tN+8MaF7(?*Gr^BFohXZq?H}Ubo88$ypdUzd1RrhYshCMT-#szOlanOnd+Q(zB zJQA#+AmB#6vA0kM?Ac7pUa;uE#fgCcd*G!WdBVH7JW##2siF?xM73b$y#JHPEfm!M zTzf}h*sCu(?#skL#B~~0-L-M?HEjEz<(vqbDXuk;L}yiEnq&&I?(2%C(8Rv?Gi(f&FX?^hFgH{r?n%|7bnE3a?C}q0H2N#(;q1k@n;-n`` z*q|Q7F{XGp4sJ7d4Jn2;>Yl~LnES^U*(5&KDN9HlZ_})h=pw#1$VXDbrch-i$AnMf z_e(ZM%1YDRxL+^NOLXb*n?jrItzA6m3k$c+IKpR&w9l@sJtFCQ z<(e%hrn;cibFLz>tf5DywVOqbYHyLJ^u2s=D6Y`5ciheh* zNE>3rt%Ovu-A4AH`i)PcFQb8I*$B5ufoA8AWH#o4o2f6V|cj!Rk0Egm5MaOg$@;p;fpo=-Dtg1@8-%tcN*2a-jXqff(d(~@fJ3M zQv26O8aX%J>hJi{Tdgi>F3E*#a+dRC`)wx}S`2k2y?E3g^X8(pb@B^+2@4JBNQt%G zMBR}GAQI#!;cBl+~xNluFgNlbG2IHF<+|-2OQ^-< zoj{#hxOJ@z5Bgennnd`63e~Q7XP!L8{=yNn9|;xSNiEO;-f7dsQX=()t2m9}Tp$Y*3kUjP zF}<}lH9sDzHp$*J{p2!W`%c*;tbP<|*u#00mm*R*#r(tcUK;uL#W_JD5Z6jbNbeJzz@IX>>Fk`9 zaCz%ar4-+W79D;0>C;iF9zpvDT4vK5Ty(eGcg{cl8d;&Am3x`8FKu9&no_E48Br2< z`7FU+d4iOHjX5syDFrW44%YkOw!AS~d!k4*b57>gH?B!BHU` zEPKu%m?2(kU@Zo)U~s7@AuoqcFh!SjzSJmDomge@R2&gg`%%fUiF<~enYo6engh1t zt<7bN7~-QO_y&RaH<=z}GumCaOy(azT3}R} z>(@G3=BU9Ss^>q){>X{8_h&J?4mdwBDwMZYaA9Bwc6+j_5J{Oi(tqgm+$YVn9VTAO zzk`T68*O#|k-_iPU^xEjw?4bjS+PLWMzHujZoH6<4786mmp&T}zFB|PZ8l}=k{Y$X zK7swI-Qm$|T#{YEMi`gc-MOczPZGpJleTg+O{hdES@E=MMMokDI%|hWk^}cl@pyN8 zXUN1o#3*rJl1LCtDo(Y+J5jFDcJKa`Nih6{ls7rLUi~U|+eLr>Zo?7o4>QGIcgn6- zr&<@}C|TS#Hnx!`Pjw1dgRY1)J0#2foA_J=;s%E1eap20FQ(9D<#e6CDTND~ZnT@vsJ z9eycSfs$-67Y7MzYcIfNA&C&h1e2iYJ7a?OMqPq-GSJ}2wd?9(e8Ud$W}8<+mF6%G zGTNG^%G8jht%=>rnBXR!V}BTDy|ogDuRsQWw4t7IZEN8Q*T4*m?TBvQ(nwoh@U{)UtZz zm3q(=^Gsy8H^Sbgqb{S98Vh=@1a-O}F(YvfuXK2PWK z2L0)Y1$eaOXx**-NZA{vPNLI_>fQ(SXwYnJQr;7rt>dpE4EV-^7Zi$q@5l^a!AguT zgU3W5Sb9BqNvAOm@0c%$fV=jvp-!8x@aA1*4#bgk(Ue%w?FvDCZ<@W-Juj3Wym66m9JfviTI6>4Yu1lH0Co~_e>ueMd(f~ z>%+89r%q3Cb*86v6wk};%gbp!Uw;s(raMw%jrLeL!!ce~U@6j?=u$xBtsfh>Eh?fM zfHB9upk;+nJ5*}L_Z_t z@~4nX6xa!iZ1E7(7RzCg@MZ6yTkdX6mcLM_K}^WK;r=wBW>z6(1IzTJC+(;E)QNg- zcYsjM4C%Pj6UuD2(Wj$Mx~Q*shi?~p6lcvZ^S+^fabt5s>}<1xuT6>U;>eSm>yam6 zg0I(IqlWkEm)$N$9N(6HJvCPFKKd#~BUH(djwuwf-a9wq5&gnr?u)}VA+h$A1m+d* z^N(Bj-YUF!<3pc@jr-@3g@Q{y-zY?xd%R1dSg7MJCd=9ILU3kA_{yR2jGYl`@rIl5 zCJ5?eV964yWCDHOp=ci2J2S?2X9&sm@7W@5Fn>r@xstiUA^en|bNLqeH-g>IgiqtF zZAiqoo$ePU9rKN(eT<=BzwzkZmy#^fllbF$+gq{?(XX-&-rc$-?;#Z0$BOsRCE9IJk}rf1ym)-xJ9g{p?7UeHvK4XrER z#1ZonseJr_k?3%0PukLJYwJy)5iy`1|KyvXhS98Bb0L6%xLWcBoG^Ik{XC z#ylzEnsP!8z84@tK*NPBFM8WSr(Wyh;7D5jXp_Zf_kp=jZ2TloC2sWC)*>#-u+TZp zdG6VrsqBysXlzXbHED#4?|N?@INQILY(c2tXQ*lfV3_yP6*_m~I1$F2;<1(e=dY$_ zLMRvNmXtM|zY$!7*Bjq{WbUZCV$p5N*=pIyTF}*Z$|#Y&y8os^9Ntb|S}iMLFiJYo zqBlNIYl_l6ph_-tRu53aB+eY4+pO3?(P5t_AkPAU51u+GiRLSTbToqL`;7;RzK>ze zXZ|t%hInJZY5(jt=rDXfDv)PMN(xg#k%!P*Nsl0x0$Zq_4VfQP1STPAH~8rN|F0w# zs0{EPh)Dyj2YQe?@W%UhQDc}WlmjmUspVh5NKp=?K25CR9t-Cz(knhhW*~e}Ef>gI@XP}w3Gp4)3h9BzgD0jLfd*Ro&uj5o4-AT~4~8E&Q%FAm zyb21hq{*S@N)$*$;3+{x)CV=1|Ix+&YeuE$Ct|V zMNh#6OF2SKNd*G_``{s{x_hJhz`fBqrwFn;OoIr$(RG9#9|I<4QLU$z(Yys6tW00* zzdB&pa!Y{r>U^4*OiCUs^Z>LlLjDTR!~XiUhu3Rgf`HbON=-4+<6#>Ke@vugd}K&% zp&v~_bP+P3YTp5MD}g|V>$^~ozkbr7)mFv@5C}gppMsx#>;={cgnHA#g7n5o_?s(a zFaxJN2G_7vfGZ<0S4q|Hi-;f7AOHtEvj?1n&Ha@=iIRmZxi+plXwZUB98Z)7k{dWE zs7I5j@Rm6y(2_#dI+(R<}6@{@^~P(uHdwghB%8m_4U z6pWI9rTwvFx-kAaQx69h6tV)p%3JrRRexu9=>EVUB>w!VLguz~NQJydS44&ISrF_Q z1MO_fKoOp70je43c-{*GUoC;mN?U)lF9u$)XQ-(Q;Ubcgvy(RO+l2P@Cr*JIO@Sn! znID$jkykfscZQL+)A7QEvQv6mz+ef2uo3@Lw}zG^ShH|x5Kha3)@JY{P|%|Z$&q|O zngslB(P<#|Ry=WAP6pPVf~LD{*NS$=hCw z<@Z8safqm4&K4GfDsM=0ILHI`!Z~ov5uGcpZ_HJ$!-_3Py^M#-+{K0>k^$b^cFbn? z6!1P~G<1J$7!fuEH5zg>WSgrFsT;g2U?V6JKkro!7m4lmCQEL7c5%AKVk8D`=Dk!3urj<2AYc8CYh-KQPef@fip;iAje9 zC8@@&>^W(KhAsRGGe=|;8J^ueYRd(CGe0%qE~00LnBbHr{O6Kr{or|(46DEo%~W^z^0lX;oG6E@JH00 z5tK+Q&xU1%3WK-Ngx>QopOmtuR46UAs*;fUlm`p521_gz8v7WEOhzTWcfOmY>KoZq z#lREzQ}nUj>gzmjlW0xC%${yO4CpxWzN@iE!(;f^vckC;9e8MgpwguRF)^}FJi>FEsdf-5c!5wSSu zAyYr!x&6Fmv{SH<#&WhF|lX+wX;H1%Im<{ir{|nqQ&u9{fg5+1czT%-b=Wt>1A)Zk5w=(m~NW?e3QBQF3EVLA=Z37V(&iPLfUL8{Jp;ku19!H_lPwa%F>NT0bWf#b$PYy;XtU z8=`tD-WU?C&eiV`X6tbjR20>OAhcBfgw1LH>lc2cIVZ3Cq-()18jqf&7$dN+^Kfb& zXClQ<ELD*6Yq^^ zw#&|GW+&;pO4z$fM=)|&$~FwUju}3nc|7J3=;{%?y`cL;3+)r9F7|tTBon;)HFGw4 zw0UpWjN7LXGLI8TN|e$wu?8&_W~pw(rV;eBD)_vWiX@>GQqbk;*LT04uP7a~tQH$- zy{JWv;Y!-nwc*2Hxa9Zx$cEy<05cO+c5=DktPHS~$YBL!;Ph}2Ab*^``m#+e)vmO#hXJ2Q!(7lWNN6&tzV4hP%xBNiDVA@3pzguD z_bYMj8gWBp{YM`loUIPT#;t@(b+4;wSL>TUn7%Y-PIQ0ea-yc^4NPR9 zvepVtk^fU_+Y89^%#rn38*L^~ZLSV(;WtqLcb}d5ihnH5v0UiGeMV}1E9ZF@3Bt4< zhkVcJ)045dJdJX%rtt;u#3HTrZ4a5;kmKIgLZZW_)(xZ-B*Xn%3U^!T_y&LCSs9v> zD>8*kbd6bfSUXH$DdnpXsnr`8lD$69cNcW^WK3|TmR0FyNDL=2$BK{K8P-TLPI7lD ze2w@uLgdc$yw_?3yaFdZ52 z(*=@!$>J(K5|fAJN`y~8IIl%B*E6moBSmw_(9os0L7i9OYkb3tmCsygSY(CAJ$8;v zrOATXtj@Z1^4sx@7wuk1BpU1uW(I!sy&lzdoG~+xRDF4ToC(6dH9rJ*xx|$cdclKl zUEV-QylU`Oi(A0xBQhz7US4KpG3|k6#pQDAoR*WzM$f+OjM3prW8c~`a3kx+rfQ}! zOG!1Sx%&ds=}O?6vw6;t=&hdx8W&TW#xK^UUnMiOsoD zc|Eo;6YvP(Bbp*_{aYapuan!;KD1pzRE2A}Y?D?MKU3YJoSED(^_Ln_Z_D7bX=MfJ z)jfW%`Ge?PkY~SLy7AJepK_F)R}9Y@3(*??E83MGalt+6SNb4y<$Z-6+%|^ z1zW`V2P!P=9FbR*KY{!wmc-o{ci(gt zZdI3|+2)t+ny=VayiK_9=;r$;BS=aalH=tNl+0fR$7?A*U>F?wX{XxDxmoMT--4EU zY*C$`p7fz%__-l>|GV3LUvR_Xnx9Qbe%2(^UCm(B{P7dL8%Dx%`)TrwX40V9h(na? z_>2yUBf&UO&F~9Jp@Z;tjjLtA zxWxTiS*!M2){cn{iTffKPfM)=9#E179O^QF0y+FseJ4*@l4hYSv|-_@FKoW(p~6B$ zMzm@ql%#XtH(kM~UCV+$A(DG8YH5*VBIh>xz(->BA~1<8&Wbu=kDsF}H3H8@L7Q%A z_(67DMp8xsj76(py7W}YK)CG|mPUysi|3u;D40nM_%uBDUb*`+&0MWf;|QtT3Wv=X z^C)voDTLdJOA|L{aK$u1c*I&&;gAwXls+NG9%MGqUfoK&DQR69yyJy}bI_TUQ8wH( zq4eE4h{}gBPMqqwa`r%#ROw}o5R2uq9K+0=EIxv2VBqexf5X!Mqu3d-wM~iGVpv1i zRI7I;z2J_n;jIn&Wp2$ZwWPRw^+G$|J5Kyb3_FG3Bk5uL!*eXlNcN<6jc=Sz{Yay% z_4_9sysgGnUK)Awzae8hZPwnc_pvK$R8OZ0-;z%}pBZ-KS5;QwMnjVPE{SK--|As9 zwlwC)AHzN4-tRb^#)D_glT@3&iwkPef+jyI4Rd!yA6&WEOlDIy37us~NYLS*$Ry zItPSC0@4Zi5>IN7+m3ulZ1cn3oxL*x87`T%D}51|kKobKJ7=2JouL%^$=2f49&BaB zDN?men1D*fQmc|mFy9?ml&dpM(LhqglB0pF^8nhrE>P^ zO}ykc9mJX)t~sly$c&N=TtlHrehVrF>bNSMs<8M}!D!kHlbL`Nu0#}l#0P#Y?>vcK zj{ zVeOryxi(3WFW|kwesv{Z?UNh%t#orhtIyNUO)Cd2NsJU@YI6;*rm*hUpd#A zU0_#SU2-|w{pbIic_5Dswl^VvA2ta;W%(uS{_SUo*(IuRgU$ceC*5@2C%t0{?=so* z`(I>M+xJxBA$Zke@M?&WRkx$J6wjaMK2y^%aqaEc3XB(z<1>BfVfeqwuMutk6fwy7 zeW`RRFIQ7O_K8V!&!k+Zx|sBzwEv&ci788HmH;sUhU*ALaUP1IB6Jae@xi&BzYLBf zduBJt?gY>V;OfLfsc2DDIFD>BXqt3=^JB|0WO)JbJj170(7Xt<1wRi;NWr)U`76qs zJ>UgLa8v!vzr{6r3htkW;XkRN;# z-uKL0b4`3>!YcRqqg6=v#?y2;7sU;Nk${PYSTT3e`cdlN2#q!kH9P)-*;(W_%y z-iukNwL9N0@M@a0*_uItFR ze6h3lT^nK~OM|Q58|cwIBui);c^t|cXU>JJlt{8owK-i7}B?XxG>ULN? zj^<1^Jt={4t~MHddwk&H3-YT2YiII2FOh1n{MRS}eR_}B~OjSL(a6aF7 zl7!Fj=g%$T+uz1f-`Zb1{D+GHAb~waLVBuZjdM8oJ8%WM@LG5QXviW+r903CVpU)C zB+GS+8I*>Jg9n&^dk+3$!-GOFXO|sG@I=Z&ETniHdKMwBUR%_lJ50p2Qwzu;7M{Q0 zp%<-CzD}62vD-u*5T3HHU0(^oovvT~vSDzLLsH4A)q0gcivDCaMsQ0L)v0pu7$^LtTIoG6Eu=Fhc&k>nK<*WVL%-_zFb>;7xcB`x~x_ zn!tqBs=v=@0B^`Uy|!ri-C@Rb{`lGi*vR*{l|cvH5~!I|lmwTY5%PrH{-Y{-(*yU_ z{!A&a?dDXgUwTYN5sD<9JivD5URz4x(r6K#RdN5X7PvDkIU5-GrlIu&Z?vnhlQhDp z8Wo&QO$^3P{j4#{?-@)NdKcC#dveQdv8E6TJH^&P5Sf;7yi~`>_&3>EK^tBxaG1(Z z0`uQL@8^FaS5Gx>)FQuif1X%ha+efFI4*#fBlEc(yWjFF@Q(smC`gt?{YG#ygEYbG zSlYdx){C}dPY$aeC-%a1)OuTW>gAz3P`w^NF%QW?PXv4|&A>UBa5=IVxYiUUJ;>cG z3ZHl$E+^;5%e=;$CXMub;`X)Xu=;H@A0s3OLS#=Y=!`E2ZnV1s85sz&pl9wsWMp7p zd~4nx-ToUPM%JVBMv)cDN;LmN4Gs9So@1y8y=+US^PxCfEbx`m0!0*fYr%5{Wee{` zl9Gr2v9T7%g5LwF%8L2)K5|XJ8N!_#fYfUh2SByXP}-6N#sCt z#)C)5n8Ci&cmHO|1pycUuQgB(Z zT^h#Iz9pmxqCTMtGiiK$cW%kMZAjD1*Vrw5>wn9ikhzW2|H-u&w|DkIm6E#a2)B8e zMjG9MD%QHv;QIhXg|gX{ImFCDmU`*b=I3?8CFj zJf}^T1feNgx({yX2;{$qR%1wOg2C&*+s+uJc!vg#!PGdn&q%v4ByaZ6RsFC_%{5xS9 z_Hh{)JZ3^4Eo6%1rPJ(>2=vb%m{+_~m5#*-m+TtL4^=Qbx9v?UK>rjfV3hjEEGLZ} z-A~9JF)GbLB#1%IQsLcIVea6pR6L2WqeG(i*MZ}pTJ930)%|qaqJXI6P${EyX3{lt zv<}`kiQ#6?B8>>{iPRLXCJCi*yC;dvh)H=ivg?J&NkrKAEXNCN2g>7&ul;zqy6Ukj z?LL{(l4WF?a*<*;-jA7VLzG0flz@-7>r$-^Dne#a1zqQ; zq)Le#qx6weC6XL~Md@H?SsI2zEB}#_fYk=D>T4-Bdc*Pe+5O~W+yMH1+4!t6MW}yu zQCO3Zel%@^%Vu7AS^t+=qAWQ%1|d6zlmwj=ZNHt<|gdYUBihZm+B*w+qCB@tG#w>! z*^U@G$Q$LD-1{Z^Z08H$K^R&Q9*2-#+UbP!a=SNC1en z!-6;-xLCG=u=yD`CfvihcODvHH&4pmWvk+S4O`i#i%H0~!$5j?Fh?W)kn5x+#!rtF z!mn~@xf5>(j9$W2w;kyYR*22tfUZj+3!He_kL~V61JhgVP84D{J*$ll>J*z$}Y81nJs@$-_#vMZRX;U-q; zk^1TtdKmpiz}DpZd=dBTM%OQ6vJBnv{!1-38&>}MhGN5~hJKVdMGl$l(tI&}2j^Dk z!e8jng{XUZ;~%VmZi{rpLXBl_I%&jg%Y^qvN=!F<3A^W*lR0C(_KLefAjGRH)r^-;=sHN>HUQ{s@3oElxU^MNYv6ji=Ym(q>Hc?xS`i zKYqk$bI)Mv;uZJiR8@0_bEUq)SGM9j`*a3o$JcMZ_i__gUTthP@BNZ4lU_4Oev^SF zqxLICgWq4n$$+s<*Dr0v*_u4__NhlrE8`Q3VA1xH%!56hCYKMUVV>yFcM;N`3Ay;b4m1 zgVaXKRnTC}Gr5vO>7ZwoB`O#~Gln@CII(%Z`fV?pi1eCIitfTpdRF?J>lb93)0-u@ z$rvW2zUEIKJNP%1BenRevqqVe&WEx!)eEJXEbL(R1XoWu8egzik#5_r-T2<{TzA6B zJDTpT-Y-?%)v@}{EB$l7Tl|d0n=U`odM@_-FHOpO*V0nHFU%CDrBAmL_52jC+@T@l zFkm?1Fs0<%(QY(^Fv-iQg}h&gGt0 zNHO^wim2rSItRfaR=vrU(Pe%{0fFX;>3L#LhJWQy#<{zDw9{T>nIY;4TY<}=q6PB-NS1o* z%w($JCnvjQ4hx%^Xtn**v+-R0Yp+EX=IY6XcUzFD#Y#lZ2EQ?St2zv@)D6!#p|i7~ zeOzh`p%S&oG78n!l(dK_KrIX(!5QwM^4w#4Fl1Y0{-tb~l>60{UOmsa z$fwS4chkK)rGvSI>uA`*FI`R-YE_MyGAze4t_lD9;;odr& zvnyyaUILio%W-V@n4`_(Xh`91P@xLCW3?``x7Eg0(?~+JAA>x4Xs1-~; zs>98`Wy+_DArL^izruYZJ?yiGnPjw+zkH6?chmq036&u=76YSu)={$QcN*Q>WNw@A z7)ao2NtJz$(b1fo(E2*wDkyZNt#X%UL!LgCZ>hE8vOv!qvowWeb=Qc?jJIy^?xF2A zRbEKmAr76OACC-cQl%XMS@%HGv>og%;Xyk5^L8{}^+T%|8bPNYj#eD>&H>vzx!g~S zRN}+T-wEenWk1tRCR0{$;SX~XSLF}Ai52m~b#*w?7TYfbI3*b~vsNzCR~9-~y5rq6 z6fo!jb=C2zx!bLk`Bm1AqY`@gI_G=D;21kh3)*N|taT4%m*-2#$Modst}CXY=eC!b zoBn8-V~ws6O?*c z{YKa$d6lr2IF67})Ui*LbzDO?&?FDkzzowjZruI(5-I&KYu1Mu}rswW;K%8|}V46V=G5d@g=m@%EzsH-bJ#j%23*!bwGy zC!BJ~x(wPF^1Pn&pF41zh!H~Gr+@xx`bg;Rg~qk_$w_!!aNwixVl~BjrDw;r*Qx9; z&y#$M!H($8mNVuF!`+{d;Yt2aVp<-|O1g4}H9>FSm4P=wYz$Pdzk~QH_4v|IU})>Q zm}8RSy2(5UG$ts5-tIl1B*Fu13Iw#CkmZ8xp#l{&1Sd>Fc}#8a&p}lGt@=O`2uG&@ z@L!yjjGSXZ4G#m4`v~une}+b1yft|H{PLDFA!&aRJ}sEUf$$-qFUeP6So=5D15iD# zk1!sXin5;hGw1U{Sw`oRV1D8L0f}#0z@3U(Dv1w;))2we2c9?xQUZQv!2IuVwlEew zOtdB8q!4gRn8U#E4{zB36xnM-P*DMdmAwE`h`Mu*y%)Jr3<-V^Xi)b`EFzcx(-v{< z4*MhB0GakS7~J6zOwl{Gk~n;HklX|`8+f+wI$>J;$0Ji#W(%%PC^&1J>@Nk*ZHp_5 z(@?`ZXg&Yquqp-VxtUxr3j_7zI@c)*-WUIPR$e$d^TF}9^>DoHCN#yuwFYg-76v3q z2~=8e-v5L{uSeaW|4xPigU_FU9z?a(fD18qp`q3c6$L!+D~3Z_@II4C@Rs}e590@4 z8t$$EMHtZ34jMU>AgPI&bcbZ1q|oQ*E_BEwC|kFIMndtWz`6~16244=tX#rhJur}J z23c*``HGlXHzA<~@mh?&^oEXaCTI$GU*(i+;CaE~^C?87Kv;pqb*qZAM zs?>b*fW%h{Om}-pymZIM&z?{~nv)+SLH=L@1SLp)6d*~)0$k%3bNEUIcD`qF zZC22Q_G7j*HUJ(!$XO=={{mA)dmvDOpjTw#Uqm4-P=m9xVt=jlMDsy+b&^Ef*dW>+ z@IruiL~N03J%gmF;vo~TCwr7WSv3_a}0TX9QR~$K8!$# z!IR6)fF7(j=)y4=q3Vr@g8ZuXR{i&TOsMJ*^%_yYMyNMZxHualJz~#$M37Gun5Jjdx_XNiUj|8o1};-{CyV-mF8)zqu!TTv?9PnQ|@Keba>UFO=s&erE= z3+JX8LfzHT()73_h&a;G_EKrm@1e#sL^S*OR9iK5o(muw&Q*^&onk_sjpNs+vYe^z zy`n4g&GgBf0nVgl`=2)hf2_DNewZpyZ5b&t{NC*wNHQH@A?Fdp%RkcedJrA!v-{5M zrw(3U8tT@>6}1O}QSNo*@_CZM8%ICPc`X=(_}I|14JPyhY_iZN)4hX)z9m}9dwjDI zozSow2^~&dz07b|NjeLo}=KiO<<-c!@;F3+c5`7rZcDuPGGE$IZM5fADJ zI}WVRwY&be22)+?(jrPF$&~CE&2JiRZ*|&XX-*&`t5DJ9ePRp2T5r$SPkVxqJcuO8 z^;nM~LjX07iAWRexs@EQs-#DrNbPhai$cKifc8cwq9E5uV&XlY)nUSBM53$V-XFga z==@R+j%z|tOO&SE{A?XP?ZjDUCgSM*(vG~8O|?Tvv2#756rLJmGT^ZngHA2 zY+hkCE~8~~ckY}ecC1g#%*0lU3}3ZcQc_S<>b47B_tw;i{frvB^!D4EDP?}CM-DLl zyK|4UkppRl<09wYf0^L*U zSG;TN@?|z%P50w7xHb%^pMe&G@_P}6BAiXFgSJN__b&@+dFO>ovRFCzkK#WGq@mNp zRFaPcm?nB6Y(mstSP`u{{xDGtFrY50LZ=#=vPR8VT_qedQW=TRwZk5e^*Pf1^_+Kr zD2EZh@$^zh3#OS9fhNetAIe0499X#SG)L@876av@TyI22M~7vjG}|Vl-Jm#b)Gx+u+;{`$sbdQp8ynwk z5#B`4gK^CX*_qtp5?E{(nlgc-TOW}FQ711_{f)VN=-mrsffa1#o3Bo`HdjnLaq;V# zT<7@6X?lHZ2gZKTx4zlN z^cj$RFY)?3^7=bSNyk@m*%jg6;pn0i{o?H%=sSjbO1&sfCHWIt`XXash5Lu*t2B1^ zTlE`%%&C4OU~3LF63_T$l@zGueFoTd-9q&J7(0#+>ksY_`RTu33m;TGrVb-$6VKTvTi?p8rXUXR6Myo(DzS4igD1tQc8^yjIVK9|1;xc1cPUsDl)IP*o{M;6LBv}mYX)!y70u647K{6Ux6f( zhV2KcoDj6PV_+}1m@E8$rtno0UsQ>`SfRA2 zJ6oDb2`jfg1*xXEX^bW70 zeqGPilDMV^^>xCrGA7%t(L%;4rY#(y*`~eBlow_%Gdt2r_bZxxJW}mDgo0~tQ(|kK z7dywOC+7qW#8dSjVc*JE>br+SrQE&I898!hqDs?LDBSBv%t4Uf=@G3pz(o}G<2ia- z4Vhs()(1xG{GM7x!=cik=a++s9FJ_;I|S^zSL!{MiRif`6c5Q&0DE6!;++~V_tSKR zyzsS!6MngdPqPkH7i&_Qr7CNT6YCGLgN^RofZt_q!$sIi%k+3$EXkJT+CH%a{I(MnX*>I@&FQ=~C`s5eZsFC(ewF3PxUU2V)C1h2jGBY5y?BL7Vp z^K{F{7t^(`v8j%OxX&A_Ydqznp9FnsI~*}Wx1^-!a>by`!1d05%fy3zc*B7~*)Wra zO}$sEW~tXL!Qm0-`|&)F?5{t#YkG=_*?7o)P?`Nwr)IsyaU?NV>Ey*vlYT^C&}T7% ztED>hX6ck@&x-sZl4*P~xBfXNfcrck8W4&K(KqfHiDju$jjSb;6Iu^rS}W@2!C;Z! zrHk+1e5gf1i8wZC&KBM&$lq)r9Xy~A7tb461IiCQ0ebVgKJZiO!u7z`?0cO_AmmWu zhhevIhVOdFg&S|5&v{hxcKujy>sUzfzx@nnw&saPY3Hw?I)S(8-}?0xyX?ZZ9MrrI zjLEePc?CSX-)@<#T+;PbSy%8pcp?#oY04SB+-Oz3JiPu%^{LXsPooy(&n^@woQAFv z7f{Y_-Y1jug31rUr@nY4XnzmHXjsi(h4OArRk-!{g3V26Q?P9OR_o|xtFz?ag@Mj| z5IJPF{}l0*9>Zg1aX_e`{zX3UuIPm89v44~IqT0E&wdgRrLys!l<#=e!s#FrIU;f- zePwLi_lBu@*(7=q_1^l8i^o51R)wl*P*5)3Dvx|TXNYNM zc&T!JX>sO6Q%d@S9Ub!bKmDtL&{lZb&I&~mOq!Da2K+?*!}@9OI3hgBe<%LP>}t+y z*exdCtHVTe#Bj^M@LRU%nvD6G$ADDUtjBy^8||Tupv!-b-e6#Z@;46D(6%Y=OE8jL z=L?=*cbPz)gx7CSn$YME$D9n1OafFY#Ps_4 z@bT^19*_q%(8u6|VMh$&Ki!n@d`$1#ND9~E9~1(Bq6SO@i?!NdtJo!aVewrs=*9fL zuP>pwNF^aw=BpE1@*lTQ@7G)|0As(s@#%IFkzWdM0^-ZFC3nK@L)1 z?-g_4mY^#aG&ZUQqc=$P1kth}fUws!!*od*id2n4+w*WD(@7q5(A)RNp@EHTAeyfT zK1n6S20<|}8$*z50&LX(OzRLrnU?mRg;HMJG0*6>f)5ow;0chBfd_cuIH>~v{#-Z; ziH-%HA+sO)!$X3cLEv=@H=2Fw?_Et~Y~jKnc}>1ZNSqj(&qHB%Uv@vbiib|bu0yqL z-mUVE!u38qAT}jKw4axRV>*x)q$YU-Y}vzI9NB7%U@DJ7vIAvttOcL@R#f&xkl(k0y~A`()f_rLd< z;l}5;zW=-KTHb(}GiUbM`+eT`iEKnwrU9XG3J3#*YN4D5z+~6*`u4pv$N5>zr;sYQ zt+4t!-yjYN7r@*I5CwdWSLoa=M1cn0<1V^+gZ{1)PrU}6K_D3 z?&!jj^_{^nt4VL(1Wm{A8v@GN20a;lDik|LD++Go8@Mr%`s0cKWnI4CE=2aoI)404 zi857qce6Ez8Ys)ID1jfFIZll&ge^ODnGi_!8jL{%cIe1fn3jZ=S;7^~fh-G}sv4oi zvFA6k<=b^N=adBrdd`hFd!*dp-)R@qktH2uck$j$a9DLJ%TiI1NeY$>t#vQRcPgzneYDqp=6VFP^BlUJEN)Sb!$dD(Tf@z2i)*6igC<$NXIWNv>UhHv3Sn#=5@ zKh32PYG6X-^x7jn*inj{AZMjItWLpukER}nw?rea!LvL59es8)wRqdz5Jf4j()+b} z_bbJp5r3x7Si~#$KaJN!jT6`uIA|=^eNNfzx$N3Ihx51OJ`M}8r*Nx=iO988IZ|F# zj+#44p6fM@JqWqD^irVM?(!zDurv?$*;I-^Z6C#A%7N5}Qg=gA4<3?rtTGiCvKxZuk|pZwq+%CXDCu<~9Uv6tUZHGM)pHinu}0>ath*5q*N>^WJl)xM>9=~JHt zSg&G*EiNQiK0-AgXooOHs&DPPVOL$)(zek2fHQQ1EkNdFiHeNm$a3;BJ9iq!Fw{+w_-K7xN zxYXBYk|(g#sc$ozc*4GTZLMJ5Cxp;ib*xj6$TUq|ZC5N_#O!&e+uOY0m)|2kfi?A$ z0c}$TeBY9*w_$pg^CA>!BS!V_kn{Sx;FF4Y#3md`%(Br!yq>wQ?yNEZ2LWIIK!>$? z*9Vnkx$CR_5`Q#u>qb94TYRl~HDI=<&}yymKG^3x`63@aB8Wn;+(J3GgqrRV|2F`T4@#d#V=hx=025ru)4?swXwx_`7vynB=Kngz?OsL}C^ zlB*@NN^}qXeQVZ_fD*ElT++ZJf9W7sEYV=MU#olO1dF_KkfwI`-04Kd=UOeLRC7lfsmSskvBS)k#}QA z=jaaPnrwO!>t>>uKZX!3cks_V`Bw3*PEMC_AzPXn`?2&l2l1AM_l!bS%nYtKjQ3O? z^IV*LygbD0XF_?dtFexj!G*~rZ)z}kp)t>#abjoiTxAJf9^ccC{p8p3ga|HpG zLe?MiIW94mxA|`Vt!@3^r8W(0AfByZ{|7Kr&q{BIt=v&?2`itd-mm`!G8Q%=c3wbW zXIsnOq#?nhjKQ8IkbwGdW^hVs1&xW(Kl=#D|D2zL5cREwXSW2kK2cgH<|jrV9(|;V z#E&94wHo;eT?X0wy+X2zlqqflFC_Dn3CTQdYwdS?TxL5cF?uw;#$5yf`I&zq_5Zw8 z-&gxvp2Ns{IjiD2dHQ5Pc+a&d#L)E}{YzDcd|f~8SO~uj_o6|=bphV6JU5*7)}87V zg3yqxg2@IuA=O=eh|$n1$}xG*%0O zKN{l3l?t9N49vWJ`DtL$Q0nL}EHI?auc(Wl=NBh0z7o82p`PBaB3a+dPred>iDP}E&k@aSg!+oQ%wm2icX{?5)0Rz;a+4NW zsAX=gPj8#*)eOGwvhtjJ4%cb6mDdXR`E;tz>D-ib>R*(MH<)P9YQvQcIO||zT>diK zc5IsI5}z#gp*`i61kH11`o6gmo!Ey`auh-RP0SDT6CNIMvX4j$+zwTfXj1FCIwpO} z(4a5pwQ1rSb<$(bS6lr#2Y;M^DM>jvj1o3mJ&WW*4+8tLnlChmQ?pq35uDr)=W{%z zu`D)XbuMZQ$aw3WwIvlbB~c%Qw-t6L^tKK5#0;EA{5>g@nNcskSf3rRy={G6WYQtk zkBLn`5#3QnJKX;*>;;^_4js)3h_R`O@ZntwCeHI#%f^ONJD2+4171YSQlmvBAR!T<87h?-$Hff=gNXz`~V z-zMJYWX^NMHG0Y_=M*?|hMbKWQ2E_QM0S+T_JA-S6%olL{|_hmC?&PM>!k z&5jV;b?#p}&+nO=(a3*vn`BOnbe4ii(V6?edJ+f;`+#ViMx4l;&Hhk|J)T7 zg}!<5O_SP@dmEm~w#kJnGQ$s@mOK|?zen9+Ts_YMY(5Scl~ecXKqQlA5U*1ZFH7{J zHz;d8Ff0ew9@hH3w8NE|4aFxvV!^*oHsJO8aXr-eq*y<2nW6v3sxH zfjOXbK(Z)c^7_*=4Uwh~ouR?22U*S;#1oPuw@oc8eX|vC4-~a8wzdNvvDo~q0ZOWN zHTQgL!24hQxQ#M%@q$WwnyKFdZ`iSR{lG1d2)pXy!aibV8oo^;c}Tb1A1z)XaDD;= z-_TTU8XV4XzTUfhx8HDnkQR46Ea&rU;zD~`IuEAloo=% zDq^Alrcl2DwH}S15YG6{&^@W#x!>|0P&Z%5odIle!Q2%lhydqUN)2#kQ36u8o{TS1 z%R6AGfv(tdQ>|ZQk_fFnIEvJ-65*o*&;WpIr0($6Lz(Y$OSp6o0W&<}JxRHR~0C2jc zjUWP?fH3gzA->71fJ-kC1JEl2N5?-!WGs~shFK2GZ-LzjXfr_9VEnJVw5^fJkHac2 zK!fHtfSUlQ5x_TA`w4}uTDJ&;X^E(V7P7#Ofh;--2AEFF{gQu#?tg#~7$s%Qus#!^ zEZhzT7O_P@qi{_w1!xvziCQYdzDrULLdF1_vbt{FtTd87GylOPQ4uF^mae)#89rSg zwx)11cV2UTprjHyz9`Os3ikHj5hIX$59M0eXodvBJb(+!u?DPE4$!gy0khIDYQ^oy zng}33P+2DeElMRN5OPgdO2HvA#aa8LEV+#!^n*#7i*Ac}+a|Lj_&x*9=+U<}HU`iu zOxr%>H5ul_{9{jnM$=}Jj;X8Y77}FynEM}(mXApvii)~IpmSh5)H*~BX&=qtkwkYK zW>l0N%9e>q+SYEt77|>hOyHuUBVU@Ns?fTgx3)>Y%ArxOS4T* z$s@Ne+pOGrr1_b9Lo_ueh*1rwglj|1xA9y84{nEkp_K>2c`dlQS9fAvgs8NK2AvdD zZ_8^E>G0f;PvWMc+!E2dgqr%fsnA*%V48SFRY!;_W?(?OxqY=T1N5-*v78?@3E73| zO-<4~D68pmSYa_z8vUbr`O>*X@#pOO%h=t@pG|_!-EN2$u-U4{=~$o>bS9NCWm&8_Dqr~zZo*Qkb(V5`U{tij zy`-1oY~)S`xZ?rrGlBo8F@3 zXxt8M6XCZOL6kLaMUU*1YOM1fn%OjGe0n*iz!Y#UvHXnKYn{%hf!0ss`ulo!*J$%Z zM7C3eBC0OXHmog|y$NGd^#2X8?v^s+JWF7_ex})Hy};Dnz3?YqYk${Z_D?Ov~k+Jl92~)H1PIt zIKRMn?vGHSIMKqWpOd)hCqi0o9LmPw(!N8kuvf8yX@V4LVTyuW(u3kVhr&eX#g0-I ztXBuNiTm%Qsn_7Ey$oKPPD(SE*B`k(^?c#W!F4dv{n4|T6WM_qZN7MVdW@0wN203z z#iKNViEjNj%(i{X8a%IVuav|Om=S9@2?vNq*>sQJRfr?7c>Ci?QjhA-BlMH*ougV?3WA)zZD9#5|fRE@1T$Iy_gU%R$5US)%Cm)r4P;prdu(enFu z>FLwOMN-ZZ9QlFQihH{8M|C=eZ;OKH8Lg^4>r3glrAI%b>8$npbcpmn`~;F^T|4Tx ztfNIP>Ub-^=TYyvD&KvTOsWV>dU`bQ!|e`xujTi;amwl6JNr0qp1uDIE81-l{nC+p z&$jH2GM+^wSnv1WU--gZK5>}76>|EsNYPz|KQ!fi%dEGL^2{%#&?lM=PYwSj!&@2J zjx0Q~tSU1vtz~O+%Fe8d#OfmECw}ouzl(&3*tjca;)m+;=63J`?G1q92*1 z-b<2tC~9U+%lO7xpzY|$<#ylHlQTba3uFq|km znpg-g{T<~*vbEE!oX`H7a?(#hCpq4m5U9AF?lMRi()@54WM6cMRfieSh|PSbdhGH* zpj45}R+bk#_p;%k;>1HI{R4}oSI?h!Pi@g~>X|*pf0s=rhl0O*t7uN!b%O`@7}UNKe6@DA>`=Ks@@=`n{UYJ#6PGi+ zPn$d&nZuGA6#bQ9qHpk7?5G0y$tMkktFJPG3!@%?-m%E1KDe+;KH@*oGG^N@M?q+9I2L;Qgee(_ zWO|<+d4^KV1jNyly;^%@&vTlHi{K-naR9@XW~NXDb+hx}XOk9^)3Zk#?E3CsaMDFS z-pR6isnC3mq20VQnt-!^&|F-)zq+ic=EwBPFAm!-=*CYR7EtTQ#H=@%PX^vhr zrL}7AJ=t8Gp&8|CiRu%u}&2ZVn)OYqo?9!f_`7K;zIMM(33XxKqX{&2ZIdQ6wG1m3>BXhf< z>!yKkl1dM==;ot`Yy;W|67O>46}J*L3gBLA7vWYCr~fg&t>0GmojH-dUy)q0d@E)t zPLyRiq?+_Z3$`jt2-X8K!^_SmXY(!lYZs#!)f2+$TJvfKKUEuHD<>FgWgf`2<89fB zZd+j|jS{NVvlG8)uaXfU&jzceC9t)lb>pd~yKJVh4L?U@=h^!*q>B$(xll1q1|9b} zHDrT$6rJTJ68(4q|1-J?2K3TimsC|{d2AHjKaun;?3bCeg^)`=j%1cYk!_mF1@GMmq2>ns zryz3XcF#Gfg_;dV0OXy5SYk6x57sU&&=-e2=vG?er*A=-Ri`K>yUVq3WM{;ckT|-% zzFb5cc7H6O*6`!!VVp)W>r4Bz#nS1Yw7v?Td-p(mwOv=LEcXtr zFTWsFnR4ok?0jX}z57zVxF!>ihHo@GUA@;m8Wr_AI5nQxc;A&dutCfGXN!Rz${;3E zAf){!i<#V2fmO-(bM}|Rtds4i%R93-G~p7GJsfRzAn=><8e<*gt}Qhq5VLY zi-LwUvWRB9r?Yx{l+(sgU$mQI)O4oa_=!qa?UdAxJA0ys`-^F!;!3YKm2Gz|XAk=r z?{CR_q+>R%m(# zQA=*J_cyL`r6O41;A{BSplU|kZ)ROndKS$S1CsZDIz5n@;o@X+4_guo8xEV(VPPUr z#l&QMa44H_0Sz1se*p~9VlvLG&-%X5G5OcA_j(=v+bIfRn1dPu7ws3(5Y=dwK1jp?0%1%aOvP0oAsUUx})6B zcdZU_Zx^hy;NE=9cu?9E_TABPp9cR<{>L=(Mv|Cc%b%TU)`|WoF4R<&r+7LM?c5bm zDfSjR5SVhP%A=^TXTD8KWtnEUI=|H@O}6ZG&{vstG#+Aa<|FkMSR}A1%?yp=U9>Wz zEOWdcI4Az`;YURBJTrvCK;*y^;vR!#ewD3NWca6HH=J~}#51Fzh@7?XX zc8B^d8k!+%iyiLdTEMbp696NDOMe~~sb5QUufn~#Ve3INaD-|a68Q^jFzM&4Zb*P> z1;uF-dKqDz{0OZ?c+%YGf58Xz>AHcz4BtvP2odZ*OOPq+5@qbAo{ zA{uT7;>w@iazic)1!=Bfwx9aU=}Sbm<qChgA3?LillY|Ld8leLW2{N!9Xz4^NeBt6*QBOCNK@h}P(^vwO!a1j%< zNu~F~4+b3lQSdycq8%ile8lnTNwnp~PJ9SzzW?7E7sMX~a&YFpDiVo^u1};Q9toRJ z;F1@zy7FaciwRA?(bqpwpBGNWf~X|`Jpj7cf|tG` zF+?q0h6_@Wmd`-?7}$Ozjx{C8fR}uR5V*egeVA8Oawx0r@|rish#~G~Q>VPE-We|z zCLjVzhC9j{!2ZJ%WU{#xWmkYU_pRd;U%$7&sg z6l*6NwtzJ^G&EGe86JB&iF}}8|F>e0J>$OBz#8It4-J?z_~D0zSy&N>Fa%Po!8?7u z5{-OXMbPVl&L+@d1Mz-oJAYCi76J-`eqVs?NA*|?n)OvZ9EWyA*1~YOyg_anw6=Y# zh5`f_D}7mi)O&PU1QN}VHBJlW254ePK3Fi&&$yWhF&>E{jY45N`y_5UI=*@zPMM81 ze*%mxVx;kgP;V5`jbG&K=lh7J#sC@apRcAgBETJ8eM?r>HDw>seY`lRnBspa>3o5@W61|Y${~sChdCznv5zvFiZX|D z#^qZd32X!r-|mokX!VjNf$&ZT;r0lfPL6ovwpO??50Q1=gBeN1uJ?C324lW0irDEt zO%oMlmSQ^>O&%Q;nW?O*Eblk7;+QV)vvkv+D(pn3m#A>p#ToxW_p6IWle@7d1YeFDgU1e=c&^j_B>-pKa7@J7D`vSeFr-96YCzq6?%U}eF zvx4rjic-}VmglKL)N7vdQm7uXoMLC$%Fwt+;TiLaC%oUZ@TmtL`Y~PX__ly|5_Q|M z^a1C>$>D}PwfGnHR5Aoi?Go%Z@@Jg&RD#aW3Vd#l%Xo2@Kj_U9Vzaj|idJ71UNKlL zET+Y$-zL}j_>RXg;iKKf%U7T3k_c}TCHZ~Y?p)P5r#wt~>rud`Sg+AEwg3^EhUmhO zh0b%f%}xt1UZY-nZ8u;MZwc@G(Rw^<9^<*m$(~o_@>*k2mgaLpk+|vA25O4XPqG~R z7i1j>TR+Ou@kmJ%g+)~QZks5?8o3-N1F9zT!hl$?iO$ zW$1qRf!lG!4CavvziX*BzU#keyOe^v&;go}g-d!D-m**;sP6CHxb_#;*dQOfdF0dE z;^K$jy^hFlzW|9)LdZB_B@j$_j>o5nFz*a>_r@g3gY&KOc)Wxk>Y6q5A*%$%VS!{3 za-gF(-HMTd_qUoYqh=Q6!W&L z{T3JH!9k6#Y}5ATixOt~x+dHfL=VhG2qvjeM8UH}&63`AHId?`AFUsWatW6#^hDK? z?Hl6zs4^1C`?kr;ClZdt^d(BFMgEjLlB2yWvwrp^`LCU~XHQBfB(r6wlsQ=%X($G5JPoMK$WjnJP)-+LSRBao@=o-i^3v3 zTTE9sL+t6f`!jAg2k)2f@RK%4a!dxZSTvI=TNltOdC@tM)zNqUA!4}pA@7ymByA=-N7<`Z;Fj;!!ovD*Nb(0}JcDA6 z-5{#8u=@^&vewU@JG{z#9B|F~5b{o`-74*{*w=aFc z6%x;;u0Dy^E8TtOJbvV?1jWx~%i%9p4r9ak_L)01C)#T;Z8IrR zH0wI8LHw>+mMtnqY-&t@wyI6Kub(){&gin=%8!f6wN)2Zm#RH@87}OhnDrF(1xNO$ zmZWmREt$F;%CK#iLr}~zofe*U{dgB=zRjDEW#H*{gE1t_>#Ek=pNdYu?fr-LFWH_E z`@EP<(oI^QpD1&<_sJ9=Kx#Ua80EF`t~OfnT;4`U&L+oP(}^juzSr{q>%Hd&t0y2Q zC%>k2IrD06@ASG}ahnzeGJKALBi#2<$%vAsUL8#Fz?l7Sbt-1ohe%;*W?12)DKP)n*Rj zfh<%oq7^VHvMq7beMe+6PJHw^fvxA8P3q!9V=f(dvC*HZ#!tZ}Pz1L^%&jD*+2*7j zp=Ls}_o=FPbjjmCD=N%Z)(<5dF3M>E@1;iZLjvjZIK^dNf0(a5?Fb8WB(YAEJX2dh z_x=t;$ej$F$`;h1WVy#!@0=75qsz9HDy+?8G|H^67BNYqDGbN^Btk_8 zLvbXu_*6f}(UTP&ZUrnLKo@TnIgf1p&;eQ3KpizUrF*e}NAUroMWtkB5 zItlqkVMd9;n0rZ+M9gY^tu8zqNbU=;o80}b-v25W#K``{2y$@IVD}QBQ6VmgXxIfC zi9XEhMEC**ydr|r zJBX3mT^f_l@*iRmTF^8IkZ2fpG>q=e=VW24fC`ISfW_YeI0mJgE|8*gJD39DmRtp# z3Lw%4wwM3Y%t9OmP0h`|hm~k`kkC7hA=zeVI|Y#38#d_icE@Ei2P^`2?>VNgZvG6{ zGHIZpo?$Otwd@z$r_?Mm0zQ+dfUZ~GAhprfzVRy0T}oXDuc1vOr7lSA_K)JplJ1te z+t?_t51brvbD&iuOkKt=fX@dEAzz)`ax8{qFTgBAYkdH5h66-o(|hO`O8ppg*=Ll5 zHVj#lqL1hMr4X^t*m$p~G*vnl{6KaWjPwD5C5#Y+y|?a4WU6NDM4E(dKwxOs zGu)FSs@{edhuCd8#Rv1tQu}Cie8qD*Z$2Q!5{S&fe~emUMc*j1U&_WEFW+BYC^8{* z;+eZOTls_Zi-cgePvOa>nw`pu@3q3(Z{otd1ckenPk-uuP-Hsp+xF%U*~`*Zhgz#( z&JIOq?@i15am^wgmPwWuQsN~?nH@w}!*FxiT*kdB$?=KE)I`%dyV~8Y*_ZG}*s~!H3rExDG_xB1R&{0bB|u(@y`oTjE7SdK z!#A5s8LS=O8}dydXCy1k(l}1DiAYxHIbyq;$2HsB8ht6*VaVB@a$m<`Vnc+|q;;@B zP;$TGUD0W}z*|ub-zwxzZZL`B^yV%e^pIwW&@1%LdGaS`yrBwzuxJ#c!pZtoAQVgX zRquV1v!wJhgmSkE1xig-4{*hzmRe$?mLmu<#9q|6W!!0R$u4;M{HGochg!v8 z5w~{rn6l+seH)oDKm>Le3ku@P_qiR-mAt>?Q`ZwsCOJS-uy`t(_J!7f)|~r1cj;Y% z!i>k_(Rdc64;gM;r?{s!e6;Rg%>RNQU_g#euGS@zJ4t<>{%!y}3Kq%Z#3L(Gn&j zu58JIRD||+B&w=@gH`n*BpLahDQlyX`h9|hrct`$Z-yjp0SI*Eyifm<&2$QTdG0ko z^>~NQ(^2(lr<$xEkhE|XZi-Ff*t%F_oj&~zx2|4Iu54ndsgwLUOLu0IV+98b@H4er z_|4T7@(PYJj*VtG(OoOr{yq?Pk8!;Lg8*2>LDLlI(gI2V*jWq#J(x~WIvFLvkwkL* zNC7T^awR3STnPqZ0Zf9i>&efD7QpKmOJx^Ggv9>@D5hZG0Z*lW+w?3hJn-w}V_)$v zCgxtM%v>54tr0ngs-x(y$rtFo|BU(m>0w^+36E}6W0Ogq;vb!BnQss?T{g73NoOD@X6mMX`zooch zyU5`5IF^Ls=??5DMHcXP3QO`rN_GL@r~PEjk{O~G|586GI&9znruigKzRjd9!iGb8 z^r94C3(V238FzP zW5-AK>f>SAI93l2B{W%ERa_srcZ<&E1h#OW=zTu7ilq=w-D=-biH|433w(I^_{hX| zLITK2#=>o%Mx9u_czCtl6i!QLn19_0bVZ*)dP zEr^^Zw)dA$`=~8dX1Q8#^rx0PX$=esxj)vQx5XJIvE9WjovJ^j8bS{l0;pR zYM<`9IGMRN-|NTg1&y2U1RxP$_bfx}^{*ez5_}EHWA2X$zdW-c@{EHVTxgR84kV?_&p-p~##Xpm1X?QZvtFSP zvmMf0WB@P|)_j4^98n%C{MUp!A1YI`;R}!=U1vq;Wo?C20|*}(kv@bFHCWC#PS=2o z20-RHwFtcEuPQnXnA*@D+Iz=>*&iztrrX=K8$*g*E!4cCU9$xl#d=&2+iXP`MTpix zfHpBg>U<4npo5uy%}h|@2!s<%w4{D0ML5oXcWf7hz6anJHPH(axnM_Uw9f*DnXE}G zZ8vGV$@r|})=&-ZHrqpnKEcz!LgjpakQZ|BPqdZ&g@tmucc?|;ty?4`b$&nFN6Nq9aNx4T+BKE1W~GfV5+f$lbI&glLK>D@y; zTiQLoxMsE53F@aJ1{9_5n78fAr6*(#e_&yqxQ~V9d?Ep7ld8?BkRNxRsS#`|*Y@4I z2Krxry-vFAtiR5X=iLMK9g;g<|7}Et zu(913IRoTUQBUNztk+jn!7SY81oemp`qF#=A^6sRi%mZL|5(dUlY?_aCNZ#6*~QX6 z@T-=d1B+R|AON8Ng;Q&QvJwfDRfLTDouV9I?Ux6{?t_>|6c|_8H3M-toa!L{Wyq`$ zm{Fk)!xkX59ZWXtX`=%n{kzX~!_P5A93X(hHaA8N;0HaU}$#M z#nmI4`~+rngqGQcQ{9YV!hZlU7T`%^DtjMtz#ETB1tYkmex#z7bS%C(Z)8vJ1>z-% zT6-~8ba0442}^d^gQbFbm&uOBK#mK6NIu}mfICguf?{|AsQSrGnruoP+DG`XoWU~7?R6$ z$FP?E8W@@r8%v0SslCaLKXN%&XP`G41Y;0T9J_e|?f@W+OGC{8u_fi0^pKH6K#xV_!mYF@g^&PuOD!%=c!+aOYtCKkXj9t z1{1u`_J||}_5PQrI z6#uWC0{WvU`B;(qWr8*YL^r^a2IeaqKP;TcJX#&mRlo;W1Ac9(*+^vrJ5|8^9x|!Q zi!ojbabO8@0Ne@e_`;geqfS*sFcR7pCOCj4ihaSQ|y@E+- z2Kq`~EvWhP(~@klm)x)WMpDC;T``%xf%9lH$ljS>;#W|Pf3_S1}4HhdHZgBSzA2bE~R zcT8^zYy9LhvvTzy?^dD<*@&D89t$sZl92u;PV0|3Iw7Opr(I#a@~VMfo<6bF9wNIv z5KrwxG#|%ii8Zb;P@GLyvBMZ)>zBwjpnP8~L_D09SA?P2iut4eR%H4+Y$KJa_E(~56h%Ujx(K1w;ua}+M0c1xX!9V@%4h|JS*?Dp_{lU;;9~B1KjAjpcg&jHT=}4Izsmu3e%2hLOyw`t5i<|vD z{W_h3`1-@Q)Y1V1*S(1 zo=giD=tHgkp9P>h;i2ZjwNlMz_7aN;P015#6&8K`-|YjQnN!L*vevrkF-cY-eTbJ#)bLBV$Y4YW{l#7Mt8%pBFH0E8E{3pfUhv24T+ z*w*v%Nn071HVZow$8qxqB=o}IWza}s&w$*5g0DcQg$Ud*n=8^v+&N}C!Ubvi6nL!w z#>PJx7@#7-w6~C7LIpyB5Qt-eAsBQ&8QOWFbymd!Fn9&Nx_HA8(w1NQ&mhE;eG1H( zN<-TZYS>p5qfMG$syD~8Uw6bjyQ;6xy8RBD#(DYjnCh-?p4CCJLf^91Cf3g4L7wMD z$=%U;+u{T>gP%MTU%d)e{OHmSweHR)naq|kKZ}{PZy!_t%0l3BrA^DLDFvrZ7H^#q zyVx~)?DmVVy90tQ-tKR$We!{rWSgI1I_w2x!G(Y|V<+Ozg1ML3(2n@bqExDuhh~77 zAZZW-k9AYBpx!c8$k+aY*IL0-E@~QPohxBIzgf-*sH2@pt6hojV&?Ci~{zi`Ucy&e^F&m)Jde zsqNW%&lq69iIz)q!gLh?H zMj@6Z|7Hj&`O1ZheLc8R1Ps6d1yKWGas8Xz2nrWy#=;{7)u7y`ykG99Umw>scQj>pcUxqZgONlPCpOo8IzS|3v z5>>_Q4gM#=-k^a9%ikK&0Y8~4Kpn=QbaDf3HwGnSHW95FIHHC28Dh=Pi3E+;BuK&9 zr#8eyN&~(SbTck5#$N%k-yfy8IPd^CT!XvCpyz-VcqN;8%(P3Mc7J;MCEoS<@XBN= zs(+`SqggR9{Tvh~8bF2%i;32DK!y7^DVXo0wl?tKA*N@%QdIzHaqXZlog=iZXgPEA zG#m32WC+6=XYMOIqDajEIzIpgfMpJFfQCf3Ak2bz3$>|^03Z{g2L2v_X;B(*yE2?n zfg)`EB+wNmck=B;r|KX^y#sw9QwMA$+FIz9*r7}m)|d>oBy?mJJ|8qq>h-%+F`@Uh zYpC@>4&vSiLv?@~RxiLxNzmCDKa7Tkw*04K3I{$XvPM7zQ;!yAK8`jBR?z{wBs8f7 zOt77`wF50k2LvaoCbZ*eYsgVV7`IPDsu-^lFXBjN-?gY=Wa59oATJF#cA9!J8Q{j` zr-zuaW0+6-E;y9`$Yk9Z&%jc*HH5SAshov8o#JR8{JFMtmJ1GMe9&>{2ygGRjz7-B9m=WoS0Tj66ul7NI3 zFzLWJ^8%#MK^h_4%K_m-2@%O2TP}eW2?eca$7)Um;Ma^MX2k(AFxa9y*puX230J2kl=)0aNW;D}ia)zjWD`WZg}lpKd0VYdhuGy0~jAxfI~+?RoP{Ghb!E z5h_h=YNmXIOtgTGhpg)JlsfUWK#Pg&4Y{$9mcxS&(IGjiZ&P^aEq|?d|K@wQ&*QSS z_SFoPGoQAX7d9L@SAU0%(Lzv0{@s>*spKE6ynttk(ODf&*+2Lo=(E(n1OS}}P%R8b zhX?P3?4TqVOb?wgke59Yj7>BNQnT;03RXTRs>@mUGsuhMr0rzN-|H3s7goian#S=% zW30+g0*6cy`vzS4eID*SMNXdL>(vHZGo1Fk2}Jc~{OLrm-v>=eGhF9=;4~P0pDABl#5FOQ!%RzpwGT{bN|FJ)XZY5a4N7Pax$gd) z|E*hW*ZfD3VtMkH$s5q%2@@{5f%(^TfUQ>ptfkEBo(MG5`_3Af!mgMe9Ba;S7s9&( zP$=SmjoyafIROSp$Lzc)$V6#vf#f_3khF0 zWm14yI6BL2383ZUaU3Y}v|zg!U>*1o6|up=0}}1t%Kz(W1A8uaK3IaZ5x|$u43JjC z)(er*P^=8tjL~)4Pz)I8=3gF(OF19EIIj=hw5IAx7@)$pISwNIo}b5;U;P6*%9TNX zbR|;wT;+fh3caBmyS||}6?ob3@>$WQ0hti!|I6??Cj(qNSVlge(!+@V;paqLloo}& zF`@ttK4`Bu1fY{NdIXK3v+ybf!)!r9%aiW^u2>{&mC!9DSHDO(4wz~(UeQMonV z(C_I?VcSV18Be(M&jM4AE(;G3iQcjYHW+_71P0WD7pWX8| z!8ZrCPMwNQft%c>cjrBE?sRpV!lh~P2Bm4^+rJcqlQBr0$myLr5jMQc?SSR3Y;EdIKPN?VuZ^EYB*(v#*e=zw z=9HRm(Lsg#Ygg{mR9q%0n+opr8csKNJYCZsdEnR3x_S%8ZhxG(hn`k?Se4!deNW`r zkAm#o)@F190y+jT2AbX}1Lg~OH1L$sy%>xwJ#y-RV^tp+O#U(IfZb!nF_9Aw;y!JS zC?uW|onKgh*=C?8#+BfI59L8e5_2;(L3APbod`b*(5Z$+xThP^0Mp!<7adcDC+m8$ z9XJ7yv->MMl8p&Qvbb*_-STh!oouhoY}fZ{%l)UvtzHzn;`K5*fYG5Q3-+6$xD~#A zMH=6<15{7&XD04(Xg-8J#sZ}G6NBvL6ftTh#90aZj3q&ewA!%L^rv3hV^ip>a7-v* zu|h9$V6OoD^FH8QQv&c|8apq9p{*A@rB-+rl&8@tr@!iL62@FKhQh$r$$;!id?;}JtQG0jsnQ^|KF*> znSZh?H9TEWiF*UNEYW1VtoSQgZ)7FNd_E!fMAF&pfnxIMx5DwZDz%ONEMY}iS} zmyn)169P;*LMIK(w3OXy92|O`H-BuX^!wkwt2g)r*J3{B0Ubjk@q7-p-qD2}e}A5f zW37(w^)|nNqXOMr;^aUiO8WXjERl{6!CMtEgCh&LvD{G@ggKE{?droMZ~Z?*i%fap zFQetJvYNm+@(iaM&55R-w)70d_yjFk@a@IuBD4m2z3r2kU_UM5CMpU*)udw>f^MF_ zA5^G*li2}NP>_xw?31Dy9~S`e3*9|O8iG8qBNzx?2GqDFnLsIk)p>>)Es}zx zkq$tn&D_G(b)efoix*>{Sr1&Z6+qwd6z}cS*fy`Vq9^pu=zZA~iu(D(vx(^Cy>RZS2Qu9AkBVRr4J= z8BX^{Jo7707`P@~J1%QP_pYwgK1sQbi%NCf>h#B@YLd$}-|PLGGw}2Q2MO%3cbwJ( zt^o4O#(cxS)IbRB2j@$|KUB+Be$j%@sW0)2v{jj&JX#Q4RgC!4aPxQVTI{^hP3xz% z&kBc%S6*zmuX=_zYEvGMC36k9)qXQQ5p38oJ`1#r=j$>NhmO%)QBn z^9<0z1u?X-#jL;y1=Lw!n!pcM8u2V&T&=XaL<~Ibj2-gmM8y`vAov zxL^bj{bNuxleY@>S(tN-3Zbz(&SAzHITNV({QSYOfe9XFiSo=tmw zx8d&$-X*z#oAlzO-7oWtpI4Q0vS+dGoi?O6eP5ZW;zX~YK}tMJ%7c=G?BSe;Iq4np zHgfJD!QjJn6656-FXf17*Kyo&!F!$*NtcaOB*z|+x}s-SUK)7&l(N6Z= zSw&n~_uP+1@71yrLMQBcbz){`r^avBRTb|ZIDdWDrQtE!YnXV5N51_RR<~1e`UkT} zPp2n28(lQZz!X{mP6=y4<&FrMAtc8TwmhuRltMULYw7BLMH2CAiAzP%Tw<3o`J!{2 zfwy%35;@Mc55CKv4nq$un^PwxMWdt$woQm$&=qa=WwvQ@&8^17tNs2>`HsF?1l!C@ zOX!6GDJ`+Nm~Wb(v?%jBD+pEAlk_|5aF9N^i?b~9XeI1@n60Rbw@7aq#e*fEC#|2S zc1P_eQsTEQzLc1lM5oSu{0ocknVsnc`R=~&8+mC1o#RiRRcYtuKcZl%nzu1ds z!Db^s-zDK*Y$?&PuL}=adTS#`VZ+FH)=;NhMnSUEE#B*a3YLdzPrcgA8<6=@i}kI} zWzi(-)#bZg*5mT(!Szi~96O)Ilb;?xLm*#s`7* zpx=Yf0nsp8I(%Fjw6sUJU5NLK55=*pun-16I8lJHnp@oGd{83lR^r zHG`;hyhaKWQe3?;61Dj@%kYobpg#auR`7f{J=zG496+>LMWkfF@(>d1L|}ss!6vA{ ze+5<-Sp9!>y>(nvTl+pd3W|!73Q8+rQ6ilqsH9S&AcB+<(h|~xG!g>{g0wV(bSoig zkOR^UlG0tnyY`;JbI$X9e}4&nn3+90*1GTOz5;rVlGos2J}dYTK6r1>;D#||0)TSi z`E#nBuNbgsq)V5Q=hf??|D`3|9Qva%6S7l?I&;+Lc6KbB@PlKwuGG=OQE0f4(x z%-oZ+Lj7a#Hr7yg4Z81?78 z+S*(PI6Nqzlds+A`EyN<81!SEPF*wra{*l_ufIR%BmX=8j{@dOzlM9>3Or97YO{S+ zZS(TQ3*QCE`fkUZ>FZVyj2G!YYsTr+>_(pqTCn4x3U0?*lFL$qP7L#ApAK|iumWtN zGc2LE5JzR1&ahg!QQSg=-gmq3{mLzZ$Jc*8Gt?xAzQQ|VL-n(sY2o5Kwi1?W`8Q9S zus#RQRCfbZ2$Dn%%WTxNCO2C+X?PHbT)NO71xJ~jH=IO11eDTLtomNTl8+RXS<}bd z;qBJxoM9V7RQ%#>70v4OBwLgvR>uFZ4)RAsYYnQxONFG3vn3=FT zgfy*~8|50=*1l$$VSX51%<5a|)#hE-E1KXSIz`nV_2gsq(B4@2BbG09dHEI{EqSvU#hWsCPja-HX)#n+>o#OU0E`j3=@M$$T|#q zgCR25%&!6d$FDI@yhq|OLY9vZpcoCt1U8FXos_Rh0L1?3D&&6%as$tHfpryc_K$&( z8X1_Z@Xau)ZI~h0@yhGB^9-lbw&<>`Ve`+)| zPvL{cWQh>)eO39f7_&r+sI>Buy2^$9Rt2l7@%B%5@g@g(#Yw}!X7OKoaGcB<=xkdU zPy`LX|9`u=hvI|fd_nt3f!)CHFf_4tQ6VXF1r!T_zjWvpssRf@{F8CiM~LHL0V*bc zh}nRj7O}Dd?leT^z>~VcTG|HcudQo44dfX1JSZkI zIX9JP+j@fbxplB4=0$Z7(UF~m(?a`3Pdlyjn&}mcTzN9p>*$~8{Y0V!pe<0Z0oci$ z8mRk%|Ie5RC#`0CNix|i*{38yTZ&u}kCDBOvbCI&Q)r&pNw4WI| zM#7nSKz&bg=Gv*;7O$7arI7Wr0a zjgL7IlyO@pP-9u5$SM7;TXxfHLqEN6Dvn3q5RvakVb4sb4R|I_UJnB8FlSN2oi{PW z6i9?*_r5Z(SHSZX4AB&>zPFx0&d7&|f4#k5_%l);;dJFm(mt3uPtf~oqSppjTNbZh zSH%6x)?s0`!0*vWFaPh1PBvbQpT$DVVw(!d_{OS@88XDhr1ve{X!3@>( zq_`V2>0h>z?;t<`^38;5CGg})Qi5bAFBSldLVgPn1qH`Y20fr_(Bj(_*l-lGF*Rxn z^MZup+HT-J2O2NXi}^t(2LgmdwS*uv2N+Yz_m`B^qQmdXzC1hY`S`@Vljiubx)W_V z3r`1+RU00eztA@tFTfjTe9>xJiO~H-n^bTF`@JXusSO{#YIhRsc$=+<9Go3NF+BHswL=hL924L?d-winub91QN5G#k!)JOVzi)Gqh8mIsq@?f=f0Z+ z!15nwd=6z%~$cCim2T=acK3*J8l6#nW&e-eC*PZE7;Q@nq(XZx>~| zwcdQS*5D)6ZjRfns^LTu+=@?h?pli7IYt#^?cpdxpZcATDEf5%8LNg(^km`ZuP%Fn zre0@)dkEAUybI6VA||PPn>D4)_9VTcou2jB@7&%fA>)!Tht`Sj4C@{|UaO_SqYIKd zzq$%IIU|FnXd|TF$j-1XunMQtXPG=n5bWufdTmCKv|^-zrKNoKe$jsFe6=N!Vu@AT zxXUy3kT1jPpk2#r0XJGo9Ki02^9I0v0|s_buMa9v*cu5CmsA;`saWG1cw_|!l;r=1 zX92HTVD$tu#(Da0;5mUtOt7OYO#8wi8lWgX#R*FB!zk7(pAHAcfxuQ`t3w5%Jc8Rs z75eiL+`#IP(WMMAx@3XEhe8Ib;&Dv0|Lb6t2=9?=hg03NPy(wtIR3L_VG!W$sre-E zN6lFcTn-W-ZYiB<*q8nUc5SGQl~n+9wGc_{cbRR3X%?B-RVTV#!AP5g{V1i5dflF})P61p`pC=3E7mx`sKz(8AQ0``u zMlL|IT=dIyai%DK&p^{5i|Mv?gq~ht01_EG+ALJ!*=EiOVREOkX=3;ouzZV;!ASG3 zSwtuJ1T;)hOM%e558wfWe3xMZB`7?mzT2YxH!Z6|xcjlVX)}LXerp=G+rzHlLG##o z#QQ>vABu4OSy2s~#1X^@9>j6C;|Ww21p4x`EaEePfk~DQ4q{J7I1IVJjncE<+EuGm zE47KNB|sIv+@9Sx>x@+DMeuc4ZP&;A`Qvq_ES9`>m|~2OKmMhRb?o~u-L`u#K1n@` zkcxYc5<&7xIbDeByO>){E0NpO^*WMmt7ySit688>$o{n1+VSA&_vO+;3nJf%r_^n2 zg9N*>tTf&yXN0>;^9#o4pEhQ4(jp8dEfyWQKj=iDtHYj?8+F}2<6^}3JWSJ9<_YWD zvGe=6Ml1{_8tm-sC+WtMGr7<-Op}$oY9)$%%M7Dd2|b}a>`ivW`lW@-quE%ZObYGk8Yp=Q|Qec-Qwt-wE5Vedp}p_*U6t&85eh zXY!cKE;bY0jyh9*i+_HW!K?Y3-l>4-{+6KaNG7)}?g2fm#F{4G1sfTIp}-goPN60{ zuUqx16{jt$e}A4t z)Q*3LUGtJbZ;P+vLP4b1z3lKdu>~j5_f7HR%~-;qVG4 z&&#q8=H9EOVdy3zlcA!UeCJQA=`O)?#GH}pM=_nTahJb?&NR7io7QO}Nh_KG^`{sV zO@kW`SE#m&fAvQ3k=)Wa?;~ADUh`PFdH_^}#H+hW0HhSZ6FDDnvsjuJ!Ekfj{ZCz9REXvfC#KdCiZ2L0z37oF5hnb6lqJz<+Wk*dtPOfM8M} z9bAR=%gh=SJ*Ez67Up@4Z}gtjqXkXpJMEVr;GA{Td;b;93z5+Pj&}(AgMADFoWM9` zZF3nmpSO8sHIhGSRcIs!kxiE@Vpu~!Z478ku%Qk#x;gb0exxAcWj!8?iu}utmN>-% zD&VlJi@Gr500R5*qoprOxM^g@7raxo99S{)et{U^H}DkYug!~}Iq2{Qps_>xsHHUU zHil5-cfdwjjzYLCi@?e?jP*vpfI{HjE3^Bsf5XL^x}dt^-3Y2!B59vYM?Ih;Xa_n< zXteV1oI(hSd#=YBdrD4Puu4I{DlJFx#hY);5^dIu1f4J&NLd{&=pjmuF3W#)s`w11 z1cz*<*|ElzJ-;mI3WkB6i@}9 zfFsap097@9{GtM#bKJZQYA67O0?>%Xa2z9LkOB2Z55fS!A_&y(5HB9!oArl|d&#)E zeFE^KoIrFoqvRWCIvd0Z&o>6YLkl19gNH=E2~eIFpaJ2EjAUp;i*rMQO_#tdB?Z*} zry#VH3^JvLjNSl6>|bz=3QXJsW3y>+xYq$Q?jJw9Lvj(PL74ddn+0DTyHduHY!|Tl zW>~6*#0hl~a`(4ZS!%X;73oTL-iYtLptwKRKa%?}b^WIKX#(u=_;WQ62OZv>x$i!N znF@9KOnvUd%X0!m_i0~vSKHB24oJ*tI{vbvbev7vo%7ifCeopLeB#&#^aw#8*BFfU z;?D<^pq9zfiQlpq!Y&F0mF8hY%I%kNN?eGX_t!-yEvs-UEWiLR%lIVQ zpL37fTfzg7DMV}8CST6^g0TA;eZ^{uRIlGj!z)+f8bZa-ed&C>RKeU`BHW)L$5nLfUZr(hEtf*T~z2ts`6yXdj%(b1y!aW zAW#9@l*#FK&|mvObRKXc&;o`zc~6`t^t3^WX__xk0nU2ps)S=&g9aU*!%hQdez@j^ zr)xN_34H&;?t5^PEC&X>)YA*&^-xg+6Gh>XBJ_S>ruY>{5P-$J6QFKp0XOk&8EaEJ zqg7K;d$18hBo)>Wa05g8Fch)^rML~JFDT$kqdVaGhV>oUK@Y(>hV_alhGCVU^i2-$}3$4~#K<|c0|E=^HQWXUFUJ2v1_F8vSfO}U!NbKzVo9860u1Qi zeT0V5V5zjq(1n5@dYmwSxj~Qyg%zS$;hv(7hmd(*`BopSH+c*xB6uhBFg%Rf8bACC z7+xv&iL>IjSIGJf$71^{RZ?Gg025;IoC@GD^879YEP-f;hC*l}`a*1mCJgi)g|DvR zvJsBM|Ca#{3e2sS@edAFur%+iaQTj4e^(T4QV%$8^2(4@4qh()hw+vIJ!lYzNMgQ^ z-((m|KPaO$cV_gDetp&k-&n&~3u02Z!MRamI%UwNC+<<~Mat!smvwWk6OUV{b1MSt z=i|B`XAusrE6-c&SynC3Ejd;UewkBix;G_#Z^1{de~+nlt70xE&-CW?lwtHc_vNTw zA2IL7r*?GP1p~&!ziqfm8p!5_u2COfJk&6`s4!@Q`^FiX}Uq{>gDV<2V zmAGo`$Pn8#Z;@CZOq{O=YJ}C(!QX5+g_qWUiAp6B3Q7Vlba_Fbw`N;E6#q6u0uQcVu40Y|^VJ=cGCN^3QT%uav5mr;r?oFs&vGJo?tFcl1rB~;VK{29^Bs{m|r+z4d z(kJ*VKaQ}|(&3)c2<4x*ng4S-6dOu?E`Z+nq=CWagFw3&?AU4DB=t?bsYmAF-EKXD zE-g3x48BPc$){c-Uri*|urZkud72bCyQLbSzMA=*G0)WO0P)$R>H_k(1;0D)jf`l@he)BoMsjTZ+6O{_8>?XW14X2geto$6 zjFw?P3H;w5p@w-`@agCe%7~tU7l4EUg%Tg~WP!Or$M0=3-8KPruL3nBIW_rd4=pif7gKQ=sp56`> zS#U5^^swPc(ua5ggqy%=C3rCcebiPDtare1dM-k?jo-nCg=FBS_#^iKrE>pdHIPl< zo_|lkl8^@nU)er@TQm0TuLA#}M!UKc-BTQZ#u5hvzkq-Us1yM9w}&MZpWprhk+1l2 zfs59WZ*e?Ojuii|kt78s7^s`T;yN`$LiBTH69OOoQHecC0MP@1z@31SZ8(Pj&7#XLCetGKxwVb69d%@-w-g5F?};S=89z zQrLPi5nQ->`IT69ULMQQQ5Q9n>=TvQ#(g=)$tI&Zgw~NlIp%H0y`yc&&4hE3G-{v% z1B_~+IM%aqK!%L>H9E-x$pT=0fDvKv@t61AW?0q0xg>s{_M@D+mwx^dt&FurblhlS zH>NA(r40pwy7fiq~63S{jN z`<38w1&^Gn;zWqJ%WyFpJ4}LKTgB&dcbsO?CaOV8(cX5=(Wr+El>twIt&fMLu&g2=C9nowa`lEn>95z*zfKf98{V_ef&@zj=U0OCrK_wpgqy=3 zIz|a~U;>2cc?b-2$&=TEdCW@W9z?g4+;5E0y0$Qxx3KT8xlk=P&xD{lT5W(ZBKY8Z z%w=yjMtPf-slax$XuC?$x$k?o?dII4+3YD*L()5j0&8x(Mm@t%ZN9e_5hQOOEgE3` z@Qd_{lGk9$AA)wM5l_jkm zBQxRExvoliisFu71|I8_>R%}#muMFY+*;F*WN4`xd(zYNIoTM^YqXr_c%*KZFSSAD z)K_{3$YWiP{-_s%DAIA@RyxYb4rhcNg6ooyW)!z6fmWa)8I*Fio~+54G_YDah%XgC zsKh6hw*>;M7u?Gi|GHDj0?EY?r4q=n29+MfLV#+}lTC+yQK7eRW>nBE3JT)0hf6uM zvHmwTs{DskGE}2N&Rl;<1JEGhY)OfQg%9KPLx$9MBiyFRdJ)Hs0^_+;KkSnhGy%+rr@x_0H4rlnVI^$< zwlS|K#8UuIXnpp%k90B{F(7QPprK6zU9ld}P%qc$2 zD%cL)g5c+4ZWr7LZV@OE3%N-f{u2E|&dU&I<$(M}ckJUNg~m1gO84hht0!s|_tPBJ zp6YGI%*+_6mHJ#~ENj11M`+6L-I3lCC|oJ)a{MYvJcqsnD@{UhY%yvnnvy?Y*!5{G zcUhO*_PS3UeM|O7F^aOPB`3xD$EHGGe$Xg)5QA7u#*)2fMlfCXu&Q7q@&``jIpDbf z3|9RjIHMHeniie~Kow+o4g$(y#-i8>xi$KN+Ocst$PT1EsWTTpW#=@5o&#e%5f=Ev zK^*+p7oM8ry}%3|R#~Y~faMXtf55C{ERRbv2HbA2E*!YM%0Yb%G|q=O&~Q>Y7|M9S z#t9Gy0j7$+1@sysFjzRpclgkEMgmT3FtkHKYAuYXkwVkta^&(7O9pJPEe}2LXQ8#4 z{yL=PH-?T01CfY;9ULyt@89#kVEp{=nmHYYPI#+-xabD$6!5mhS#rg)A-nMKr0ojyLpbPB+pVF#= zLt)^IfjzeAv{-RNQ=C&|258@IZOj+9mjuODq=RK+T%`s;CHWdS;dSE+u_rsiTNv<# z3(tbJ=Wt*OZOvc^54i?H28bD890`Sfy1T!GwkpUY2JC6TPYGCXsKY4{$-s;W8i>xw zK09ODTePxA2 zD<7x{?QB1Qy?XeA?n!#1M=&G|nmOqGqF`GL=qiHaPE7o%sWDlW+G0#%Jk2z~kVxJ2 ztwrVdlV%8;O>$~2lA{t7Z zkf-qol5t6BvLx%0&$g&t`N!LH35OFH$;KWg>Y zH4Cjj<@Lr0`)b=%DQ=Wd*)$Y3egs`MqtpO8hG>G6q)ZJ1uNPFU0pgG5OdOA zY4Bu9O1n^Ugivo;w(v>eYR!VnDf9Z=MEw5s20>`~pp?dk`jURf&-+l(NnXc z^Rvz?V$CMf;tN)1jxG%y`<_e9a%S@9^m{(eGXeJe$i=LV9djP`1==%gGjTKT3F0nW z9#^VaFuZw_qoqSf9xWA`XPfgrzffw1z&VoTMk(>?pi}?db&||8nd~89-sU~0OWU+s zT&fhbmf7UWqQj3)>XE8SQTCj?o~4nTd1R9M?yK7xw<^1Zdy5N2UVhttA#4{dt4N8` zt7#Y-JYx{*`1*QXqi92Y{+HNn68=hzRsu=>(Fx7TLTW+pqJ&V{T!aa$bLT)~%w0|f z50{_Ef9~9Q!P!w>t5WCCQ}8&}AeVNVKc>4=)24Jy`$p$++s7t?ahI+uhIk4}(!Ua_ zX$cP0;PWO(CP=*eRN34lU z2Bj?=!oXSj|A32~{{J$vjxdH=SN_beDJpfOPaqF5M%(;TiGvM|o!w5)zVE7n_+D`%)4AR$v)mKaw_S^^7 zXNUf=Ko;Od2+IO6xI|J|)&>|v^j7-GuWXjbfLSqEs*R!k8WhT20H=atW43E>q5wV| z2H*>nKM-pYEVK{-%8%wPP|e{TWHB*dWL2dHR(IADIE13n9g4mkfvDk-9smwVK6(AL z_}X^WB>;p7s)zI>jOt12;Z*n!&fzSPpjpZ}DzFEAfZ5!jxlM#!Q6&I631D8_03S9K zff?8kKnR4=>-YnP8-Iu)?oJ8DArKq_79^VwFL~qNsi2mt*WF}9z9ND=Vy%931}GnY z`RH0xm$bL0jZ^3d{r=3g+qb_XFB+s(?zB|^c)yL~NGk9(uJmB~Sha%*I+d^pguxaj)3;3G7z|5OHHmvRMf z_w`N~Gf_9rTND;m2MhE~rJt(-1-XeR@L~-TgH9NvDTE>_JlgOqHD;N~?M7{BM-Uvm z>e)l4ckk}A^YAz0`Jy$A&35zHAB5iSnx+Vrw+RT|P)Cj;J3ijG zif=1X)ongXIt01ITrqVc4v(cxZj1%KGl`O(58R39lrNO zHnZO)A_Ylr-WoTde!5M;{?|4OJlJKvs%Znn~% zp-XT`)6U6+FL5VZwv_g^Y)`KJUVs^yE^|Cf)@fSPVbgJQHw57yPJ$Ot*%0lCp=Qm2 zh`_WT$NTf@Ca0&hG(a5%$#d<*xg$+jnB)hn$D4?r@a@G=36}ONrf%u&%FR8CvHJR} zi>CLq*{jE#sC7+>CH|1R))}iZlk14C?fn8x$QSDx3(SxQ{uG}9BT*N4V|~)?(tYkd z23b0RTmpP$ge#1^3}lED)6>1g%jeR3nhlXidl!um{C>3!ICE2!z`oYk|p9~5rd@GqsRcL@!We-Man z7RnN8^C0FlUoe?}pE=QWydo&5I3@e|q%i|mQNCbBT1)q_cL`3;EfAb%IwT=&p!8hSFnLIuBjD4A!4u@OAS zuYn$?bI=nRRSGQN;PIn8xL>94{%ZJ8YRFjzmwC;K({Mwo5ZDS4>RD{Sd;nq;C6z(s zLs|UvN+vH4hymiPOl?Cv-vJ-lsG^SR@9}2S__VaU-0)=9 z`J@R{yt2Rn3~t2%yYmcZztT+}`+gDfJ==;8j5e zUb}Izv?SSs$L^j2Oa|p8F_%$XucNAr;|0pWl3E~o18ykzIi?tNe4l~u707C*qIK^` zTXBwg0r;{|pfZ{TL%w5hG;BTHVY7fXkFscVyhmT-B1xMh39UV}}%d;Lky$2XNkj+Z?uH7x_#YWBXd=K%fq5Bt z55UQ|2)ra9p&ab59!!om*t22O64U%SBh6H%28{LQ#!_!%>+XO|Fc%8pH~@UbX}BhdaYLlA0SdV z@9Z8U-RR(a7~roLp0o*h7Fhl@DfrYFjwTT`5T}p%mUn)%MYkYorJLA?%oLO<9_c9# zUo09>&coSHa>O0-E}NZdnV zlg;&{=5be^-O2rzZ*>}o+L~;3Q#W?xICYtoOMfL~ZW`<*k0$Z{u93>=XyVz$ki||e zS6$d&buDACpk0n17MKf_mu-{yb4{CXTX8@AXO-R3@ZzeWZnH(k5F~5+sW3d0yBGhkd*sHQg0T=-3 zEp91JNH@DaI6%C(xj6VmVE2axrD&L9!yd|(T{jHF#E>3XN{lBK?k8VDJQSk($g z_BmH}@m%%VFl)$v}?0nox2z{oy0iwCy;)HFu`t0O2Qjwk&9ROZpRCqm46j*Sjs&?uq2# zdHQX^)s(%i=h_?5T>HJXRbSGRBqO*78BeSyMJ}K{9Wi?WY5Qm6mqb>sgTKIRk zLtGNI4rt|4AP+lYRV}=#Pm^GwfqwrtSSX*&eB`ns>_S!`wf96&*AG9h2YeoQkbFMP zB0KNz0~C#qk0J&6RRudMh_4}=RB|SjrF#;e%XEGFh8?Jn!(x` z&V)?`c~vdRtGk`fozLSp{EzPD_#7bYRV2}3704s&*Nm0dKPk>T93VctT~)GHP^VdC zI4ZpCjNGjO`v=GvphlP~R;%`smvbcvHmNeos@m-IIiZK1VUVLru7*EZv?u+Kw(nW^ z%rKf>xhNes6z{eZ(rTet?INp?^pR;OD#2pqrV)S{BUTmPRrS%4E))@Mda+zx?eAD( zR1ty?lesG#RrC=F4nXG}b}L=|=W%4EW1 zKq22G`w82zPR1chvWVZhSdo!e84QmY$18~(Lx=A^vU#Hc(%OXHuG+VS?X|vB2#L() zI6$Osp*DkfIlm^Uz85-{t0&x{q(SgHms@?HhS||tUG(&5@4Vr$7Gb(;z6NIvZZea+ z`*M^N5EY~nc>sz=zH{%H(B5Bn_TsHJwu3)7$}=DRpui+LjAqzNYsK!FIkZL zB#H5m`u){V)w*-6U%D}!nlHp9Z@X5RxQN&eKP__0s9V_#yNQ`(KUKH-<$e7$jfU;6 zwBuM;WrDmz`D@vix<$IrsbsL|Qpuiu_=-9?qo^Kh#hE#?8B2VT|M|}$1qlY7uR?o` z+c&viARGodn)CP&1Wv}nN3_ickct))3-ly!knUaa#cHfvS2(Voh;Z+D<}cJcuu0;+ zNioJ1*PSa=O*|Su-rsB;`Sw^~wWh@O_7ExmRj96dP z*Q~f|o@j*J+)U(R6q-s@QGxBJFAbutD=^UL3@t()ExH}CEwda;-goS4fmAeK!tp{= zqodnvABUcj1M7#O&vOq8JHzv*&xSuT4;J+-5fdmNegDj8IjiIzaggp;dS~eQ*8+#0R-a*Mp;hcqRbS7B`aNORj>P3~}^+1bx;hdu1d-aaioKJ*F zbiRtuwxR6R$b!EueJc?W?3t&3BPGgYs=Yr~A*-11siL0sQLKK~%JcyuuO!eI$?M=a zaoXvan0g9HcKqs0y6mbi5{+FS*2rhB5vB((k~{8QIL<ZPL8$D}?4cW501qcv+4S ztpL@w(5zjx$>_FxRmH0e*|#?=%w6MWwAM3-NV5sC?wQEF!r{n_RoMvFoRKQqtFB^C zBo$*M_E4MIJHHr(-H^S=3RG7*k|c+D{hU_gl5ybyqDNH-%^56Ru*Dfa4nE}(S@30@ zSW@iz0Yal-iVVfLKUZ1A)^!fpHGRZ(MXyr08m(;p8lYWBA#KbI1l4)GMrdkpB-4&V zQ2a~m@Lk@fY8vp>u|?Nr$)z2Bkr3_^w5w|+|S22{%oB2yoT`~|J8XNYfG>Uu6@;<@{|&i1^zxCEeE8{j+vEUT9?6f5G4B5 z#JCVk-I#ge;odCKL_GN0M|V1pnu4#4En*st^u649F6{sj9zWK;dfUxK^DB0y-Py4a zr(vLc$SCLmCOnJaOThPSk0l*&*^xIsfqpCa?Pi`6KFe?MVZ7b_V61I&o}s zZH9>FF^$>>SIxNCdJk(TE*otpRJ(o}Wwc4J6w;8)oXQ^#JwP~6&YkrfC~LWdvIkc| z&XwBl={RY29`>Yi&W<>5(xk_cof1zMOybsW$cpR=qI|9CI%UB#E| z_?b(_`H3eS zod^ig2D#BDjH$ei0;sq@gPqwIAFOUGen)++->DA~yKZwAyK<%L>h3we_?_aad117_ zva85zdS;p$b7X+g+5uvi4L39zQFY#UvZB3s$wlv5?7QPwHR-hw5C=1QKAnFCST(hL@$eyr(a`1?sJr~W?6y+*Y!-5+>bePOdW4F4Qt4m3KW)3S2ym36%jPG3)Fzj7GWC4P&o(sHCsf~B*%KOGl& z$KI*5u`sP59Z#3@HQ?$syd96Y>~*ViLMrkAF_PpG_)K*o$ck}~&LS^4hW0$;`XkoK z4Q&*8}f^ zvSl@?_Y1XJ{+8B`7)o|IGZcy$py{BdoDTzCj{*EzF#4-x7o<4Uo%*MQ?TG_4K2~1n(JFYtM8hbxY zU`4P>U(W7i0725px-7@5*DJ3vG>^1cw42_X$1=(%qvW@3Ei@A+GS+%$Jz`jygxNH1 zKgC*KIOFLbWIZ(L-#1w^5r~K;Sj8TmyKWtwz6znxj!!oA=$PC5E6ObvSMtdfl74Q* z-kE=+$wT?foP^UdG2NK1E0njK;@CF=nd>!G+`2ZM`S(e#cztJ^H;r_ua*!c8}XeK!i1|x8)QyLa(B%ti(jU!`d*;BzmoY@&+qY zrD&a0jpx^wQ}mbXybF!32Tk|_kF^Lzozyga(d0i`k$dI+DUPfjbXeq@uD3bwn|)fu z7-k2Vvdgktr1bl#91|`lL|E(IF=6JX)f`demEXSXAV5_zNR?aIqqV^QIK#3@n#8j> zg0Svc$EU)#WnbnsXHMt_Z%5JQ3YAlRB4PCzE4uj0eu0*uy76Ikx3%r6uCb-B_ULL( za#5q^?7G0O%Y$_8-|iXtaGmb^7>uV%$i3_m}vMt3Zw>tS*t$RHRMQv$#CmjEvo6b>G8b?fg;jDTp&G`oJ zyu|Xbo+Io1p|)=eL)Dm|;Jvr2PH5FikdKiW~gq2lnA966-=oxqh#rH`Du&59W4_^)i{QjJ6k} z7t{Mt50nKj-@IGlSBGu;xRs)4V2S!zYSG_WMN?+GY5%m0gpR-C^3ZvL*vY)7x1Pty z2N0F+T#1qORM5938Sy5vP5Ah&U(|UAyA!y<>txJ?0@MQN_`>-wpjDb!(I@IpZgLve z&i>g5mid$JkpWg*QKUon=5?5N>lLY?Z{;<`4;{9Kcfydb4-DsSd1zf6AYdw zG=FveQpZhkR>BZab7L?(Ma@EeBN9Y{Y$Jxb$rIE<7zIkDg5XA>-0?yi1}js*F`g_- zX}rT7d0A;H^?5I8%f~;amFA{dj~)EdUl&syy{hjVTx?L3r^lT%M36M+Ql9GEfjzGO zM_%_W;!~ZT$?OeNm6M19`HU;3pDN#)NSXJ~Qrw_7jN&Y!8n=mjmK_v-zV-!k#Q^O? z%0#PQsf%VYx2U;A0}<4;oT>h&fHSbqxgZ}3T7Nz6nigXkiAsOt>7uoPtJfZVTpNEF zzekSkLG7M)iC>iW-x!Krm0GM!+V^{*GIG9yB_yzhTa7zZ?9;2fb=TfcR)~Sy`j$sZ z+blkq<>{-dhDdGd*;{LF^=y(p?J(jrVljTVbFw&=Efhb}AohjcP8zG`jdVCvSdrVmze0}no-1e>Gxy~V5cLmNv} zTcc3bO)h>{de|*y`PnvwbHbkC(4qp{jY!U$?XhJZCzb^?vu@N7AK8^NcD0PP~rXrEoH_so|Ou z!ES?}({cqTt90{BY5q?n~@_1;&lm@r!A)iIXO0k3&xbJOs`H0#xZWp3p8X~5&N2%zc*bK z=D^l+C#mgHw3dWbMO9VbbMa!HL@|^x)aXqPo;TAyK>SH|V)Gh6Kg(S;fAhoo05PDM z4_5f!T$4FVl#IG68?%w9e%xp6RiK|tU*@vPTq`b6RNOgB_k@^cnO)Sj>%pGhZzxF( z;s|Im{=EkG!^?F_d4xcfdoi+O2Tdin9p6})sCR&{HgRQr&}+&zi#u3O<*|kxwBt|M zSy=77iA^HMl=jGFYt(DGjoE12S7sIA9N%nzJ+>is{<%4N*OTr6v1LiVD!uEMRDL9e z(~9_wnR(>hy(!c%4-HD?@!wN*)wy+iSeYADR#Zjt_D>w`x|Gj+IVwQ2<9@@Svzdw} zQ#Pj0x>;k#bt`hdnK)q9br|Dna)3C(vvD@!`G7P1c?NEaydqGC=Axf14S(cCnsw~y zO_G(OEM|~=2MD+KyQjY8s3`bVEm0zMvpZ&3uuXc5tN({~GdNG;HR&%GbUH2B_0vbgtH{FtyrW;2 z^q)@`=eACOro^REYR0chVnHEj4=w|!Kn`yNmfua)Bmu-1m7l(AJR)~?3bSeZ zcNCjZBbQxk(EnV84?o|#O$(CnY;t}cJL?i!TV>JiQbg3i`3$h>Ia$;)IH^{@jPH*b z3hqlk+%S;-dlHy_f2lcB@w(xaYj`&}MWqinq)GQHq&GSl=qWzqbm6Xq92_%GS$}?C>lB%tZ9h+=i%?=_wEE)dZ zSLgW0Xl^wbtfK0`yRmK?a+8DiP-Cor*oRs;r={%^Vx=YRiq`zUH^s3?3m+l@4tU&?eE;4ZcN6wa~09gW4(XYdj9Sa^u4W8J~}Y|LNjzkzk!IS!_z{ux|x=idaK z5O=6r6752FKEVF-I;P(--%c{}Yz?8uJ{}+zrUkta-C9z@v$}ih%@-}JSa?y<$ok33 zYIG;_v>kg7v=bKhNiybk82bz3O>4L3q9jMfWv07J;tVUQ!Y9|T z3IDw86cO8`B>T$^p|q{ks@-xi)a_lnxAVRzG3x**rFVPy?^n8S&XV**bQ0CF>acNu zV3HNm<~if=imtt+Cu-Ma;oVLjyG80qBVvT^I! zjKYty82Ss=^m)@Y+@uTB>+_8Bi(C^lCA zH10%!Ulf~C!INVE`u)-^^yYI$EsSe#8a4{qqAlfBz4x{a3!L<*Vz8X(P)Ek*;jxbR z`d=gunhD8Q`Oq`iD|M*P%-uHe#o#Sc(W?=n|Bsho6dp7)_5994NiT~t@Aoo%PTnt2 z+~-BbR0hIcK^H3fqP9Q0xvJwa1+Z@d1*3iQ=#>W&;CLT_LzK^dXIvRjgzj0>=?Oyb z-F)!T*dh^t!-+;~s6OpmUSP9lq0#9!PGN5ATB0ZsQ!*<_b>EIdF2`uChi|^~QYNq$S7x$(;LW(NBiD&p5@E zAh~^j2#q2?KvDI=ZVxr}Wp(ecuCwopkoK14MW2J{wZV}xY0To1sx!+wzg>B@mj574I>4E zka|OuKl@&I8wU#6gv>lMwa{CJyOT|UX9h+vwJo_;7psY9t1k7JGvELI zRN6h!z4+wV*Ne}Bo8?7S8b*1Cel35eeOjw`CZ^5QmQyWIktn~YO5A@BJP9(bJtd9@ Gz5gGw5$>4) literal 0 HcmV?d00001 From 4d28c008f35e4455429a5cea2c70bf4ec7908ee1 Mon Sep 17 00:00:00 2001 From: JackTan25 <60096118+JackTan25@users.noreply.github.com> Date: Wed, 11 Oct 2023 10:11:26 +0800 Subject: [PATCH 16/25] fix: fix name with ` (#13163) * remove quote * suggestion by andylok * rename * suggestion --- src/query/sql/src/planner/binder/project.rs | 17 +++++++- .../processor_merge_into_matched_and_split.rs | 11 +++--- .../base/09_fuse_engine/09_0026_merge_into | 39 +++++++++++++++++++ 3 files changed, 61 insertions(+), 6 deletions(-) diff --git a/src/query/sql/src/planner/binder/project.rs b/src/query/sql/src/planner/binder/project.rs index 55e4e64d43ff..87ca013c9b47 100644 --- a/src/query/sql/src/planner/binder/project.rs +++ b/src/query/sql/src/planner/binder/project.rs @@ -22,7 +22,9 @@ use common_ast::ast::QualifiedName; use common_ast::ast::SelectTarget; use common_ast::parser::parse_expr; use common_ast::parser::tokenize_sql; +use common_ast::walk_expr_mut; use common_ast::Dialect; +use common_ast::VisitorMut; use common_exception::ErrorCode; use common_exception::Result; use common_exception::Span; @@ -50,6 +52,14 @@ use crate::plans::SubqueryType; use crate::IndexType; use crate::WindowChecker; +struct RemoveIdentifierQuote; + +impl VisitorMut for RemoveIdentifierQuote { + fn visit_identifier(&mut self, ident: &mut Identifier) { + ident.quote = None + } +} + impl Binder { pub fn analyze_projection( &mut self, @@ -263,7 +273,12 @@ impl Binder { // If alias is not specified, we will generate a name for the scalar expression. let expr_name = match alias { Some(alias) => normalize_identifier(alias, &self.name_resolution_ctx).name, - None => format!("{:#}", expr).to_lowercase(), + None => { + let mut expr = expr.clone(); + let mut remove_quote_visitor = RemoveIdentifierQuote; + walk_expr_mut(&mut remove_quote_visitor, &mut expr); + format!("{:#}", expr).to_lowercase() + } }; prev_aliases.push((expr_name.clone(), bound_expr.clone())); diff --git a/src/query/storages/fuse/src/operations/merge_into/processors/processor_merge_into_matched_and_split.rs b/src/query/storages/fuse/src/operations/merge_into/processors/processor_merge_into_matched_and_split.rs index 1bbdc2b8f1a7..79e0a7dead5b 100644 --- a/src/query/storages/fuse/src/operations/merge_into/processors/processor_merge_into_matched_and_split.rs +++ b/src/query/storages/fuse/src/operations/merge_into/processors/processor_merge_into_matched_and_split.rs @@ -248,12 +248,13 @@ impl Processor for MatchedSplitProcessor { .delete_mutator .delete_by_expr(current_block)?; + // delete all + if !row_ids.is_empty() { + row_ids = row_ids.add_meta(Some(Box::new(RowIdKind::Delete)))?; + self.output_data_row_id_data.push(row_ids); + } + if stage_block.is_empty() { - // delete all - if !row_ids.is_empty() { - row_ids = row_ids.add_meta(Some(Box::new(RowIdKind::Delete)))?; - self.output_data_row_id_data.push(row_ids); - } return Ok(()); } current_block = stage_block; diff --git a/tests/sqllogictests/suites/base/09_fuse_engine/09_0026_merge_into b/tests/sqllogictests/suites/base/09_fuse_engine/09_0026_merge_into index b1b493db22a7..5dd2d4acf453 100644 --- a/tests/sqllogictests/suites/base/09_fuse_engine/09_0026_merge_into +++ b/tests/sqllogictests/suites/base/09_fuse_engine/09_0026_merge_into @@ -507,5 +507,44 @@ select * from cluster_target; 3 a 3 12 b 1 +## add more tests +statement ok +drop table if exists target_test; + +statement ok +drop table if exists source_test; + +statement ok +create table target_test(a int,b string); + +statement ok +insert into target_test values(1,'a'),(2,'b'),(3,'c'); + +statement ok +create table source_test(a int,b string,is_databend_deleted bool); + +statement ok +insert into source_test values(1,'d',true),(2,'e',true),(3,'f',false),(4,'e',true),(5,'f',false); + +statement ok +create stage source_parquet file_format = (type = parquet); + +statement ok +remove @source_parquet; + +statement ok +copy into @source_parquet from (select * from source_test); + +statement ok +merge into `target_test` as tt using (select `a`,`b`,`is_databend_deleted` from @source_parquet (pattern => '.*[.]parquet')) as ss on (ss.`a` = tt.`a`) +when matched and ss.`is_databend_deleted` = true then delete when matched then update * when not matched and ss.`is_databend_deleted` = false then insert *; + +query TT +select * from target_test order by a; +---- +3 f +5 f + statement ok set enable_experimental_merge_into = 0; + From f98837e3a896d097c7624d6311a25cbe313e5e8a Mon Sep 17 00:00:00 2001 From: sundyli <543950155@qq.com> Date: Tue, 10 Oct 2023 21:09:57 -0700 Subject: [PATCH 17/25] chore(query): refactor agg state merge (#13153) * chore(query): refactor agg state merge * chore(query): refactor agg state merge * chore(query): refactor agg state merge * chore(query): refactor agg state merge * chore(query): refactor agg state merge * chore(query): refactor agg state merge * chore(query): fix render bug * feat(query): add test test_box_render_block * feat(query): remove useless holder --- src/query/expression/src/utils/block_debug.rs | 45 ++++--------------- src/query/expression/tests/it/block.rs | 39 ++++++++++++++++ .../adaptors/aggregate_null_unary_adaptor.rs | 20 ++++++--- .../aggregate_null_variadic_adaptor.rs | 20 ++++++--- .../adaptors/aggregate_ornull_adaptor.rs | 14 +++--- .../aggregate_approx_count_distinct.rs | 13 +++--- .../src/aggregates/aggregate_arg_min_max.rs | 29 +++++------- .../src/aggregates/aggregate_array_agg.rs | 32 ++++--------- .../src/aggregates/aggregate_array_moving.rs | 42 +++++++++-------- .../functions/src/aggregates/aggregate_avg.rs | 17 +++---- .../src/aggregates/aggregate_bitmap.rs | 31 +++++++------ .../aggregate_combinator_distinct.rs | 12 ++--- .../src/aggregates/aggregate_combinator_if.rs | 8 ++-- .../aggregates/aggregate_combinator_state.rs | 8 ++-- .../src/aggregates/aggregate_count.rs | 14 +++--- .../src/aggregates/aggregate_covariance.rs | 12 ++--- .../aggregates/aggregate_distinct_state.rs | 38 ++++++++-------- .../src/aggregates/aggregate_function.rs | 4 +- .../src/aggregates/aggregate_kurtosis.rs | 40 ++++++++--------- .../src/aggregates/aggregate_min_max_any.rs | 17 ++++--- .../src/aggregates/aggregate_null_result.rs | 4 +- .../src/aggregates/aggregate_quantile_cont.rs | 13 +++--- .../src/aggregates/aggregate_quantile_disc.rs | 29 ++++++------ .../aggregates/aggregate_quantile_tdigest.rs | 16 ++++--- .../src/aggregates/aggregate_retention.rs | 26 +++++------ .../src/aggregates/aggregate_scalar_state.rs | 17 ++----- .../src/aggregates/aggregate_skewness.rs | 40 ++++++++--------- .../src/aggregates/aggregate_stddev.rs | 12 ++--- .../src/aggregates/aggregate_string_agg.rs | 11 ++--- .../functions/src/aggregates/aggregate_sum.rs | 31 +++++++------ .../src/aggregates/aggregate_window_funnel.rs | 25 ++++------- .../transforms/aggregator/aggregate_cell.rs | 6 --- .../transforms/aggregator/aggregate_meta.rs | 4 -- .../aggregator/transform_aggregate_final.rs | 16 +------ .../aggregator/transform_aggregate_partial.rs | 22 +-------- .../aggregator/transform_single_key.rs | 43 ++++-------------- .../group_by/aggregator_polymorphic_keys.rs | 2 - 37 files changed, 350 insertions(+), 422 deletions(-) diff --git a/src/query/expression/src/utils/block_debug.rs b/src/query/expression/src/utils/block_debug.rs index 916f44f1ce5c..67bd614b99f2 100644 --- a/src/query/expression/src/utils/block_debug.rs +++ b/src/query/expression/src/utils/block_debug.rs @@ -177,46 +177,17 @@ fn create_box_table( }; let mut res_vec: Vec> = vec![]; - let top_collection = results.first().unwrap(); - let top_rows = top_collection.num_rows().min(top_rows); - if bottom_rows == 0 { - for block in results { - for row in 0..block.num_rows() { - let mut v = vec![]; - for block_entry in block.columns() { - let value = block_entry.value.index(row).unwrap().to_string(); - if replace_newline { - v.push(value.to_string().replace('\n', "\\n")); - } else { - v.push(value.to_string()); - } - } - res_vec.push(v); - } - } - } else { - let bottom_collection = results.last().unwrap(); - for row in 0..top_rows { - let mut v = vec![]; - for block_entry in top_collection.columns() { - let value = block_entry.value.index(row).unwrap().to_string(); - if replace_newline { - v.push(value.to_string().replace('\n', "\\n")); - } else { - v.push(value.to_string()); - } + + let mut rows = 0; + for block in results { + for row in 0..block.num_rows() { + rows += 1; + if rows > top_rows && rows <= row_count - bottom_rows { + continue; } - res_vec.push(v); - } - let take_num = if bottom_collection.num_rows() > bottom_rows { - bottom_collection.num_rows() - bottom_rows - } else { - 0 - }; - for row in take_num..bottom_collection.num_rows() { let mut v = vec![]; - for block_entry in top_collection.columns() { + for block_entry in block.columns() { let value = block_entry.value.index(row).unwrap().to_string(); if replace_newline { v.push(value.to_string().replace('\n', "\\n")); diff --git a/src/query/expression/tests/it/block.rs b/src/query/expression/tests/it/block.rs index 8264f595949d..397c769028bb 100644 --- a/src/query/expression/tests/it/block.rs +++ b/src/query/expression/tests/it/block.rs @@ -1,5 +1,12 @@ +use common_expression::block_debug::box_render; use common_expression::types::string::StringColumnBuilder; +use common_expression::types::DataType; +use common_expression::types::Int32Type; +use common_expression::types::NumberDataType; use common_expression::Column; +use common_expression::DataField; +use common_expression::DataSchemaRefExt; +use common_expression::FromData; use crate::common::new_block; @@ -17,3 +24,35 @@ fn test_split_block() { .collect::>(); assert_eq!(sizes, vec![3, 3, 4]); } + +#[test] +fn test_box_render_block() { + let value = b"abc"; + let n = 10; + let block = new_block(&[ + Int32Type::from_data(vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]), + Column::String(StringColumnBuilder::repeat(&value[..], n).build()), + ]); + + let schema = DataSchemaRefExt::create(vec![ + DataField::new("a", DataType::Number(NumberDataType::Int32)), + DataField::new("e", DataType::String), + ]); + let d = box_render(&schema, &[block], 5, 1000, 1000, true).unwrap(); + let expected = r#"┌────────────────────┐ +│ a │ e │ +│ Int32 │ String │ +├───────────┼────────┤ +│ 1 │ 'abc' │ +│ 2 │ 'abc' │ +│ 3 │ 'abc' │ +│ · │ · │ +│ · │ · │ +│ · │ · │ +│ 9 │ 'abc' │ +│ 10 │ 'abc' │ +│ 10 rows │ │ +│ (5 shown) │ │ +└────────────────────┘"#; + assert_eq!(d, expected); +} diff --git a/src/query/functions/src/aggregates/adaptors/aggregate_null_unary_adaptor.rs b/src/query/functions/src/aggregates/adaptors/aggregate_null_unary_adaptor.rs index d349cb0dec77..0f5833b8e1d9 100644 --- a/src/query/functions/src/aggregates/adaptors/aggregate_null_unary_adaptor.rs +++ b/src/query/functions/src/aggregates/adaptors/aggregate_null_unary_adaptor.rs @@ -202,20 +202,26 @@ impl AggregateFunction for AggregateNullUnaryAdapto Ok(()) } - fn deserialize(&self, place: StateAddr, reader: &mut &[u8]) -> Result<()> { + fn merge(&self, place: StateAddr, reader: &mut &[u8]) -> Result<()> { + if self.get_flag(place) == 0 { + // initial the state to remove the dirty stats + self.init_state(place); + } + if NULLABLE_RESULT { let flag = reader[reader.len() - 1]; - self.nested - .deserialize(place, &mut &reader[..reader.len() - 1])?; - self.set_flag(place, flag); + if flag == 1 { + self.set_flag(place, 1); + self.nested.merge(place, &mut &reader[..reader.len() - 1])?; + } } else { - self.nested.deserialize(place, reader)?; + self.nested.merge(place, reader)?; } Ok(()) } - fn merge(&self, place: StateAddr, rhs: StateAddr) -> Result<()> { + fn merge_states(&self, place: StateAddr, rhs: StateAddr) -> Result<()> { if self.get_flag(place) == 0 { // initial the state to remove the dirty stats self.init_state(place); @@ -223,7 +229,7 @@ impl AggregateFunction for AggregateNullUnaryAdapto if self.get_flag(rhs) == 1 { self.set_flag(place, 1); - self.nested.merge(place, rhs)?; + self.nested.merge_states(place, rhs)?; } Ok(()) diff --git a/src/query/functions/src/aggregates/adaptors/aggregate_null_variadic_adaptor.rs b/src/query/functions/src/aggregates/adaptors/aggregate_null_variadic_adaptor.rs index e4e0317a7809..3c455fe7b1ec 100644 --- a/src/query/functions/src/aggregates/adaptors/aggregate_null_variadic_adaptor.rs +++ b/src/query/functions/src/aggregates/adaptors/aggregate_null_variadic_adaptor.rs @@ -207,19 +207,25 @@ impl AggregateFunction Ok(()) } - fn deserialize(&self, place: StateAddr, reader: &mut &[u8]) -> Result<()> { + fn merge(&self, place: StateAddr, reader: &mut &[u8]) -> Result<()> { + if self.get_flag(place) == 0 { + // initial the state to remove the dirty stats + self.init_state(place); + } + if NULLABLE_RESULT { let flag = reader[reader.len() - 1]; - self.nested - .deserialize(place, &mut &reader[..reader.len() - 1])?; - self.set_flag(place, flag); + if flag == 1 { + self.set_flag(place, flag); + self.nested.merge(place, &mut &reader[..reader.len() - 1])?; + } } else { - self.nested.deserialize(place, reader)?; + self.nested.merge(place, reader)?; } Ok(()) } - fn merge(&self, place: StateAddr, rhs: StateAddr) -> Result<()> { + fn merge_states(&self, place: StateAddr, rhs: StateAddr) -> Result<()> { if self.get_flag(place) == 0 { // initial the state to remove the dirty stats self.init_state(place); @@ -227,7 +233,7 @@ impl AggregateFunction if self.get_flag(rhs) == 1 { self.set_flag(place, 1); - self.nested.merge(place, rhs)?; + self.nested.merge_states(place, rhs)?; } Ok(()) } diff --git a/src/query/functions/src/aggregates/adaptors/aggregate_ornull_adaptor.rs b/src/query/functions/src/aggregates/adaptors/aggregate_ornull_adaptor.rs index 442fc85cc356..80d74c1a7ee1 100644 --- a/src/query/functions/src/aggregates/adaptors/aggregate_ornull_adaptor.rs +++ b/src/query/functions/src/aggregates/adaptors/aggregate_ornull_adaptor.rs @@ -173,16 +173,16 @@ impl AggregateFunction for AggregateFunctionOrNullAdaptor { } #[inline] - fn deserialize(&self, place: StateAddr, reader: &mut &[u8]) -> Result<()> { - let flag = reader[reader.len() - 1]; - self.inner - .deserialize(place, &mut &reader[..reader.len() - 1])?; - self.set_flag(place, flag); + fn merge(&self, place: StateAddr, reader: &mut &[u8]) -> Result<()> { + let flag = self.get_flag(place) > 0 || reader[reader.len() - 1] > 0; + + self.inner.merge(place, &mut &reader[..reader.len() - 1])?; + self.set_flag(place, flag as u8); Ok(()) } - fn merge(&self, place: StateAddr, rhs: StateAddr) -> Result<()> { - self.inner.merge(place, rhs)?; + fn merge_states(&self, place: StateAddr, rhs: StateAddr) -> Result<()> { + self.inner.merge_states(place, rhs)?; let flag = self.get_flag(place) > 0 || self.get_flag(rhs) > 0; self.set_flag(place, u8::from(flag)); Ok(()) diff --git a/src/query/functions/src/aggregates/aggregate_approx_count_distinct.rs b/src/query/functions/src/aggregates/aggregate_approx_count_distinct.rs index 94a53dd09c28..94bba8b7c9d6 100644 --- a/src/query/functions/src/aggregates/aggregate_approx_count_distinct.rs +++ b/src/query/functions/src/aggregates/aggregate_approx_count_distinct.rs @@ -126,17 +126,18 @@ where for<'a> T::ScalarRef<'a>: Hash serialize_into_buf(writer, &state.hll) } - fn deserialize(&self, place: StateAddr, reader: &mut &[u8]) -> Result<()> { + fn merge(&self, place: StateAddr, reader: &mut &[u8]) -> Result<()> { let state = place.get::>>(); - state.hll = deserialize_from_slice(reader)?; + let hll: HyperLogLog> = deserialize_from_slice(reader)?; + state.hll.union(&hll); + Ok(()) } - fn merge(&self, place: StateAddr, rhs: StateAddr) -> Result<()> { + fn merge_states(&self, place: StateAddr, rhs: StateAddr) -> Result<()> { let state = place.get::>>(); - let rhs = rhs.get::>>(); - state.hll.union(&rhs.hll); - + let other = rhs.get::>>(); + state.hll.union(&other.hll); Ok(()) } diff --git a/src/query/functions/src/aggregates/aggregate_arg_min_max.rs b/src/query/functions/src/aggregates/aggregate_arg_min_max.rs index 7c2909c6e8ad..bcb72619ce15 100644 --- a/src/query/functions/src/aggregates/aggregate_arg_min_max.rs +++ b/src/query/functions/src/aggregates/aggregate_arg_min_max.rs @@ -49,7 +49,9 @@ use crate::with_simple_no_number_mapped_type; // State for arg_min(arg, val) and arg_max(arg, val) // A: ValueType for arg. // V: ValueType for val. -pub trait AggregateArgMinMaxState: Send + Sync + 'static { +pub trait AggregateArgMinMaxState: + Serialize + DeserializeOwned + Send + Sync + 'static +{ fn new() -> Self; fn add(&mut self, value: V::ScalarRef<'_>, data: Scalar); fn add_batch( @@ -60,8 +62,6 @@ pub trait AggregateArgMinMaxState: Send + Sync + 'st ) -> Result<()>; fn merge(&mut self, rhs: &Self) -> Result<()>; - fn serialize(&self, writer: &mut Vec) -> Result<()>; - fn deserialize(&mut self, reader: &mut &[u8]) -> Result<()>; fn merge_result(&mut self, column: &mut ColumnBuilder) -> Result<()>; } @@ -180,15 +180,6 @@ where Ok(()) } - fn serialize(&self, writer: &mut Vec) -> Result<()> { - serialize_into_buf(writer, self) - } - - fn deserialize(&mut self, reader: &mut &[u8]) -> Result<()> { - *self = deserialize_from_slice(reader)?; - Ok(()) - } - fn merge_result(&mut self, builder: &mut ColumnBuilder) -> Result<()> { if self.value.is_some() { if let Some(inner) = A::try_downcast_builder(builder) { @@ -291,18 +282,20 @@ where fn serialize(&self, place: StateAddr, writer: &mut Vec) -> Result<()> { let state = place.get::(); - state.serialize(writer) + serialize_into_buf(writer, state) } - fn deserialize(&self, place: StateAddr, reader: &mut &[u8]) -> Result<()> { + fn merge(&self, place: StateAddr, reader: &mut &[u8]) -> Result<()> { let state = place.get::(); - state.deserialize(reader) + let rhs: State = deserialize_from_slice(reader)?; + + state.merge(&rhs) } - fn merge(&self, place: StateAddr, rhs: StateAddr) -> Result<()> { - let rhs = rhs.get::(); + fn merge_states(&self, place: StateAddr, rhs: StateAddr) -> Result<()> { let state = place.get::(); - state.merge(rhs) + let other = rhs.get::(); + state.merge(other) } fn merge_result(&self, place: StateAddr, builder: &mut ColumnBuilder) -> Result<()> { diff --git a/src/query/functions/src/aggregates/aggregate_array_agg.rs b/src/query/functions/src/aggregates/aggregate_array_agg.rs index 43930e3d2cf6..30c743e73d58 100644 --- a/src/query/functions/src/aggregates/aggregate_array_agg.rs +++ b/src/query/functions/src/aggregates/aggregate_array_agg.rs @@ -124,15 +124,6 @@ where builder.push(array_value); Ok(()) } - - fn serialize(&self, writer: &mut Vec) -> Result<()> { - serialize_into_buf(writer, self) - } - - fn deserialize(&mut self, reader: &mut &[u8]) -> Result<()> { - self.values = deserialize_from_slice(reader)?; - Ok(()) - } } #[derive(Serialize, Deserialize, Debug)] @@ -234,15 +225,6 @@ where builder.push(array_value); Ok(()) } - - fn serialize(&self, writer: &mut Vec) -> Result<()> { - serialize_into_buf(writer, self) - } - - fn deserialize(&mut self, reader: &mut &[u8]) -> Result<()> { - self.values = deserialize_from_slice(reader)?; - Ok(()) - } } #[derive(Clone)] @@ -356,18 +338,20 @@ where fn serialize(&self, place: StateAddr, writer: &mut Vec) -> Result<()> { let state = place.get::(); - state.serialize(writer) + serialize_into_buf(writer, state) } - fn deserialize(&self, place: StateAddr, reader: &mut &[u8]) -> Result<()> { + fn merge(&self, place: StateAddr, reader: &mut &[u8]) -> Result<()> { let state = place.get::(); - state.deserialize(reader) + let rhs: State = deserialize_from_slice(reader)?; + + state.merge(&rhs) } - fn merge(&self, place: StateAddr, rhs: StateAddr) -> Result<()> { - let rhs = rhs.get::(); + fn merge_states(&self, place: StateAddr, rhs: StateAddr) -> Result<()> { let state = place.get::(); - state.merge(rhs) + let other = rhs.get::(); + state.merge(other) } fn merge_result(&self, place: StateAddr, builder: &mut ColumnBuilder) -> Result<()> { diff --git a/src/query/functions/src/aggregates/aggregate_array_moving.rs b/src/query/functions/src/aggregates/aggregate_array_moving.rs index 3a58e9640939..5242afbaa4ca 100644 --- a/src/query/functions/src/aggregates/aggregate_array_moving.rs +++ b/src/query/functions/src/aggregates/aggregate_array_moving.rs @@ -45,6 +45,7 @@ use common_io::prelude::*; use ethnum::i256; use num_traits::AsPrimitive; use serde::de::DeserializeOwned; +use serde::Deserialize; use serde::Serialize; use super::aggregate_function::AggregateFunction; @@ -56,9 +57,10 @@ use crate::aggregates::assert_unary_arguments; use crate::aggregates::assert_variadic_params; use crate::BUILTIN_FUNCTIONS; -#[derive(Default, Debug)] -pub struct NumberArrayMovingSumState { +#[derive(Default, Debug, Deserialize, Serialize)] +pub struct NumberArrayMovingSumState { values: Vec, + #[serde(skip)] _t: PhantomData, } @@ -137,7 +139,7 @@ where } #[inline(always)] - fn merge(&mut self, other: &mut Self) -> Result<()> { + fn merge(&mut self, other: &Self) -> Result<()> { self.values.extend_from_slice(&other.values); Ok(()) } @@ -200,8 +202,8 @@ where } } -#[derive(Default)] -pub struct DecimalArrayMovingSumState { +#[derive(Default, Deserialize, Serialize)] +pub struct DecimalArrayMovingSumState { pub values: Vec, } @@ -313,7 +315,7 @@ where T: Decimal } #[inline(always)] - fn merge(&mut self, other: &mut Self) -> Result<()> { + fn merge(&mut self, other: &Self) -> Result<()> { self.values.extend_from_slice(&other.values); Ok(()) } @@ -454,18 +456,20 @@ where State: SumState fn serialize(&self, place: StateAddr, writer: &mut Vec) -> Result<()> { let state = place.get::(); - state.serialize(writer) + serialize_into_buf(writer, state) } - fn deserialize(&self, place: StateAddr, reader: &mut &[u8]) -> Result<()> { + fn merge(&self, place: StateAddr, reader: &mut &[u8]) -> Result<()> { let state = place.get::(); - state.deserialize(reader) + let rhs: State = deserialize_from_slice(reader)?; + + state.merge(&rhs) } - fn merge(&self, place: StateAddr, rhs: StateAddr) -> Result<()> { - let rhs = rhs.get::(); + fn merge_states(&self, place: StateAddr, rhs: StateAddr) -> Result<()> { let state = place.get::(); - state.merge(rhs) + let other = rhs.get::(); + state.merge(other) } fn merge_result(&self, place: StateAddr, builder: &mut ColumnBuilder) -> Result<()> { @@ -646,18 +650,20 @@ where State: SumState fn serialize(&self, place: StateAddr, writer: &mut Vec) -> Result<()> { let state = place.get::(); - state.serialize(writer) + serialize_into_buf(writer, state) } - fn deserialize(&self, place: StateAddr, reader: &mut &[u8]) -> Result<()> { + fn merge(&self, place: StateAddr, reader: &mut &[u8]) -> Result<()> { let state = place.get::(); - state.deserialize(reader) + let rhs: State = deserialize_from_slice(reader)?; + + state.merge(&rhs) } - fn merge(&self, place: StateAddr, rhs: StateAddr) -> Result<()> { - let rhs = rhs.get::(); + fn merge_states(&self, place: StateAddr, rhs: StateAddr) -> Result<()> { let state = place.get::(); - state.merge(rhs) + let other = rhs.get::(); + state.merge(other) } fn merge_result(&self, place: StateAddr, builder: &mut ColumnBuilder) -> Result<()> { diff --git a/src/query/functions/src/aggregates/aggregate_avg.rs b/src/query/functions/src/aggregates/aggregate_avg.rs index a4aa86c0fc0c..51656c0052b5 100644 --- a/src/query/functions/src/aggregates/aggregate_avg.rs +++ b/src/query/functions/src/aggregates/aggregate_avg.rs @@ -42,7 +42,7 @@ use crate::aggregates::AggregateFunction; use crate::aggregates::AggregateFunctionRef; #[derive(Serialize, Deserialize)] -struct AvgState { +struct AvgState { pub value: T, pub count: u64, } @@ -117,21 +117,22 @@ where T: SumState fn serialize(&self, place: StateAddr, writer: &mut Vec) -> Result<()> { let state = place.get::>(); - writer.write_scalar(&state.count)?; - state.value.serialize(writer) + serialize_into_buf(writer, state) } - fn deserialize(&self, place: StateAddr, reader: &mut &[u8]) -> Result<()> { + fn merge(&self, place: StateAddr, reader: &mut &[u8]) -> Result<()> { let state = place.get::>(); - state.count = reader.read_scalar()?; - state.value.deserialize(reader) + let rhs: AvgState = deserialize_from_slice(reader)?; + + state.count += rhs.count; + state.value.merge(&rhs.value) } - fn merge(&self, place: StateAddr, rhs: StateAddr) -> Result<()> { + fn merge_states(&self, place: StateAddr, rhs: StateAddr) -> Result<()> { let state = place.get::>(); let rhs = rhs.get::>(); state.count += rhs.count; - state.value.merge(&mut rhs.value) + state.value.merge(&rhs.value) } #[allow(unused_mut)] diff --git a/src/query/functions/src/aggregates/aggregate_bitmap.rs b/src/query/functions/src/aggregates/aggregate_bitmap.rs index dd0286d80bd3..efc289ee44d2 100644 --- a/src/query/functions/src/aggregates/aggregate_bitmap.rs +++ b/src/query/functions/src/aggregates/aggregate_bitmap.rs @@ -14,6 +14,7 @@ use std::alloc::Layout; use std::fmt; +use std::io::BufRead; use std::marker::PhantomData; use std::ops::BitAndAssign; use std::ops::BitOrAssign; @@ -296,22 +297,24 @@ where Ok(()) } - fn deserialize(&self, place: StateAddr, reader: &mut &[u8]) -> Result<()> { + fn merge(&self, place: StateAddr, reader: &mut &[u8]) -> Result<()> { let state = place.get::(); + let flag = reader[0]; - state.rb = if flag == 1 { - Some(RoaringTreemap::deserialize_from(&reader[1..])?) - } else { - None - }; + reader.consume(1); + if flag == 1 { + let rb = RoaringTreemap::deserialize_from(reader)?; + state.add::(rb); + } Ok(()) } - fn merge(&self, place: StateAddr, rhs: StateAddr) -> Result<()> { + fn merge_states(&self, place: StateAddr, rhs: StateAddr) -> Result<()> { let state = place.get::(); - let rhs = rhs.get::(); - if let Some(rb) = &rhs.rb { - state.add::(rb.clone()); + let other = rhs.get::(); + + if let Some(rb) = other.rb.take() { + state.add::(rb); } Ok(()) } @@ -482,12 +485,12 @@ where self.inner.serialize(place, writer) } - fn deserialize(&self, place: StateAddr, reader: &mut &[u8]) -> Result<()> { - self.inner.deserialize(place, reader) + fn merge(&self, place: StateAddr, reader: &mut &[u8]) -> Result<()> { + self.inner.merge(place, reader) } - fn merge(&self, place: StateAddr, rhs: StateAddr) -> Result<()> { - self.inner.merge(place, rhs) + fn merge_states(&self, place: StateAddr, rhs: StateAddr) -> Result<()> { + self.inner.merge_states(place, rhs) } fn merge_result(&self, place: StateAddr, builder: &mut ColumnBuilder) -> Result<()> { diff --git a/src/query/functions/src/aggregates/aggregate_combinator_distinct.rs b/src/query/functions/src/aggregates/aggregate_combinator_distinct.rs index a8cba0a414c7..1e392d00a737 100644 --- a/src/query/functions/src/aggregates/aggregate_combinator_distinct.rs +++ b/src/query/functions/src/aggregates/aggregate_combinator_distinct.rs @@ -96,15 +96,17 @@ where State: DistinctStateFunc state.serialize(writer) } - fn deserialize(&self, place: StateAddr, reader: &mut &[u8]) -> Result<()> { + fn merge(&self, place: StateAddr, reader: &mut &[u8]) -> Result<()> { let state = place.get::(); - state.deserialize(reader) + let rhs = State::deserialize(reader)?; + + state.merge(&rhs) } - fn merge(&self, place: StateAddr, rhs: StateAddr) -> Result<()> { + fn merge_states(&self, place: StateAddr, rhs: StateAddr) -> Result<()> { let state = place.get::(); - let rhs = rhs.get::(); - state.merge(rhs) + let other = rhs.get::(); + state.merge(other) } #[allow(unused_mut)] diff --git a/src/query/functions/src/aggregates/aggregate_combinator_if.rs b/src/query/functions/src/aggregates/aggregate_combinator_if.rs index f02f8fc5936d..fe82c14dad37 100644 --- a/src/query/functions/src/aggregates/aggregate_combinator_if.rs +++ b/src/query/functions/src/aggregates/aggregate_combinator_if.rs @@ -153,12 +153,12 @@ impl AggregateFunction for AggregateIfCombinator { self.nested.serialize(place, writer) } - fn deserialize(&self, place: StateAddr, reader: &mut &[u8]) -> Result<()> { - self.nested.deserialize(place, reader) + fn merge(&self, place: StateAddr, reader: &mut &[u8]) -> Result<()> { + self.nested.merge(place, reader) } - fn merge(&self, place: StateAddr, rhs: StateAddr) -> Result<()> { - self.nested.merge(place, rhs) + fn merge_states(&self, place: StateAddr, rhs: StateAddr) -> Result<()> { + self.nested.merge_states(place, rhs) } fn merge_result(&self, place: StateAddr, builder: &mut ColumnBuilder) -> Result<()> { diff --git a/src/query/functions/src/aggregates/aggregate_combinator_state.rs b/src/query/functions/src/aggregates/aggregate_combinator_state.rs index 368264e5cfb5..59ada5c9a016 100644 --- a/src/query/functions/src/aggregates/aggregate_combinator_state.rs +++ b/src/query/functions/src/aggregates/aggregate_combinator_state.rs @@ -111,12 +111,12 @@ impl AggregateFunction for AggregateStateCombinator { self.nested.serialize(place, writer) } - fn deserialize(&self, place: StateAddr, reader: &mut &[u8]) -> Result<()> { - self.nested.deserialize(place, reader) + fn merge(&self, place: StateAddr, reader: &mut &[u8]) -> Result<()> { + self.nested.merge(place, reader) } - fn merge(&self, place: StateAddr, rhs: StateAddr) -> Result<()> { - self.nested.merge(place, rhs) + fn merge_states(&self, place: StateAddr, rhs: StateAddr) -> Result<()> { + self.nested.merge_states(place, rhs) } fn merge_result(&self, place: StateAddr, builder: &mut ColumnBuilder) -> Result<()> { diff --git a/src/query/functions/src/aggregates/aggregate_count.rs b/src/query/functions/src/aggregates/aggregate_count.rs index bc15ad143cab..e4478bce05c0 100644 --- a/src/query/functions/src/aggregates/aggregate_count.rs +++ b/src/query/functions/src/aggregates/aggregate_count.rs @@ -32,7 +32,7 @@ use super::aggregate_function_factory::AggregateFunctionDescription; use super::StateAddr; use crate::aggregates::aggregator_common::assert_variadic_arguments; -pub struct AggregateCountState { +struct AggregateCountState { count: u64, } @@ -152,17 +152,17 @@ impl AggregateFunction for AggregateCountFunction { serialize_into_buf(writer, &state.count) } - fn deserialize(&self, place: StateAddr, reader: &mut &[u8]) -> Result<()> { + fn merge(&self, place: StateAddr, reader: &mut &[u8]) -> Result<()> { let state = place.get::(); - state.count = deserialize_from_slice(reader)?; + let other: u64 = deserialize_from_slice(reader)?; + state.count += other; Ok(()) } - fn merge(&self, place: StateAddr, rhs: StateAddr) -> Result<()> { + fn merge_states(&self, place: StateAddr, rhs: StateAddr) -> Result<()> { let state = place.get::(); - let rhs = rhs.get::(); - state.count += rhs.count; - + let other = rhs.get::(); + state.count += other.count; Ok(()) } diff --git a/src/query/functions/src/aggregates/aggregate_covariance.rs b/src/query/functions/src/aggregates/aggregate_covariance.rs index f28ae31a511f..4b13e100e04d 100644 --- a/src/query/functions/src/aggregates/aggregate_covariance.rs +++ b/src/query/functions/src/aggregates/aggregate_covariance.rs @@ -230,17 +230,17 @@ where serialize_into_buf(writer, state) } - fn deserialize(&self, place: StateAddr, reader: &mut &[u8]) -> Result<()> { + fn merge(&self, place: StateAddr, reader: &mut &[u8]) -> Result<()> { let state = place.get::(); - *state = deserialize_from_slice(reader)?; - + let rhs: AggregateCovarianceState = deserialize_from_slice(reader)?; + state.merge(&rhs); Ok(()) } - fn merge(&self, place: StateAddr, rhs: StateAddr) -> Result<()> { + fn merge_states(&self, place: StateAddr, rhs: StateAddr) -> Result<()> { let state = place.get::(); - let rhs = rhs.get::(); - state.merge(rhs); + let other = rhs.get::(); + state.merge(other); Ok(()) } diff --git a/src/query/functions/src/aggregates/aggregate_distinct_state.rs b/src/query/functions/src/aggregates/aggregate_distinct_state.rs index ce6e4253526e..f38c201951b8 100644 --- a/src/query/functions/src/aggregates/aggregate_distinct_state.rs +++ b/src/query/functions/src/aggregates/aggregate_distinct_state.rs @@ -15,6 +15,7 @@ use std::collections::hash_map::RandomState; use std::collections::HashSet; use std::hash::Hasher; +use std::io::BufRead; use std::marker::Send; use std::marker::Sync; use std::sync::Arc; @@ -44,10 +45,10 @@ use serde::Serialize; use siphasher::sip128::Hasher128; use siphasher::sip128::SipHasher24; -pub trait DistinctStateFunc: Send + Sync { +pub trait DistinctStateFunc: Sized + Send + Sync { fn new() -> Self; fn serialize(&self, writer: &mut Vec) -> Result<()>; - fn deserialize(&mut self, reader: &mut &[u8]) -> Result<()>; + fn deserialize(reader: &mut &[u8]) -> Result; fn is_empty(&self) -> bool; fn len(&self) -> usize; fn add(&mut self, columns: &[Column], row: usize) -> Result<()>; @@ -85,9 +86,9 @@ impl DistinctStateFunc for AggregateDistinctState { serialize_into_buf(writer, &self.set) } - fn deserialize(&mut self, reader: &mut &[u8]) -> Result<()> { - self.set = deserialize_from_slice(reader)?; - Ok(()) + fn deserialize(reader: &mut &[u8]) -> Result { + let set = deserialize_from_slice(reader)?; + Ok(Self { set }) } fn is_empty(&self) -> bool { @@ -167,15 +168,16 @@ impl DistinctStateFunc for AggregateDistinctStringState { Ok(()) } - fn deserialize(&mut self, reader: &mut &[u8]) -> Result<()> { + fn deserialize(reader: &mut &[u8]) -> Result { let size = reader.read_uvarint()?; - self.set = ShortStringHashSet::<[u8]>::with_capacity(size as usize, Arc::new(Bump::new())); + let mut set = + ShortStringHashSet::<[u8]>::with_capacity(size as usize, Arc::new(Bump::new())); for _ in 0..size { let s = reader.read_uvarint()? as usize; - let _ = self.set.set_insert(&reader[..s]); - *reader = &reader[s..]; + let _ = set.set_insert(&reader[..s]); + reader.consume(s); } - Ok(()) + Ok(Self { set }) } fn is_empty(&self) -> bool { @@ -252,14 +254,14 @@ where T: Number + Serialize + DeserializeOwned + HashtableKeyable Ok(()) } - fn deserialize(&mut self, reader: &mut &[u8]) -> Result<()> { + fn deserialize(reader: &mut &[u8]) -> Result { let size = reader.read_uvarint()?; - self.set = CommonHashSet::with_capacity(size as usize); + let mut set = CommonHashSet::with_capacity(size as usize); for _ in 0..size { let t: T = deserialize_from_slice(reader)?; - let _ = self.set.set_insert(t).is_ok(); + let _ = set.set_insert(t).is_ok(); } - Ok(()) + Ok(Self { set }) } fn is_empty(&self) -> bool { @@ -333,14 +335,14 @@ impl DistinctStateFunc for AggregateUniqStringState { Ok(()) } - fn deserialize(&mut self, reader: &mut &[u8]) -> Result<()> { + fn deserialize(reader: &mut &[u8]) -> Result { let size = reader.read_uvarint()?; - self.set = StackHashSet::with_capacity(size as usize); + let mut set = StackHashSet::with_capacity(size as usize); for _ in 0..size { let e = deserialize_from_slice(reader)?; - let _ = self.set.set_insert(e).is_ok(); + let _ = set.set_insert(e).is_ok(); } - Ok(()) + Ok(Self { set }) } fn is_empty(&self) -> bool { diff --git a/src/query/functions/src/aggregates/aggregate_function.rs b/src/query/functions/src/aggregates/aggregate_function.rs index 97a9de5d0bcc..5c8370552dd6 100644 --- a/src/query/functions/src/aggregates/aggregate_function.rs +++ b/src/query/functions/src/aggregates/aggregate_function.rs @@ -71,9 +71,9 @@ pub trait AggregateFunction: fmt::Display + Sync + Send { // serialize the state into binary array fn serialize(&self, _place: StateAddr, _writer: &mut Vec) -> Result<()>; - fn deserialize(&self, _place: StateAddr, _reader: &mut &[u8]) -> Result<()>; + fn merge(&self, _place: StateAddr, _reader: &mut &[u8]) -> Result<()>; - fn merge(&self, _place: StateAddr, _rhs: StateAddr) -> Result<()>; + fn merge_states(&self, _place: StateAddr, _rhs: StateAddr) -> Result<()>; fn batch_merge_result(&self, places: &[StateAddr], builder: &mut ColumnBuilder) -> Result<()> { for place in places { diff --git a/src/query/functions/src/aggregates/aggregate_kurtosis.rs b/src/query/functions/src/aggregates/aggregate_kurtosis.rs index 2c1b1337686c..6cdab1bfa5f0 100644 --- a/src/query/functions/src/aggregates/aggregate_kurtosis.rs +++ b/src/query/functions/src/aggregates/aggregate_kurtosis.rs @@ -1,17 +1,17 @@ -// Copyright 2021 Datafuse Labs -// -// 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. - +// Copyright 2021 Datafuse Labs +// +// 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. + use std::alloc::Layout; use std::fmt::Display; use std::fmt::Formatter; @@ -198,17 +198,17 @@ where T: Number + AsPrimitive serialize_into_buf(writer, state) } - fn deserialize(&self, place: StateAddr, reader: &mut &[u8]) -> Result<()> { + fn merge(&self, place: StateAddr, reader: &mut &[u8]) -> Result<()> { let state = place.get::(); - *state = deserialize_from_slice(reader)?; - + let rhs: KurtosisState = deserialize_from_slice(reader)?; + state.merge(&rhs); Ok(()) } - fn merge(&self, place: StateAddr, rhs: StateAddr) -> Result<()> { - let rhs = rhs.get::(); + fn merge_states(&self, place: StateAddr, rhs: StateAddr) -> Result<()> { let state = place.get::(); - state.merge(rhs); + let other = rhs.get::(); + state.merge(other); Ok(()) } diff --git a/src/query/functions/src/aggregates/aggregate_min_max_any.rs b/src/query/functions/src/aggregates/aggregate_min_max_any.rs index 4092c0940ef1..0169329b4c15 100644 --- a/src/query/functions/src/aggregates/aggregate_min_max_any.rs +++ b/src/query/functions/src/aggregates/aggregate_min_max_any.rs @@ -27,6 +27,8 @@ use common_expression::with_number_mapped_type; use common_expression::Column; use common_expression::ColumnBuilder; use common_expression::Scalar; +use common_io::prelude::deserialize_from_slice; +use common_io::prelude::serialize_into_buf; use ethnum::i256; use super::aggregate_function_factory::AggregateFunctionDescription; @@ -117,18 +119,21 @@ where fn serialize(&self, place: StateAddr, writer: &mut Vec) -> Result<()> { let state = place.get::(); - state.serialize(writer) + + serialize_into_buf(writer, state) } - fn deserialize(&self, place: StateAddr, reader: &mut &[u8]) -> Result<()> { + fn merge(&self, place: StateAddr, reader: &mut &[u8]) -> Result<()> { let state = place.get::(); - state.deserialize(reader) + let rhs: State = deserialize_from_slice(reader)?; + + state.merge(&rhs) } - fn merge(&self, place: StateAddr, rhs: StateAddr) -> Result<()> { - let rhs = rhs.get::(); + fn merge_states(&self, place: StateAddr, rhs: StateAddr) -> Result<()> { let state = place.get::(); - state.merge(rhs) + let other = rhs.get::(); + state.merge(other) } fn merge_result(&self, place: StateAddr, builder: &mut ColumnBuilder) -> Result<()> { diff --git a/src/query/functions/src/aggregates/aggregate_null_result.rs b/src/query/functions/src/aggregates/aggregate_null_result.rs index afdd510c3c5b..01e4dcbf71cf 100644 --- a/src/query/functions/src/aggregates/aggregate_null_result.rs +++ b/src/query/functions/src/aggregates/aggregate_null_result.rs @@ -81,11 +81,11 @@ impl AggregateFunction for AggregateNullResultFunction { Ok(()) } - fn deserialize(&self, _place: StateAddr, _reader: &mut &[u8]) -> Result<()> { + fn merge(&self, _place: StateAddr, _reader: &mut &[u8]) -> Result<()> { Ok(()) } - fn merge(&self, _place: StateAddr, _rhs: StateAddr) -> Result<()> { + fn merge_states(&self, _place: StateAddr, _rhs: StateAddr) -> Result<()> { Ok(()) } diff --git a/src/query/functions/src/aggregates/aggregate_quantile_cont.rs b/src/query/functions/src/aggregates/aggregate_quantile_cont.rs index 1eabd6998db1..e307f63f9585 100644 --- a/src/query/functions/src/aggregates/aggregate_quantile_cont.rs +++ b/src/query/functions/src/aggregates/aggregate_quantile_cont.rs @@ -213,17 +213,16 @@ where T: Number + AsPrimitive serialize_into_buf(writer, state) } - fn deserialize(&self, place: StateAddr, reader: &mut &[u8]) -> Result<()> { + fn merge(&self, place: StateAddr, reader: &mut &[u8]) -> Result<()> { let state = place.get::(); - *state = deserialize_from_slice(reader)?; - - Ok(()) + let rhs: QuantileContState = deserialize_from_slice(reader)?; + state.merge(&rhs) } - fn merge(&self, place: StateAddr, rhs: StateAddr) -> Result<()> { - let rhs = rhs.get::(); + fn merge_states(&self, place: StateAddr, rhs: StateAddr) -> Result<()> { let state = place.get::(); - state.merge(rhs) + let other = rhs.get::(); + state.merge(other) } fn merge_result(&self, place: StateAddr, builder: &mut ColumnBuilder) -> Result<()> { diff --git a/src/query/functions/src/aggregates/aggregate_quantile_disc.rs b/src/query/functions/src/aggregates/aggregate_quantile_disc.rs index e14bb9203471..99b66305c76f 100644 --- a/src/query/functions/src/aggregates/aggregate_quantile_disc.rs +++ b/src/query/functions/src/aggregates/aggregate_quantile_disc.rs @@ -46,14 +46,14 @@ use crate::aggregates::StateAddr; use crate::with_simple_no_number_mapped_type; use crate::BUILTIN_FUNCTIONS; -pub trait QuantileStateFunc: Send + Sync + 'static { +pub trait QuantileStateFunc: + Serialize + DeserializeOwned + Send + Sync + 'static +{ fn new() -> Self; fn add(&mut self, other: T::ScalarRef<'_>); fn add_batch(&mut self, column: &T::Column, validity: Option<&Bitmap>) -> Result<()>; fn merge(&mut self, rhs: &Self) -> Result<()>; fn merge_result(&mut self, builder: &mut ColumnBuilder, levels: Vec) -> Result<()>; - fn serialize(&self, writer: &mut Vec) -> Result<()>; - fn deserialize(&mut self, reader: &mut &[u8]) -> Result<()>; } #[derive(Serialize, Deserialize)] struct QuantileState @@ -148,13 +148,6 @@ where } Ok(()) } - fn serialize(&self, writer: &mut Vec) -> Result<()> { - serialize_into_buf(writer, self) - } - fn deserialize(&mut self, reader: &mut &[u8]) -> Result<()> { - self.value = deserialize_from_slice(reader)?; - Ok(()) - } } #[derive(Clone)] pub struct AggregateQuantileDiscFunction { @@ -229,17 +222,21 @@ where } fn serialize(&self, place: StateAddr, writer: &mut Vec) -> Result<()> { let state = place.get::(); - state.serialize(writer) + serialize_into_buf(writer, state) } - fn deserialize(&self, place: StateAddr, reader: &mut &[u8]) -> Result<()> { + + fn merge(&self, place: StateAddr, reader: &mut &[u8]) -> Result<()> { let state = place.get::(); - state.deserialize(reader) + let rhs: State = deserialize_from_slice(reader)?; + state.merge(&rhs) } - fn merge(&self, place: StateAddr, rhs: StateAddr) -> Result<()> { - let rhs = rhs.get::(); + + fn merge_states(&self, place: StateAddr, rhs: StateAddr) -> Result<()> { let state = place.get::(); - state.merge(rhs) + let other = rhs.get::(); + state.merge(other) } + fn merge_result(&self, place: StateAddr, builder: &mut ColumnBuilder) -> Result<()> { let state = place.get::(); state.merge_result(builder, self.levels.clone()) diff --git a/src/query/functions/src/aggregates/aggregate_quantile_tdigest.rs b/src/query/functions/src/aggregates/aggregate_quantile_tdigest.rs index 2a9493e84a5e..767962ad5b97 100644 --- a/src/query/functions/src/aggregates/aggregate_quantile_tdigest.rs +++ b/src/query/functions/src/aggregates/aggregate_quantile_tdigest.rs @@ -359,17 +359,19 @@ where T: Number + AsPrimitive let state = place.get::(); serialize_into_buf(writer, state) } - fn deserialize(&self, place: StateAddr, reader: &mut &[u8]) -> Result<()> { - let state = place.get::(); - *state = deserialize_from_slice(reader)?; - Ok(()) + fn merge(&self, place: StateAddr, reader: &mut &[u8]) -> Result<()> { + let state = place.get::(); + let mut rhs: QuantileTDigestState = deserialize_from_slice(reader)?; + state.merge(&mut rhs) } - fn merge(&self, place: StateAddr, rhs: StateAddr) -> Result<()> { - let rhs = rhs.get::(); + + fn merge_states(&self, place: StateAddr, rhs: StateAddr) -> Result<()> { let state = place.get::(); - state.merge(rhs) + let other = rhs.get::(); + state.merge(other) } + fn merge_result(&self, place: StateAddr, builder: &mut ColumnBuilder) -> Result<()> { let state = place.get::(); state.merge_result(builder, self.levels.clone()) diff --git a/src/query/functions/src/aggregates/aggregate_retention.rs b/src/query/functions/src/aggregates/aggregate_retention.rs index 4ea9fb645a60..bb7a1ea55f58 100644 --- a/src/query/functions/src/aggregates/aggregate_retention.rs +++ b/src/query/functions/src/aggregates/aggregate_retention.rs @@ -27,6 +27,8 @@ use common_expression::Column; use common_expression::ColumnBuilder; use common_expression::Scalar; use common_io::prelude::*; +use serde::Deserialize; +use serde::Serialize; use super::aggregate_function::AggregateFunction; use super::aggregate_function::AggregateFunctionRef; @@ -34,6 +36,7 @@ use super::aggregate_function_factory::AggregateFunctionDescription; use super::StateAddr; use crate::aggregates::aggregator_common::assert_variadic_arguments; +#[derive(Serialize, Deserialize)] struct AggregateRetentionState { pub events: u32, } @@ -47,15 +50,6 @@ impl AggregateRetentionState { fn merge(&mut self, other: &Self) { self.events |= other.events; } - - fn serialize(&self, writer: &mut Vec) -> Result<()> { - serialize_into_buf(writer, &self.events) - } - - fn deserialize(&mut self, reader: &mut &[u8]) -> Result<()> { - self.events = deserialize_from_slice(reader)?; - Ok(()) - } } #[derive(Clone)] @@ -144,18 +138,20 @@ impl AggregateFunction for AggregateRetentionFunction { fn serialize(&self, place: StateAddr, writer: &mut Vec) -> Result<()> { let state = place.get::(); - state.serialize(writer) + serialize_into_buf(writer, state) } - fn deserialize(&self, place: StateAddr, reader: &mut &[u8]) -> Result<()> { + fn merge(&self, place: StateAddr, reader: &mut &[u8]) -> Result<()> { let state = place.get::(); - state.deserialize(reader) + let rhs: AggregateRetentionState = deserialize_from_slice(reader)?; + state.merge(&rhs); + Ok(()) } - fn merge(&self, place: StateAddr, rhs: StateAddr) -> Result<()> { - let rhs = rhs.get::(); + fn merge_states(&self, place: StateAddr, rhs: StateAddr) -> Result<()> { let state = place.get::(); - state.merge(rhs); + let other = rhs.get::(); + state.merge(other); Ok(()) } diff --git a/src/query/functions/src/aggregates/aggregate_scalar_state.rs b/src/query/functions/src/aggregates/aggregate_scalar_state.rs index 0ee4039da433..0f9f7c8b68d6 100644 --- a/src/query/functions/src/aggregates/aggregate_scalar_state.rs +++ b/src/query/functions/src/aggregates/aggregate_scalar_state.rs @@ -20,8 +20,6 @@ use common_exception::Result; use common_expression::types::DataType; use common_expression::types::ValueType; use common_expression::ColumnBuilder; -use common_io::prelude::deserialize_from_slice; -use common_io::prelude::serialize_into_buf; use serde::de::DeserializeOwned; use serde::Deserialize; use serde::Serialize; @@ -111,14 +109,14 @@ impl ChangeIf for CmpAny { } } -pub trait ScalarStateFunc: Send + Sync + 'static { +pub trait ScalarStateFunc: + Serialize + DeserializeOwned + Send + Sync + 'static +{ fn new() -> Self; fn add(&mut self, other: Option>); fn add_batch(&mut self, column: &T::Column, validity: Option<&Bitmap>) -> Result<()>; fn merge(&mut self, rhs: &Self) -> Result<()>; fn merge_result(&mut self, builder: &mut ColumnBuilder) -> Result<()>; - fn serialize(&self, writer: &mut Vec) -> Result<()>; - fn deserialize(&mut self, reader: &mut &[u8]) -> Result<()>; } #[derive(Serialize, Deserialize)] @@ -237,15 +235,6 @@ where } Ok(()) } - - fn serialize(&self, writer: &mut Vec) -> Result<()> { - serialize_into_buf(writer, self) - } - - fn deserialize(&mut self, reader: &mut &[u8]) -> Result<()> { - self.value = deserialize_from_slice(reader)?; - Ok(()) - } } pub fn need_manual_drop_state(data_type: &DataType) -> bool { diff --git a/src/query/functions/src/aggregates/aggregate_skewness.rs b/src/query/functions/src/aggregates/aggregate_skewness.rs index 0f3cb0969992..36984528804f 100644 --- a/src/query/functions/src/aggregates/aggregate_skewness.rs +++ b/src/query/functions/src/aggregates/aggregate_skewness.rs @@ -1,17 +1,17 @@ -// Copyright 2021 Datafuse Labs -// -// 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. - +// Copyright 2021 Datafuse Labs +// +// 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. + use std::alloc::Layout; use std::fmt::Display; use std::fmt::Formatter; @@ -197,17 +197,17 @@ where T: Number + AsPrimitive serialize_into_buf(writer, state) } - fn deserialize(&self, place: StateAddr, reader: &mut &[u8]) -> Result<()> { + fn merge(&self, place: StateAddr, reader: &mut &[u8]) -> Result<()> { let state = place.get::(); - *state = deserialize_from_slice(reader)?; - + let rhs: SkewnessState = deserialize_from_slice(reader)?; + state.merge(&rhs); Ok(()) } - fn merge(&self, place: StateAddr, rhs: StateAddr) -> Result<()> { - let rhs = rhs.get::(); + fn merge_states(&self, place: StateAddr, rhs: StateAddr) -> Result<()> { let state = place.get::(); - state.merge(rhs); + let other = rhs.get::(); + state.merge(other); Ok(()) } diff --git a/src/query/functions/src/aggregates/aggregate_stddev.rs b/src/query/functions/src/aggregates/aggregate_stddev.rs index 9a3330be09c7..b34d57aec2be 100644 --- a/src/query/functions/src/aggregates/aggregate_stddev.rs +++ b/src/query/functions/src/aggregates/aggregate_stddev.rs @@ -173,17 +173,17 @@ where T: Number + AsPrimitive serialize_into_buf(writer, state) } - fn deserialize(&self, place: StateAddr, reader: &mut &[u8]) -> Result<()> { + fn merge(&self, place: StateAddr, reader: &mut &[u8]) -> Result<()> { let state = place.get::(); - *state = deserialize_from_slice(reader)?; - + let rhs: AggregateStddevState = deserialize_from_slice(reader)?; + state.merge(&rhs); Ok(()) } - fn merge(&self, place: StateAddr, rhs: StateAddr) -> Result<()> { + fn merge_states(&self, place: StateAddr, rhs: StateAddr) -> Result<()> { let state = place.get::(); - let rhs = rhs.get::(); - state.merge(rhs); + let other = rhs.get::(); + state.merge(other); Ok(()) } diff --git a/src/query/functions/src/aggregates/aggregate_string_agg.rs b/src/query/functions/src/aggregates/aggregate_string_agg.rs index 42c094b4f251..b8a06c519ca2 100644 --- a/src/query/functions/src/aggregates/aggregate_string_agg.rs +++ b/src/query/functions/src/aggregates/aggregate_string_agg.rs @@ -126,16 +126,17 @@ impl AggregateFunction for AggregateStringAggFunction { Ok(()) } - fn deserialize(&self, place: StateAddr, reader: &mut &[u8]) -> Result<()> { + fn merge(&self, place: StateAddr, reader: &mut &[u8]) -> Result<()> { let state = place.get::(); - state.values = deserialize_from_slice(reader)?; + let rhs: StringAggState = deserialize_from_slice(reader)?; + state.values.extend_from_slice(rhs.values.as_slice()); Ok(()) } - fn merge(&self, place: StateAddr, rhs: StateAddr) -> Result<()> { - let rhs = rhs.get::(); + fn merge_states(&self, place: StateAddr, rhs: StateAddr) -> Result<()> { let state = place.get::(); - state.values.extend_from_slice(rhs.values.as_slice()); + let other = rhs.get::(); + state.values.extend_from_slice(other.values.as_slice()); Ok(()) } diff --git a/src/query/functions/src/aggregates/aggregate_sum.rs b/src/query/functions/src/aggregates/aggregate_sum.rs index 9cc668b6672b..1bf34d6a4557 100644 --- a/src/query/functions/src/aggregates/aggregate_sum.rs +++ b/src/query/functions/src/aggregates/aggregate_sum.rs @@ -39,6 +39,7 @@ use common_io::prelude::*; use ethnum::i256; use num_traits::AsPrimitive; use serde::de::DeserializeOwned; +use serde::Deserialize; use serde::Serialize; use super::aggregate_function::AggregateFunction; @@ -47,8 +48,8 @@ use super::aggregate_function_factory::AggregateFunctionDescription; use super::StateAddr; use crate::aggregates::aggregator_common::assert_unary_arguments; -pub trait SumState: Send + Sync + Default + 'static { - fn merge(&mut self, other: &mut Self) -> Result<()>; +pub trait SumState: Serialize + DeserializeOwned + Send + Sync + Default + 'static { + fn merge(&mut self, other: &Self) -> Result<()>; fn serialize(&self, writer: &mut Vec) -> Result<()>; fn deserialize(&mut self, reader: &mut &[u8]) -> Result<()>; fn accumulate(&mut self, column: &Column, validity: Option<&Bitmap>) -> Result<()>; @@ -71,9 +72,10 @@ pub trait SumState: Send + Sync + Default + 'static { ) -> Result<()>; } -#[derive(Default)] -pub struct NumberSumState { +#[derive(Default, Deserialize, Serialize)] +pub struct NumberSumState { pub value: TSum, + #[serde(skip)] _t: PhantomData, } @@ -114,7 +116,7 @@ where } #[inline(always)] - fn merge(&mut self, other: &mut Self) -> Result<()> { + fn merge(&mut self, other: &Self) -> Result<()> { self.value += other.value; Ok(()) } @@ -144,8 +146,8 @@ where } } -#[derive(Default)] -pub struct DecimalSumState { +#[derive(Default, Deserialize, Serialize)] +pub struct DecimalSumState { pub value: T, } @@ -227,7 +229,7 @@ where T: Decimal } #[inline(always)] - fn merge(&mut self, other: &mut Self) -> Result<()> { + fn merge(&mut self, other: &Self) -> Result<()> { self.add(other.value) } @@ -324,18 +326,19 @@ where State: SumState fn serialize(&self, place: StateAddr, writer: &mut Vec) -> Result<()> { let state = place.get::(); - state.serialize(writer) + serialize_into_buf(writer, state) } - fn deserialize(&self, place: StateAddr, reader: &mut &[u8]) -> Result<()> { + fn merge(&self, place: StateAddr, reader: &mut &[u8]) -> Result<()> { let state = place.get::(); - state.deserialize(reader) + let rhs: State = deserialize_from_slice(reader)?; + state.merge(&rhs) } - fn merge(&self, place: StateAddr, rhs: StateAddr) -> Result<()> { - let rhs = rhs.get::(); + fn merge_states(&self, place: StateAddr, rhs: StateAddr) -> Result<()> { let state = place.get::(); - state.merge(rhs) + let other = rhs.get::(); + state.merge(other) } #[allow(unused_mut)] diff --git a/src/query/functions/src/aggregates/aggregate_window_funnel.rs b/src/query/functions/src/aggregates/aggregate_window_funnel.rs index b710255df07e..d709f0fdc664 100644 --- a/src/query/functions/src/aggregates/aggregate_window_funnel.rs +++ b/src/query/functions/src/aggregates/aggregate_window_funnel.rs @@ -147,16 +147,6 @@ where T: Ord self.events_list.sort_by(cmp); } } - - fn serialize(&self, writer: &mut Vec) -> Result<()> { - serialize_into_buf(writer, self) - } - - fn deserialize(&mut self, reader: &mut &[u8]) -> Result<()> { - *self = deserialize_from_slice(reader)?; - - Ok(()) - } } #[derive(Clone)] @@ -287,19 +277,20 @@ where fn serialize(&self, place: StateAddr, writer: &mut Vec) -> Result<()> { let state = place.get::>(); - AggregateWindowFunnelState::::serialize(state, writer) + serialize_into_buf(writer, state) } - fn deserialize(&self, place: StateAddr, reader: &mut &[u8]) -> Result<()> { + fn merge(&self, place: StateAddr, reader: &mut &[u8]) -> Result<()> { let state = place.get::>(); - state.deserialize(reader) + let mut rhs: AggregateWindowFunnelState = deserialize_from_slice(reader)?; + state.merge(&mut rhs); + Ok(()) } - fn merge(&self, place: StateAddr, rhs: StateAddr) -> Result<()> { - let rhs = rhs.get::>(); + fn merge_states(&self, place: StateAddr, rhs: StateAddr) -> Result<()> { let state = place.get::>(); - - state.merge(rhs); + let other = rhs.get::>(); + state.merge(other); Ok(()) } diff --git a/src/query/service/src/pipelines/processors/transforms/aggregator/aggregate_cell.rs b/src/query/service/src/pipelines/processors/transforms/aggregator/aggregate_cell.rs index 0245729e701c..a9b37089805f 100644 --- a/src/query/service/src/pipelines/processors/transforms/aggregator/aggregate_cell.rs +++ b/src/query/service/src/pipelines/processors/transforms/aggregator/aggregate_cell.rs @@ -32,7 +32,6 @@ pub struct HashTableCell { pub hashtable: T::HashTable, pub arena: Area, pub arena_holders: Vec, - pub temp_values: Vec< as HashtableLike>::Value>, pub _dropper: Option>>, } @@ -44,10 +43,6 @@ impl Drop for HashTableCell fn drop(&mut self) { if let Some(dropper) = self._dropper.take() { dropper.destroy(&mut self.hashtable); - - for value in &self.temp_values { - dropper.destroy_value(value) - } } } } @@ -60,7 +55,6 @@ impl HashTableCell { HashTableCell:: { hashtable: inner, arena_holders: vec![], - temp_values: vec![], _dropper: Some(_dropper), arena: Area::create(), } diff --git a/src/query/service/src/pipelines/processors/transforms/aggregator/aggregate_meta.rs b/src/query/service/src/pipelines/processors/transforms/aggregator/aggregate_meta.rs index 57f3e4309d0d..e391f730b202 100644 --- a/src/query/service/src/pipelines/processors/transforms/aggregator/aggregate_meta.rs +++ b/src/query/service/src/pipelines/processors/transforms/aggregator/aggregate_meta.rs @@ -21,7 +21,6 @@ use common_expression::BlockMetaInfoPtr; use common_expression::Column; use common_expression::DataBlock; -use crate::pipelines::processors::transforms::group_by::ArenaHolder; use crate::pipelines::processors::transforms::group_by::HashMethodBounds; use crate::pipelines::processors::transforms::group_by::PartitionedHashMethod; use crate::pipelines::processors::transforms::HashTableCell; @@ -29,7 +28,6 @@ use crate::pipelines::processors::transforms::HashTableCell; pub struct HashTablePayload { pub bucket: isize, pub cell: HashTableCell, - pub arena_holder: ArenaHolder, } pub struct SerializedPayload { @@ -66,7 +64,6 @@ impl AggregateMeta::HashTable(HashTablePayload { cell, bucket, - arena_holder: ArenaHolder::create(None), })) } @@ -83,7 +80,6 @@ impl AggregateMeta::Spilling(HashTablePayload { cell, bucket: 0, - arena_holder: ArenaHolder::create(None), })) } diff --git a/src/query/service/src/pipelines/processors/transforms/aggregator/transform_aggregate_final.rs b/src/query/service/src/pipelines/processors/transforms/aggregator/transform_aggregate_final.rs index d738ed0905e9..e252df4b4e7b 100644 --- a/src/query/service/src/pipelines/processors/transforms/aggregator/transform_aggregate_final.rs +++ b/src/query/service/src/pipelines/processors/transforms/aggregator/transform_aggregate_final.rs @@ -71,8 +71,6 @@ where Method: HashMethodBounds let hashtable = self.method.create_hash_table::(arena)?; let _dropper = AggregateHashTableDropper::create(self.params.clone()); let mut hash_cell = HashTableCell::::create(hashtable, _dropper); - let temp_place = self.params.alloc_layout(&mut hash_cell.arena); - hash_cell.temp_values.push(temp_place.addr()); for bucket_data in data { match bucket_data { @@ -155,20 +153,10 @@ where Method: HashMethodBounds for (idx, aggregate_function) in aggregate_functions.iter().enumerate() { let final_place = place.next(offsets_aggregate_states[idx]); - let state_place = temp_place.next(offsets_aggregate_states[idx]); let mut data = unsafe { states_binary_columns[idx].index_unchecked(*row) }; - aggregate_function.deserialize(state_place, &mut data)?; - aggregate_function.merge(final_place, state_place)?; - if aggregate_function.need_manual_drop_state() { - unsafe { - // State may allocate memory out of the arena, - // drop state to avoid memory leak. - aggregate_function.drop_state(state_place); - } - aggregate_function.init_state(state_place); - } + aggregate_function.merge(final_place, &mut data)?; } } } @@ -193,7 +181,7 @@ where Method: HashMethodBounds { let final_place = place.next(offsets_aggregate_states[idx]); let state_place = old_place.next(offsets_aggregate_states[idx]); - aggregate_function.merge(final_place, state_place)?; + aggregate_function.merge_states(final_place, state_place)?; } } }, diff --git a/src/query/service/src/pipelines/processors/transforms/aggregator/transform_aggregate_partial.rs b/src/query/service/src/pipelines/processors/transforms/aggregator/transform_aggregate_partial.rs index 4cf479fb5ace..1bca822ea3eb 100644 --- a/src/query/service/src/pipelines/processors/transforms/aggregator/transform_aggregate_partial.rs +++ b/src/query/service/src/pipelines/processors/transforms/aggregator/transform_aggregate_partial.rs @@ -110,9 +110,6 @@ pub struct TransformPartialAggregate { hash_table: HashTable, params: Arc, - - /// A temporary place to hold aggregating state from index data. - temp_place: StateAddr, } impl TransformPartialAggregate { @@ -143,7 +140,6 @@ impl TransformPartialAggregate { params, hash_table, settings: AggregateSettings::try_from(ctx)?, - temp_place: StateAddr::new(0), }, )) } @@ -220,19 +216,9 @@ impl TransformPartialAggregate { .unwrap() .as_string() .unwrap(); - let state_place = self.temp_place.next(offset); for (row, mut raw_state) in agg_state.iter().enumerate() { let place = &places[row]; - function.deserialize(state_place, &mut raw_state)?; - function.merge(place.next(offset), state_place)?; - if function.need_manual_drop_state() { - unsafe { - // State may allocate memory out of the arena, - // drop state to avoid memory leak. - function.drop_state(state_place); - } - function.init_state(state_place); - } + function.merge(place.next(offset), &mut raw_state)?; } } @@ -277,9 +263,6 @@ impl TransformPartialAggregate { } if is_agg_index_block { - if self.temp_place.addr() == 0 { - self.temp_place = self.params.alloc_layout(&mut hashtable.arena); - } self.execute_agg_index_block(&block, &places) } else { Self::execute(&self.params, &block, &places) @@ -300,9 +283,6 @@ impl TransformPartialAggregate { } if is_agg_index_block { - if self.temp_place.addr() == 0 { - self.temp_place = self.params.alloc_layout(&mut hashtable.arena); - } self.execute_agg_index_block(&block, &places) } else { Self::execute(&self.params, &block, &places) diff --git a/src/query/service/src/pipelines/processors/transforms/aggregator/transform_single_key.rs b/src/query/service/src/pipelines/processors/transforms/aggregator/transform_single_key.rs index 73499a4cdf82..c71d7f87551e 100644 --- a/src/query/service/src/pipelines/processors/transforms/aggregator/transform_single_key.rs +++ b/src/query/service/src/pipelines/processors/transforms/aggregator/transform_single_key.rs @@ -21,6 +21,7 @@ use bumpalo::Bump; use common_catalog::plan::AggIndexMeta; use common_exception::ErrorCode; use common_exception::Result; +use common_expression::types::string::StringColumn; use common_expression::types::DataType; use common_expression::BlockEntry; use common_expression::BlockMetaInfoDowncast; @@ -45,9 +46,6 @@ pub struct PartialSingleStateAggregator { places: Vec, arg_indices: Vec>, funcs: Vec, - - /// A temporary place to hold aggregating state from index data. - temp_places: Vec, } impl PartialSingleStateAggregator { @@ -66,7 +64,6 @@ impl PartialSingleStateAggregator { let place: StateAddr = arena.alloc_layout(layout).into(); let temp_place: StateAddr = arena.alloc_layout(layout).into(); let mut places = Vec::with_capacity(params.offsets_aggregate_states.len()); - let mut temp_places = Vec::with_capacity(params.offsets_aggregate_states.len()); for (idx, func) in params.aggregate_functions.iter().enumerate() { let arg_place = place.next(params.offsets_aggregate_states[idx]); @@ -75,7 +72,6 @@ impl PartialSingleStateAggregator { let state_place = temp_place.next(params.offsets_aggregate_states[idx]); func.init_state(state_place); - temp_places.push(state_place); } Ok(AccumulatingTransformer::create( @@ -86,7 +82,6 @@ impl PartialSingleStateAggregator { places, funcs: params.aggregate_functions.clone(), arg_indices: params.aggregate_functions_arguments.clone(), - temp_places, }, )) } @@ -127,18 +122,8 @@ impl AccumulatingTransform for PartialSingleStateAggregator { .unwrap() .as_string() .unwrap(); - let state_place = self.temp_places[idx]; for (_, mut raw_state) in agg_state.iter().enumerate() { - func.deserialize(state_place, &mut raw_state)?; - func.merge(place, state_place)?; - if func.need_manual_drop_state() { - unsafe { - // State may allocate memory out of the arena, - // drop state to avoid memory leak. - func.drop_state(state_place); - } - func.init_state(state_place); - } + func.merge(place, &mut raw_state)?; } } else { func.accumulate(place, &arg_columns, None, block.num_rows())?; @@ -183,7 +168,7 @@ impl AccumulatingTransform for PartialSingleStateAggregator { pub struct FinalSingleStateAggregator { arena: Bump, layout: Layout, - to_merge_places: Vec>, + to_merge_data: Vec>, funcs: Vec, offsets_aggregate_states: Vec, } @@ -208,7 +193,7 @@ impl FinalSingleStateAggregator { arena, layout, funcs: params.aggregate_functions.clone(), - to_merge_places: vec![vec![]; params.aggregate_functions.len()], + to_merge_data: vec![vec![]; params.aggregate_functions.len()], offsets_aggregate_states: params.offsets_aggregate_states.clone(), }, )) @@ -234,17 +219,14 @@ impl AccumulatingTransform for FinalSingleStateAggregator { fn transform(&mut self, block: DataBlock) -> Result> { if !block.is_empty() { let block = block.convert_to_full(); - let places = self.new_places(); - for (index, func) in self.funcs.iter().enumerate() { + for (index, _) in self.funcs.iter().enumerate() { let binary_array = block.get_by_offset(index).value.as_column().unwrap(); let binary_array = binary_array.as_string().ok_or_else(|| { ErrorCode::IllegalDataType("binary array should be string type") })?; - let mut data = unsafe { binary_array.index_unchecked(0) }; - func.deserialize(places[index], &mut data)?; - self.to_merge_places[index].push(places[index]); + self.to_merge_data[index].push(binary_array.clone()); } } @@ -268,8 +250,9 @@ impl AccumulatingTransform for FinalSingleStateAggregator { for (index, func) in self.funcs.iter().enumerate() { let main_place = main_places[index]; - for place in self.to_merge_places[index].iter() { - func.merge(main_place, *place)?; + for col in self.to_merge_data[index].iter() { + let mut data = unsafe { col.index_unchecked(0) }; + func.merge(main_place, &mut data)?; } let array = aggr_values[index].borrow_mut(); @@ -291,14 +274,6 @@ impl AccumulatingTransform for FinalSingleStateAggregator { generate_data_block = vec![DataBlock::new_from_columns(columns)]; } - for (places, func) in self.to_merge_places.iter().zip(self.funcs.iter()) { - if func.need_manual_drop_state() { - for place in places { - unsafe { func.drop_state(*place) } - } - } - } - Ok(generate_data_block) } } diff --git a/src/query/service/src/pipelines/processors/transforms/group_by/aggregator_polymorphic_keys.rs b/src/query/service/src/pipelines/processors/transforms/group_by/aggregator_polymorphic_keys.rs index 19f96812298c..67b26e9169d4 100644 --- a/src/query/service/src/pipelines/processors/transforms/group_by/aggregator_polymorphic_keys.rs +++ b/src/query/service/src/pipelines/processors/transforms/group_by/aggregator_polymorphic_keys.rs @@ -582,7 +582,6 @@ impl PartitionedHashMethod { let arena = std::mem::replace(&mut cell.arena, Area::create()); cell.arena_holders.push(ArenaHolder::create(Some(arena))); - let temp_values = cell.temp_values.to_vec(); let arena_holders = cell.arena_holders.to_vec(); let _old_dropper = cell._dropper.clone().unwrap(); @@ -594,7 +593,6 @@ impl PartitionedHashMethod { // create new HashTableCell before take_old_dropper - may double free memory let _old_dropper = cell._dropper.take(); let mut cell = HashTableCell::create(partitioned_hashtable, _new_dropper); - cell.temp_values = temp_values; cell.arena_holders = arena_holders; Ok(cell) } From 35952e8cdbc987cf81cf7dc77012bb96ef0d7b56 Mon Sep 17 00:00:00 2001 From: Yijun Zhao Date: Wed, 11 Oct 2023 12:14:56 +0800 Subject: [PATCH 18/25] feat: generate sync agg indexes after copy into (#13152) * generate sync agg indexes after copy into * fix reviewer comments --- .../it/aggregating_index/index_refresh.rs | 106 +++++++++++++++--- .../common/refresh_aggregating_index.rs | 15 ++- .../interpreter_copy_into_table.rs | 19 ++++ .../src/interpreters/interpreter_insert.rs | 41 +++---- 4 files changed, 138 insertions(+), 43 deletions(-) diff --git a/src/query/ee/tests/it/aggregating_index/index_refresh.rs b/src/query/ee/tests/it/aggregating_index/index_refresh.rs index 36ec168fcf8a..a8d5a8ca325a 100644 --- a/src/query/ee/tests/it/aggregating_index/index_refresh.rs +++ b/src/query/ee/tests/it/aggregating_index/index_refresh.rs @@ -305,17 +305,23 @@ async fn test_refresh_agg_index_with_limit() -> Result<()> { Ok(()) } -#[ignore = "flaky"] #[tokio::test(flavor = "multi_thread")] async fn test_sync_agg_index() -> Result<()> { + test_sync_agg_index_after_insert().await?; + test_sync_agg_index_after_copy_into().await?; + + Ok(()) +} + +async fn test_sync_agg_index_after_insert() -> Result<()> { let (_guard, ctx, root) = create_ee_query_context(None).await.unwrap(); ctx.get_settings() .set_enable_refresh_aggregating_index_after_write(true)?; let fixture = TestFixture::new_with_ctx(_guard, ctx).await; - + let ctx = fixture.ctx(); // Create table execute_sql( - fixture.ctx(), + ctx.clone(), "CREATE TABLE t0 (a int, b int, c int) storage_format = 'parquet'", ) .await?; @@ -324,7 +330,7 @@ async fn test_sync_agg_index() -> Result<()> { let index_name = "index0"; let index_id0 = create_index( - fixture.ctx(), + ctx.clone(), index_name, "SELECT b, SUM(a) from t0 WHERE c > 1 GROUP BY b", true, @@ -335,7 +341,7 @@ async fn test_sync_agg_index() -> Result<()> { let index_name = "index1"; let index_id1 = create_index( - fixture.ctx(), + ctx.clone(), index_name, "SELECT a, SUM(b) from t0 WHERE c > 1 GROUP BY a", true, @@ -344,7 +350,7 @@ async fn test_sync_agg_index() -> Result<()> { // Insert data execute_sql( - fixture.ctx(), + ctx.clone(), "INSERT INTO t0 VALUES (1,1,4), (1,2,1), (1,2,4), (2,2,5)", ) .await?; @@ -365,14 +371,14 @@ async fn test_sync_agg_index() -> Result<()> { // Check aggregating index_0 is correct. { let res = execute_sql( - fixture.ctx(), + ctx.clone(), "SELECT b, SUM_STATE(a) from t0 WHERE c > 1 GROUP BY b", ) .await?; let data_blocks: Vec = res.try_collect().await?; let agg_res = execute_sql( - fixture.ctx(), + ctx.clone(), &format!( "SELECT * FROM 'fs://{}'", agg_index_path_0.join(&indexes_0[0]).to_str().unwrap() @@ -381,20 +387,24 @@ async fn test_sync_agg_index() -> Result<()> { .await?; let agg_data_blocks: Vec = agg_res.try_collect().await?; - assert_two_blocks_sorted_eq_with_name("refresh index", &data_blocks, &agg_data_blocks); + assert_two_blocks_sorted_eq_with_name( + "test_sync_agg_index_after_insert", + &data_blocks, + &agg_data_blocks, + ); } // Check aggregating index_1 is correct. { let res = execute_sql( - fixture.ctx(), + ctx.clone(), "SELECT a, SUM_STATE(b) from t0 WHERE c > 1 GROUP BY a", ) .await?; let data_blocks: Vec = res.try_collect().await?; let agg_res = execute_sql( - fixture.ctx(), + ctx.clone(), &format!( "SELECT * FROM 'fs://{}'", agg_index_path_1.join(&indexes_1[0]).to_str().unwrap() @@ -403,11 +413,15 @@ async fn test_sync_agg_index() -> Result<()> { .await?; let agg_data_blocks: Vec = agg_res.try_collect().await?; - assert_two_blocks_sorted_eq_with_name("refresh index", &data_blocks, &agg_data_blocks); + assert_two_blocks_sorted_eq_with_name( + "test_sync_agg_index_after_insert", + &data_blocks, + &agg_data_blocks, + ); } // Insert more data with insert into ... select ... - execute_sql(fixture.ctx(), "INSERT INTO t0 SELECT * FROM t0").await?; + execute_sql(ctx.clone(), "INSERT INTO t0 SELECT * FROM t0").await?; let blocks = collect_file_names(&block_path)?; @@ -422,6 +436,72 @@ async fn test_sync_agg_index() -> Result<()> { Ok(()) } +async fn test_sync_agg_index_after_copy_into() -> Result<()> { + let (_guard, ctx, root) = create_ee_query_context(None).await.unwrap(); + ctx.get_settings() + .set_enable_refresh_aggregating_index_after_write(true)?; + let fixture = TestFixture::new_with_ctx(_guard, ctx).await; + let ctx = fixture.ctx(); + + // Create table + execute_sql( + ctx.clone(), + "CREATE TABLE books (title VARCHAR, author VARCHAR, date VARCHAR) storage_format = 'parquet'", + ) + .await?; + + // Create agg index `index0` + let index_name = "index0"; + + let index_id0 = create_index( + ctx.clone(), + index_name, + "SELECT MAX(title) from books", + true, + ) + .await?; + + // Copy into data + execute_sql( + ctx.clone(), + "COPY INTO books FROM 'https://datafuse-1253727613.cos.ap-hongkong.myqcloud.com/data/books.csv' FILE_FORMAT = (TYPE = CSV);", + ) + .await?; + + let block_path = find_block_path(&root)?.unwrap(); + let blocks = collect_file_names(&block_path)?; + + // Get aggregating index files + let agg_index_path_0 = find_agg_index_path(&root, index_id0)?.unwrap(); + let indexes_0 = collect_file_names(&agg_index_path_0)?; + + assert_eq!(blocks, indexes_0); + + // Check aggregating index_0 is correct. + { + let res = execute_sql(ctx.clone(), "SELECT MAX_STATE(title) from books").await?; + let data_blocks: Vec = res.try_collect().await?; + + let agg_res = execute_sql( + ctx.clone(), + &format!( + "SELECT * FROM 'fs://{}'", + agg_index_path_0.join(&indexes_0[0]).to_str().unwrap() + ), + ) + .await?; + let agg_data_blocks: Vec = agg_res.try_collect().await?; + + assert_two_blocks_sorted_eq_with_name( + "test_sync_agg_index_after_copy_into", + &data_blocks, + &agg_data_blocks, + ); + } + + Ok(()) +} + fn find_block_path>(dir: P) -> Result> { find_target_path(dir, "_b") } diff --git a/src/query/service/src/interpreters/common/refresh_aggregating_index.rs b/src/query/service/src/interpreters/common/refresh_aggregating_index.rs index a03033cdb428..1f6fd445f98e 100644 --- a/src/query/service/src/interpreters/common/refresh_aggregating_index.rs +++ b/src/query/service/src/interpreters/common/refresh_aggregating_index.rs @@ -20,6 +20,7 @@ use common_catalog::table_context::TableContext; use common_exception::Result; use common_meta_app::schema::IndexMeta; use common_meta_app::schema::ListIndexesByIdReq; +use common_meta_types::MetaId; use common_pipeline_core::Pipeline; use common_sql::plans::RefreshIndexPlan; use common_sql::BindContext; @@ -40,7 +41,6 @@ pub struct RefreshAggIndexDesc { pub catalog: String, pub database: String, pub table: String, - pub table_id: u64, } pub async fn hook_refresh_agg_index( @@ -75,15 +75,16 @@ pub async fn hook_refresh_agg_index( async fn generate_refresh_index_plan( ctx: Arc, - desc: RefreshAggIndexDesc, + catalog: &str, + table_id: MetaId, ) -> Result> { let segment_locs = ctx.get_segment_locations()?; - let catalog = ctx.get_catalog(&desc.catalog).await?; + let catalog = ctx.get_catalog(catalog).await?; let mut plans = vec![]; let indexes = catalog .list_indexes_by_table_id(ListIndexesByIdReq { tenant: ctx.get_tenant(), - table_id: desc.table_id, + table_id, }) .await?; @@ -139,7 +140,11 @@ async fn build_refresh_index_plan( } async fn refresh_agg_index(ctx: Arc, desc: RefreshAggIndexDesc) -> Result<()> { - let plans = generate_refresh_index_plan(ctx.clone(), desc).await?; + let table_id = ctx + .get_table(&desc.catalog, &desc.database, &desc.table) + .await? + .get_id(); + let plans = generate_refresh_index_plan(ctx.clone(), &desc.catalog, table_id).await?; let mut tasks = Vec::with_capacity(std::cmp::min( ctx.get_settings().get_max_threads()? as usize, plans.len(), diff --git a/src/query/service/src/interpreters/interpreter_copy_into_table.rs b/src/query/service/src/interpreters/interpreter_copy_into_table.rs index 6482cd552800..fb0deac3bcf7 100644 --- a/src/query/service/src/interpreters/interpreter_copy_into_table.rs +++ b/src/query/service/src/interpreters/interpreter_copy_into_table.rs @@ -41,8 +41,10 @@ use log::info; use crate::interpreters::common::check_deduplicate_label; use crate::interpreters::common::hook_compact; +use crate::interpreters::common::hook_refresh_agg_index; use crate::interpreters::common::CompactHookTraceCtx; use crate::interpreters::common::CompactTargetTableDescription; +use crate::interpreters::common::RefreshAggIndexDesc; use crate::interpreters::Interpreter; use crate::interpreters::SelectInterpreter; use crate::pipelines::builders::build_commit_data_pipeline; @@ -302,6 +304,23 @@ impl Interpreter for CopyIntoTableInterpreter { trace_ctx, ) .await; + + // generate sync aggregating indexes if `enable_refresh_aggregating_index_after_write` on. + { + let refresh_agg_index_desc = RefreshAggIndexDesc { + catalog: self.plan.catalog_info.name_ident.catalog_name.clone(), + database: self.plan.database_name.clone(), + table: self.plan.table_name.clone(), + }; + + hook_refresh_agg_index( + self.ctx.clone(), + &mut build_res.main_pipeline, + refresh_agg_index_desc, + ) + .await?; + } + Ok(build_res) } diff --git a/src/query/service/src/interpreters/interpreter_insert.rs b/src/query/service/src/interpreters/interpreter_insert.rs index 42cbba492021..b87ec0cf069a 100644 --- a/src/query/service/src/interpreters/interpreter_insert.rs +++ b/src/query/service/src/interpreters/interpreter_insert.rs @@ -16,12 +16,10 @@ use std::str::FromStr; use std::sync::Arc; use common_catalog::table::AppendMode; -use common_catalog::table::Table; use common_exception::ErrorCode; use common_exception::Result; use common_expression::DataSchema; use common_meta_app::principal::StageFileFormatType; -use common_pipeline_core::Pipeline; use common_pipeline_sources::AsyncSourcer; use common_sql::executor::DistributedInsertSelect; use common_sql::executor::PhysicalPlan; @@ -239,11 +237,16 @@ impl Interpreter for InsertInterpreter { None, )?; - hook_refresh_indexes( + let refresh_agg_index_desc = RefreshAggIndexDesc { + catalog: self.plan.catalog.clone(), + database: self.plan.database.clone(), + table: self.plan.table.clone(), + }; + + hook_refresh_agg_index( self.ctx.clone(), - &self.plan, - table.as_ref(), &mut build_res.main_pipeline, + refresh_agg_index_desc, ) .await?; @@ -267,31 +270,19 @@ impl Interpreter for InsertInterpreter { append_mode, )?; - hook_refresh_indexes( + let refresh_agg_index_desc = RefreshAggIndexDesc { + catalog: self.plan.catalog.clone(), + database: self.plan.database.clone(), + table: self.plan.table.clone(), + }; + + hook_refresh_agg_index( self.ctx.clone(), - &self.plan, - table.as_ref(), &mut build_res.main_pipeline, + refresh_agg_index_desc, ) .await?; Ok(build_res) } } - -#[async_backtrace::framed] -async fn hook_refresh_indexes( - ctx: Arc, - plan: &Insert, - table: &dyn Table, - pipeline: &mut Pipeline, -) -> Result<()> { - let refresh_agg_index_desc = RefreshAggIndexDesc { - catalog: plan.catalog.clone(), - database: plan.database.clone(), - table: plan.table.clone(), - table_id: table.get_id(), - }; - - hook_refresh_agg_index(ctx, pipeline, refresh_agg_index_desc).await -} From 048da2e418d9503e290ade6cd717d45b8aa983ac Mon Sep 17 00:00:00 2001 From: everpcpc Date: Wed, 11 Oct 2023 12:15:54 +0800 Subject: [PATCH 19/25] chore(ci): fix dev checks for merge (#13194) --- .github/workflows/dev.yml | 60 ++++++++++++++--- .github/workflows/docs.yml | 66 ------------------- .github/workflows/reuse.linux.yml | 1 - .../tests/it/aggregating_index/index_scan.rs | 7 -- .../it/servers/http/http_query_handlers.rs | 2 - 5 files changed, 51 insertions(+), 85 deletions(-) delete mode 100644 .github/workflows/docs.yml diff --git a/.github/workflows/dev.yml b/.github/workflows/dev.yml index 01d48aa938ea..b5cabfd7fa72 100644 --- a/.github/workflows/dev.yml +++ b/.github/workflows/dev.yml @@ -8,14 +8,6 @@ on: - reopened branches: - main - paths-ignore: - - ".github/**" - - "docs/**" - - "website/**" - - "**.md" - - "docker/**" - - "scripts/setup/**" - - ".devcontainer/**" merge_group: concurrency: @@ -23,7 +15,38 @@ concurrency: cancel-in-progress: true jobs: + changes: + runs-on: ubuntu-latest + outputs: + any_src_changed: ${{ steps.src.outputs.any_changed }} + steps: + - uses: actions/checkout@v4 + - name: Check Source File Changes + uses: tj-actions/changed-files@v39 + id: src + with: + files_ignore: | + .github/** + docs/** + website/** + **.md + docker/** + scripts/setup/** + .devcontainer/** + - name: Output Source File Changes + run: | + if [[ "${{ steps.src.outputs.any_changed }}" == "true" ]]; then + echo "these src files changed:" >> $GITHUB_STEP_SUMMARY + for line in ${{ steps.src.outputs.all_changed_files }}; do + echo "- $line" >> $GITHUB_STEP_SUMMARY + done + else + echo "no src file changes detected" >> $GITHUB_STEP_SUMMARY + fi + linux: + needs: changes + if: needs.changes.outputs.any_src_changed == 'true' uses: ./.github/workflows/reuse.linux.yml secrets: inherit with: @@ -31,6 +54,8 @@ jobs: runner_provider: gcp linux_hive: + needs: changes + if: needs.changes.outputs.any_src_changed == 'true' uses: ./.github/workflows/reuse.linux.hive.yml secrets: inherit with: @@ -38,9 +63,26 @@ jobs: runner_provider: gcp ready: + if: always() runs-on: ubuntu-latest needs: + - changes - linux - linux_hive steps: - - run: echo "ready" + - name: Check Ready to Merge + uses: actions/github-script@v6 + env: + SRC_CHANGED: ${{ needs.changes.outputs.any_src_changed }} + LINUX_BUILD_RESULT: ${{ needs.linux.result }} + with: + script: | + if (process.env.SRC_CHANGED == 'false') { + core.info('No source file changes detected, skipping'); + return; + } + if (process.env.LINUX_BUILD_RESULT == 'success') { + core.info('Linux build succeeded, ready to merge'); + return; + } + core.setFailed('Build failed, not ready to merge'); diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml deleted file mode 100644 index 0df47a6ca4ef..000000000000 --- a/.github/workflows/docs.yml +++ /dev/null @@ -1,66 +0,0 @@ -name: "Docs" - -on: - pull_request: - branches: - - main - paths: - - ".github/**" - - "docs/**" - - "website/**" - - "**.md" - - "docker/**" - - "scripts/setup/**" - - ".devcontainer/**" - merge_group: - -jobs: - check: - runs-on: ubuntu-latest - outputs: - any_docs_changed: ${{ steps.docs.outputs.any_changed }} - any_other_changed: ${{ steps.other.outputs.any_changed }} - steps: - - uses: actions/checkout@v4 - - name: Check Docs Changes - id: docs - uses: tj-actions/changed-files@v39 - with: - files: | - .github/** - docs/** - website/** - **.md - docker/** - scripts/setup/** - .devcontainer/** - - name: Check Other File Changes - uses: tj-actions/changed-files@v39 - id: other - with: - files_ignore: | - .github/** - docs/** - website/** - **.md - docker/** - scripts/setup/** - .devcontainer/** - - name: Output Other File Changes - run: | - echo "**version update for docs detected**" >> $GITHUB_STEP_SUMMARY - if [[ "${{ steps.other.outputs.any_changed }}" == "true" ]]; then - echo "these files should not be changed for docs:" >> $GITHUB_STEP_SUMMARY - for line in ${{ steps.other.outputs.all_changed_files }}; do - echo "- $line" >> $GITHUB_STEP_SUMMARY - done - else - echo "no other file changes detected" >> $GITHUB_STEP_SUMMARY - fi - - ready: - runs-on: ubuntu-latest - needs: check - if: ${{ needs.check.outputs.any_docs_changed == 'true' && needs.check.outputs.any_other_changed == 'false' }} - steps: - - run: echo "ready to merge docs only changes" diff --git a/.github/workflows/reuse.linux.yml b/.github/workflows/reuse.linux.yml index 808fa3b0cc82..8459a5b77130 100644 --- a/.github/workflows/reuse.linux.yml +++ b/.github/workflows/reuse.linux.yml @@ -304,7 +304,6 @@ jobs: dirs: - "ydb" - "tpcds" - # FIXME: flaky # - "tpch" - "cluster" steps: diff --git a/src/query/ee/tests/it/aggregating_index/index_scan.rs b/src/query/ee/tests/it/aggregating_index/index_scan.rs index 14192d39bb90..9316863ac46e 100644 --- a/src/query/ee/tests/it/aggregating_index/index_scan.rs +++ b/src/query/ee/tests/it/aggregating_index/index_scan.rs @@ -32,21 +32,18 @@ use databend_query::test_kits::TestFixture; use enterprise_query::test_kits::context::create_ee_query_context; use futures_util::TryStreamExt; -#[ignore = "flaky"] #[tokio::test(flavor = "multi_thread")] async fn test_index_scan() -> Result<()> { test_index_scan_impl("parquet").await?; test_index_scan_impl("native").await } -#[ignore = "flaky"] #[tokio::test(flavor = "multi_thread")] async fn test_index_scan_two_agg_funcs() -> Result<()> { test_index_scan_two_agg_funcs_impl("parquet").await?; test_index_scan_two_agg_funcs_impl("native").await } -#[ignore = "flaky"] #[tokio::test(flavor = "multi_thread")] async fn test_projected_index_scan() -> Result<()> { test_projected_index_scan_impl("parquet").await?; @@ -59,14 +56,12 @@ async fn test_index_scan_with_count() -> Result<()> { test_index_scan_with_count_impl("native").await } -#[ignore = "flaky"] #[tokio::test(flavor = "multi_thread")] async fn test_index_scan_agg_args_are_expression() -> Result<()> { test_index_scan_agg_args_are_expression_impl("parquet").await?; test_index_scan_agg_args_are_expression_impl("native").await } -#[ignore = "flaky"] #[tokio::test(flavor = "multi_thread")] async fn test_fuzz() -> Result<()> { test_fuzz_impl("parquet").await?; @@ -265,7 +260,6 @@ async fn test_index_scan_impl(format: &str) -> Result<()> { Ok(()) } -#[ignore = "flaky"] async fn test_index_scan_two_agg_funcs_impl(format: &str) -> Result<()> { let (_guard, ctx, _) = create_ee_query_context(None).await.unwrap(); let fixture = TestFixture::new_with_ctx(_guard, ctx).await; @@ -406,7 +400,6 @@ async fn test_index_scan_two_agg_funcs_impl(format: &str) -> Result<()> { Ok(()) } -#[ignore = "flaky"] async fn test_projected_index_scan_impl(format: &str) -> Result<()> { let (_guard, ctx, _) = create_ee_query_context(None).await.unwrap(); let fixture = TestFixture::new_with_ctx(_guard, ctx).await; diff --git a/src/query/service/tests/it/servers/http/http_query_handlers.rs b/src/query/service/tests/it/servers/http/http_query_handlers.rs index 82212c9353b1..5ef40a3edde6 100644 --- a/src/query/service/tests/it/servers/http/http_query_handlers.rs +++ b/src/query/service/tests/it/servers/http/http_query_handlers.rs @@ -111,7 +111,6 @@ async fn check_final(ep: &EndpointType, final_uri: &str) -> Result<()> { Ok(()) } -#[ignore = "flaky"] #[tokio::test(flavor = "current_thread")] async fn test_simple_sql() -> Result<()> { let _guard = TestGlobalServices::setup(ConfigBuilder::create().build()).await?; @@ -180,7 +179,6 @@ async fn test_simple_sql() -> Result<()> { Ok(()) } -#[ignore = "flaky"] #[tokio::test(flavor = "current_thread")] async fn test_show_databases() -> Result<()> { let _guard = TestGlobalServices::setup(ConfigBuilder::create().build()).await?; From 4e3ed5a7d0ada2c3263966c64ec8b4fccd28955d Mon Sep 17 00:00:00 2001 From: soyeric128 Date: Wed, 11 Oct 2023 12:34:10 +0800 Subject: [PATCH 20/25] Create toweekofyear.md (#13195) --- .../30-datetime-functions/toweekofyear.md | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 docs/doc/15-sql-functions/30-datetime-functions/toweekofyear.md diff --git a/docs/doc/15-sql-functions/30-datetime-functions/toweekofyear.md b/docs/doc/15-sql-functions/30-datetime-functions/toweekofyear.md new file mode 100644 index 000000000000..fd8fdd6baabc --- /dev/null +++ b/docs/doc/15-sql-functions/30-datetime-functions/toweekofyear.md @@ -0,0 +1,42 @@ +--- +title: TO_WEEK_OF_YEAR +--- +import FunctionDescription from '@site/src/components/FunctionDescription'; + + + +Calculates the week number within a year for a given date. + +ISO week numbering works as follows: January 4th is always considered part of the first week. If January 1st is a Thursday, then the week that spans from Monday, December 29th, to Sunday, January 4th, is designated as ISO week 1. If January 1st falls on a Friday, then the week that goes from Monday, January 4th, to Sunday, January 10th, is marked as ISO week 1. + +## Syntax + +```sql +TO_WEEK_OF_YEAR() +``` + +## Arguments + +| Arguments | Description | +|-----------|----------------| +| `` | date/timestamp | + +## Return Type + +Returns an integer that represents the week number within a year, with numbering starting from 1. + +## Examples + +```sql +SELECT TO_WEEK_OF_YEAR('2017-01-01'); +---- +52 + +SELECT TO_WEEK_OF_YEAR('2016-01-02T23:39:20.123-07:00'); +---- +53 + +SELECT TO_WEEK_OF_YEAR('2023-05-01'); +---- +18 +``` From 0f1b2c22995bb7becc99e3d32dd64bf60ac6a940 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E7=82=8E=E6=B3=BC?= Date: Wed, 11 Oct 2023 13:26:09 +0800 Subject: [PATCH 21/25] refactor: move extension protobuf methods to separate file (#13191) --- src/meta/types/src/lib.rs | 73 +------------------- src/meta/types/src/proto_ext/mod.rs | 17 +++++ src/meta/types/src/proto_ext/txn_ext.rs | 90 +++++++++++++++++++++++++ 3 files changed, 108 insertions(+), 72 deletions(-) create mode 100644 src/meta/types/src/proto_ext/mod.rs create mode 100644 src/meta/types/src/proto_ext/txn_ext.rs diff --git a/src/meta/types/src/lib.rs b/src/meta/types/src/lib.rs index 2d03d9200e14..3fe197357f8d 100644 --- a/src/meta/types/src/lib.rs +++ b/src/meta/types/src/lib.rs @@ -39,6 +39,7 @@ mod seq_value; mod with; mod proto_display; +mod proto_ext; // reexport @@ -140,75 +141,3 @@ pub use crate::raft_types::TypeConfig; pub use crate::raft_types::Vote; pub use crate::raft_types::VoteRequest; pub use crate::raft_types::VoteResponse; -// pub use crate::raft_types::InitializeError; -// pub use crate::raft_types::RaftChangeMembershipError; -// pub use crate::raft_types::RaftWriteError; - -impl TxnCondition { - /// Create a txn condition that checks if the `seq` matches. - pub fn eq_seq(key: impl ToString, seq: u64) -> Self { - Self { - key: key.to_string(), - expected: ConditionResult::Eq as i32, - target: Some(txn_condition::Target::Seq(seq)), - } - } -} - -impl TxnOp { - /// Create a txn operation that puts a record. - pub fn put(key: impl ToString, value: Vec) -> TxnOp { - Self::put_with_expire(key, value, None) - } - - /// Create a txn operation that puts a record with expiration time. - pub fn put_with_expire(key: impl ToString, value: Vec, expire_at: Option) -> TxnOp { - TxnOp { - request: Some(txn_op::Request::Put(TxnPutRequest { - key: key.to_string(), - value, - prev_value: true, - expire_at, - })), - } - } - - /// Create a new `TxnOp` with a `Delete` operation. - pub fn delete(key: impl ToString) -> Self { - Self::delete_exact(key, None) - } - - /// Create a new `TxnOp` with a `Delete` operation that will be executed only when the `seq` matches. - pub fn delete_exact(key: impl ToString, seq: Option) -> Self { - TxnOp { - request: Some(txn_op::Request::Delete(TxnDeleteRequest { - key: key.to_string(), - prev_value: true, - match_seq: seq, - })), - } - } -} - -impl TxnOpResponse { - /// Create a new `TxnOpResponse` of a `Delete` operation. - pub fn delete(key: impl ToString, success: bool, prev_value: Option) -> Self { - TxnOpResponse { - response: Some(txn_op_response::Response::Delete(TxnDeleteResponse { - key: key.to_string(), - success, - prev_value, - })), - } - } - - /// Create a new `TxnOpResponse` of a `Put` operation. - pub fn put(key: impl ToString, prev_value: Option) -> Self { - TxnOpResponse { - response: Some(txn_op_response::Response::Put(TxnPutResponse { - key: key.to_string(), - prev_value, - })), - } - } -} diff --git a/src/meta/types/src/proto_ext/mod.rs b/src/meta/types/src/proto_ext/mod.rs new file mode 100644 index 000000000000..d27cd5f3eed5 --- /dev/null +++ b/src/meta/types/src/proto_ext/mod.rs @@ -0,0 +1,17 @@ +// Copyright 2021 Datafuse Labs +// +// 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. + +//! Extend protobuf generated code with some useful methods. + +mod txn_ext; diff --git a/src/meta/types/src/proto_ext/txn_ext.rs b/src/meta/types/src/proto_ext/txn_ext.rs new file mode 100644 index 000000000000..182e6e3e1cb3 --- /dev/null +++ b/src/meta/types/src/proto_ext/txn_ext.rs @@ -0,0 +1,90 @@ +// Copyright 2021 Datafuse Labs +// +// 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. + +use crate::protobuf as pb; + +impl pb::TxnCondition { + /// Create a txn condition that checks if the `seq` matches. + pub fn eq_seq(key: impl ToString, seq: u64) -> Self { + Self { + key: key.to_string(), + expected: pb::txn_condition::ConditionResult::Eq as i32, + target: Some(pb::txn_condition::Target::Seq(seq)), + } + } +} + +impl pb::TxnOp { + /// Create a txn operation that puts a record. + pub fn put(key: impl ToString, value: Vec) -> pb::TxnOp { + Self::put_with_expire(key, value, None) + } + + /// Create a txn operation that puts a record with expiration time. + pub fn put_with_expire( + key: impl ToString, + value: Vec, + expire_at: Option, + ) -> pb::TxnOp { + pb::TxnOp { + request: Some(pb::txn_op::Request::Put(pb::TxnPutRequest { + key: key.to_string(), + value, + prev_value: true, + expire_at, + })), + } + } + + /// Create a new `TxnOp` with a `Delete` operation. + pub fn delete(key: impl ToString) -> Self { + Self::delete_exact(key, None) + } + + /// Create a new `TxnOp` with a `Delete` operation that will be executed only when the `seq` matches. + pub fn delete_exact(key: impl ToString, seq: Option) -> Self { + pb::TxnOp { + request: Some(pb::txn_op::Request::Delete(pb::TxnDeleteRequest { + key: key.to_string(), + prev_value: true, + match_seq: seq, + })), + } + } +} + +impl pb::TxnOpResponse { + /// Create a new `TxnOpResponse` of a `Delete` operation. + pub fn delete(key: impl ToString, success: bool, prev_value: Option) -> Self { + pb::TxnOpResponse { + response: Some(pb::txn_op_response::Response::Delete( + pb::TxnDeleteResponse { + key: key.to_string(), + success, + prev_value, + }, + )), + } + } + + /// Create a new `TxnOpResponse` of a `Put` operation. + pub fn put(key: impl ToString, prev_value: Option) -> Self { + pb::TxnOpResponse { + response: Some(pb::txn_op_response::Response::Put(pb::TxnPutResponse { + key: key.to_string(), + prev_value, + })), + } + } +} From 7236b2eae465e9262eba462cabb51cbaff384606 Mon Sep 17 00:00:00 2001 From: soyeric128 Date: Wed, 11 Oct 2023 13:32:58 +0800 Subject: [PATCH 22/25] docs: bitmap_not + to_variant() (#13168) * Update index.md * Update index.md * Update index.md --- docs/doc/15-sql-functions/05-bitmap-functions/index.md | 4 ++-- docs/doc/15-sql-functions/60-conversion-functions/index.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/doc/15-sql-functions/05-bitmap-functions/index.md b/docs/doc/15-sql-functions/05-bitmap-functions/index.md index 00894e55efaa..faad83c9eaba 100644 --- a/docs/doc/15-sql-functions/05-bitmap-functions/index.md +++ b/docs/doc/15-sql-functions/05-bitmap-functions/index.md @@ -17,10 +17,10 @@ import FunctionDescription from '@site/src/components/FunctionDescription'; | bitmap_or(bitmap1, bitmap2) | Performs a bitwise OR operation on the two bitmaps. | bitmap_or(build_bitmap([1,4,5]), build_bitmap([6,7]))::String | 1,4,5,6,7 | | bitmap_and(bitmap1, bitmap2) | Performs a bitwise AND operation on the two bitmaps. | bitmap_and(build_bitmap([1,4,5]), build_bitmap([4,5]))::String | 4,5 | | bitmap_xor(bitmap1, bitmap2) | Performs a bitwise XOR (exclusive OR) operation on the two bitmaps. | bitmap_xor(build_bitmap([1,4,5]), build_bitmap([5,6,7]))::String | 1,4,6,7 | -| bitmap_not(bitmap1, bitmap2) | Performs a bitwise NOT operation on the bitmap with respect to another bitmap. | bitmap_not(build_bitmap([2,3]), build_bitmap([2,3,5]))::String | (empty) | +| bitmap_not(bitmap1, bitmap2) | Alias for "bitmap_and_not(bitmap1, bitmap2)". | bitmap_count(bitmap_not(build_bitmap([2,3,9]), build_bitmap([2,3,5]))) | 1 | | bitmap_intersect(bitmap) | Counts the number of bits set to 1 in the bitmap by performing a logical INTERSECT operation. | bitmap_intersect(to_bitmap('1, 3, 5'))::String | 1, 3, 5 | | bitmap_union(bitmap) | Counts the number of bits set to 1 in the bitmap by performing a logical UNION operation. | bitmap_union(to_bitmap('1, 3, 5'))::String | 1, 3, 5 | -| bitmap_and_not(bitmap1, bitmap2) | Performs a bitwise AND-NOT operation on the two bitmaps. | bitmap_and_not(build_bitmap([2,3]), build_bitmap([2,3,5]))::String | (empty) | +| bitmap_and_not(bitmap1, bitmap2) | Generates a new bitmap with elements from the first bitmap (bitmap1) that are not in the second (bitmap2). | bitmap_count(bitmap_and_not(build_bitmap([2,3,9]), build_bitmap([2,3,5]))) | 1 | | bitmap_subset_limit(bitmap, start, limit) | Generates a sub-bitmap of the source bitmap, beginning with a range from the start value, with a size limit. | bitmap_subset_limit(build_bitmap([1,4,5]), 2, 2)::String | 4,5 | | bitmap_subset_in_range(bitmap, start, end) | Generates a sub-bitmap of the source bitmap within a specified range. | bitmap_subset_in_range(build_bitmap([5,7,9]), 6, 9)::String | 7 | | sub_bitmap(bitmap, start, size) | Generates a sub-bitmap of the source bitmap, beginning from the start index, with a specified size. | sub_bitmap(build_bitmap([1, 2, 3, 4, 5]), 1, 3)::String | 2,3,4 | diff --git a/docs/doc/15-sql-functions/60-conversion-functions/index.md b/docs/doc/15-sql-functions/60-conversion-functions/index.md index 475ea3271a26..af47b6aea15f 100644 --- a/docs/doc/15-sql-functions/60-conversion-functions/index.md +++ b/docs/doc/15-sql-functions/60-conversion-functions/index.md @@ -4,7 +4,7 @@ title: 'Conversion Functions' import FunctionDescription from '@site/src/components/FunctionDescription'; - + Below is a list of functions that allow you to convert an expression from one data type to another. @@ -32,5 +32,5 @@ Databend also offers a variety of functions for converting expressions into diff | TO_UINT16( expr ) | Converts a value to UINT16 data type | TO_UINT16('123') | 123 | | TO_UINT32( expr ) | Converts a value to UINT32 data type | TO_UINT32('123') | 123 | | TO_UINT64( expr ) | Converts a value to UINT64 data type | TO_UINT64('123') | 123 | - +| TO_VARIANT( expr ) | Converts a value to VARIANT data type | TO_VARIANT(TO_BITMAP('100,200,300')) | [100,200,300] | From cf7a7847b505cc02c4cdc79040101ab820930956 Mon Sep 17 00:00:00 2001 From: Chojan Shang Date: Wed, 11 Oct 2023 13:49:32 +0800 Subject: [PATCH 23/25] docs(ekuiper): add iot stream data with ekuiper (#13188) Signed-off-by: Chojan Shang Co-authored-by: soyeric128 --- .../blog/2023-10-09-databend-and-ekuiper.md | 165 ++++++++++++++++++ .../blog/ekuiper/databend-and-ekuiper-1.PNG | Bin 0 -> 243389 bytes .../blog/ekuiper/databend-and-ekuiper-2.PNG | Bin 0 -> 13823 bytes .../blog/ekuiper/databend-and-ekuiper-3.PNG | Bin 0 -> 34678 bytes .../blog/ekuiper/databend-and-ekuiper-4.PNG | Bin 0 -> 20888 bytes .../blog/ekuiper/databend-and-ekuiper-5.PNG | Bin 0 -> 120135 bytes .../blog/ekuiper/databend-and-ekuiper-6.PNG | Bin 0 -> 32746 bytes 7 files changed, 165 insertions(+) create mode 100644 website/blog/2023-10-09-databend-and-ekuiper.md create mode 100644 website/static/img/blog/ekuiper/databend-and-ekuiper-1.PNG create mode 100644 website/static/img/blog/ekuiper/databend-and-ekuiper-2.PNG create mode 100644 website/static/img/blog/ekuiper/databend-and-ekuiper-3.PNG create mode 100644 website/static/img/blog/ekuiper/databend-and-ekuiper-4.PNG create mode 100644 website/static/img/blog/ekuiper/databend-and-ekuiper-5.PNG create mode 100644 website/static/img/blog/ekuiper/databend-and-ekuiper-6.PNG diff --git a/website/blog/2023-10-09-databend-and-ekuiper.md b/website/blog/2023-10-09-databend-and-ekuiper.md new file mode 100644 index 000000000000..6a81d57909fc --- /dev/null +++ b/website/blog/2023-10-09-databend-and-ekuiper.md @@ -0,0 +1,165 @@ +--- +title: "Sending IoT Stream Data to Databend with LF Edge eKuiper" +date: 2023-10-09 +slug: 2023-10-09-databend-and-ekuiper +cover_url: 'ekuiper/databend-and-ekuiper-1.PNG' +tags: [databend, eKuiper, IoT] +description: "eKuiper, a stream processing software under EMQ, is known for its compact size and robust functionality. It is widely utilized in various applications such as industrial IoT, vehicular networks, and public data analysis. This article has provided insights into how to use eKuiper for writing IoT stream processing data into Databend." +authors: +- name: hantmac + url: https://github.com/hantmac + image_url: https://github.com/hantmac.png +--- + +[LF Edge eKuiper](https://github.com/lf-edge/ekuiper) is a lightweight IoT data analytics and stream processing engine running on resource-constraint edge devices. The major goal for eKuiper is to provide a streaming software framework (similar to [Apache Flink](https://flink.apache.org/)) in edge side. eKuiper’s **rule engine** allows users to provide either SQL based or graph based (similar to Node-RED) rules to create IoT edge analytics applications within few minutes. + +eKuiper supports extensions in three aspects: Source, SQL functions, and Sink, through both Golang and Python. By supporting different Sinks, it allows users to send analysis results to various external systems. Databend has also been integrated into the eKuiper plugin as a Sink. Below is an example demonstrating how to use eKuiper to write IoT stream processing data into Databend. + +## Building eKuiper and Databend SQL Plugin + +### eKuiper + +```bash +git clone https://github.com/lf-edge/ekuiper & cd ekuiper +make +``` + +### Databend SQL Plugin + +Build the sink plugin: + +```bash +go build -trimpath --buildmode=plugin -tags databend -o plugins/sinks/Sql.so extensions/sinks/sql/sql.go +``` + +Copy the built sink plugin to the build directory: + +```bash +cp plugins/sinks/Sql.so _build/kuiper-1.11.1-18-g42d9147f-darwin-arm64/plugins/sinks +``` + +## Creating Table in Databend + +Create the target table "ekuiper_test" in Databend: + +```sql +create table ekuiper_test (name string,size bigint,id bigint); +``` + +## Starting eKuiper + +```bash +cd _build/kuiper-1.11.1-18-g42d9147f-darwin-arm64 +./bin/kuiperd +``` + +![img](../static/img/blog/ekuiper/databend-and-ekuiper-2.PNG) + +## Creating Streams and Rules + +eKuiper offers two methods for managing streams, rules, and target destinations. One approach is to launch a visual management interface through the ekuiper-manager Docker image (https://hub.docker.com/r/lfedge/ekuiper), while the other is to manage them using the CLI tool. + +### Creating Stream + +A stream is the operational form of data source connectors in eKuiper. It must specify a source type to define how it connects to external resources. Here, we create a stream to retrieve data from a JSON file data source and send it to eKuiper. First, configure the file data source; the connector’s configuration file is located at `/etc/sources/file.yaml`. + +```yaml +default: + # File type, supports json, csv, and lines + fileType: json + # Absolute path to the directory or file under eKuiper's root directory. + # Do not include the filename here. The filename should be defined in the stream data source. + path: data + # Time interval for reading files, in milliseconds. Set to 0 if reading only once. + interval: 0 + # Interval between sending two pieces of data after reading. + sendInterval: 0 + # Whether to read files in parallel from the directory. + parallel: false + # Action after reading the file + # 0: Keep the file unchanged + # 1: Delete the file + # 2: Move the file to the location defined in moveTo + actionAfterRead: 0 + # Location to move the file, only used for actionAfterRead = 2 + moveTo: /tmp/kuiper/moved + # Whether to include a file header, mainly used for CSV. If true, the first line is parsed as the file header. + hasHeader: false + # Define the columns of the file. If a file header is defined, this option will be overridden. + # columns: [id, name] + # Ignore the content of how many lines at the beginning. + ignoreStartLines: 0 + # Ignore the content of how many lines at the end. The last empty line is not counted. + ignoreEndLines: 0 + # Use the specified compression method to decompress the file. Supported methods now include 'gzip' and 'zstd'. + decompression: "" +``` + +Create a stream named "stream1" in the terminal: + +```bash +./bin/kuiper create stream stream1 '(id BIGINT, name STRING,size BIGINT) WITH (DATASOURCE="test.json", FORMAT="json", TYPE="file");' +``` + +![img](../static/img/blog/ekuiper/databend-and-ekuiper-3.PNG) + +Content in the JSON file: + +```json +[ + {"id": 1,"size":100, "name": "John Doe"}, + {"id": 2,"size":200, "name": "Jane Smith"}, + {"id": 3,"size":300, "name": "Kobe Brant"}, + {"id": 4,"size":400, "name": "Alen Iverson"} +] +``` + +### Creating Databend Sink Rule + +A rule represents a stream processing workflow, outlining the journey of data from its input source through various processing logic, and ultimately to actions that dispatch the data to external systems. eKuiper offers two methods to define the business logic of rules: either through SQL/action combinations or utilizing the newly introduced graph API. In this context, we define the business logic of a rule in a declarative manner by specifying the `sql` and `actions` attributes. Within this approach, the `sql` defines SQL queries to be executed against predefined streams, thereby transforming the data. Subsequently, the resulting data can be directed to multiple destinations through the `action`." + +Rules are defined using JSON. Below is the rule that we are preparing to create, named "myRule.json": + +```json +{ + "id": "myRule", + "sql": "SELECT id, name from stream1", + "actions": [ + { + "log": { + }, + "sql": { + "url": "databend://databend:databend@localhost:8000/default?sslmode=disable", + "table": "ekuiper_test", + "fields": ["id","name"] + } + } + ] +} +``` + +Create a rule in the terminal: + +```bash +./bin/kuiper create rule myRule -f myRule.json +``` + +![img](../static/img/blog/ekuiper/databend-and-ekuiper-4.PNG) + +View the status of the created rule: + +```bash +./bin/kuiper getstatus rule myRule +``` + +![img](../static/img/blog/ekuiper/databend-and-ekuiper-5.PNG) + +Once the rule is created, data that satisfies the rule’s criteria is immediately sent to the destination. At this juncture, when we inspect the ‘ekuiper_test’ table in Databend, we can observe that the data from the file data source has been successfully ingested into Databend: + +![img](../static/img/blog/ekuiper/databend-and-ekuiper-6.PNG) + +It can be observed that, because our SQL rule only specified the `id` and `name` fields, only these two fields have been included in the output. + +## Summary + +eKuiper, a stream processing software under EMQ, is known for its compact size and robust functionality. It is widely utilized in various applications such as industrial IoT, vehicular networks, and public data analysis. This article has provided insights into how to use eKuiper for writing IoT stream processing data into Databend. \ No newline at end of file diff --git a/website/static/img/blog/ekuiper/databend-and-ekuiper-1.PNG b/website/static/img/blog/ekuiper/databend-and-ekuiper-1.PNG new file mode 100644 index 0000000000000000000000000000000000000000..8e5f37412490ee2d3436aeec8e404d17a19c2980 GIT binary patch literal 243389 zcmcHgg;QK#&@~F3a)mKzX>im!v%Ht!}uc+JbAwe*#@i{Axg~W8ciPt1qEekCj51m_g z)!Qp!2!{`AJ?&*`Vj`ZZ=(~$BBp4Ug|GGwqJ>j_j_l;3*C`Hi!zJXOAV}eTkzlLDJ z*v%H<2mgPkA^rc?H2wPa@S`+?E7_Q`dd?e9;pfK}3yu-Q-9;a$kKsQ$bnGvzHgE%``mTxO=9c9N5%7_zgf}M9`$zl*^PzBDapPm(fjkTM^vLHAp>*Ft|@`A$vn8zAzhChhN zni(!o&dIxdCeum2iF(jx1b#+k!tbo{T=!(qe_crCP8fEZGqnxd-7D`p7 z6~L~mhN2xZS%3nlc#oLl|L+Qz{d{_IUOctQ8a7a7r|bRf*|8Jd-8>Q1gnG?ABLP;U zvIkWBKoWgUvEJYbeNbE2gx~0>h(;l4xws5N!7R}gA4YX-y~4Qi_YnGgCDuJYr+%N- zLf)A@i@Q%^tt$racT`ymy_NQGWdtCTw3q+^;|t9@`76O&rcOYa;J{P)e9;6?ih9pjC%6cV7H-|2EyjJF$%M|_z@7Oc1Qi=j<yEvry3y zf*Cu~5`=|`+%18#uMXHuPlEMp|CUY`FY@O*Jp}wa)`LR4ak@ueHw$ zWi)a-ipIjoF+~Nw|HL@sZXE7LEllVdj{9^>cRW(f_NC8|VWT8};mj>}7vr49XBaanK;7EHQ+>AujnAe|pRo1XFgzLM-ZMYu^slsfX*P&A-E>^tmRo(w3vJ)NtiR`Ih?uzB7N|(mt95~T!{anF03jHdeME3 z4*DdN3eN3uoZ)(x3g*i*VaBoeOCWN*6i8in==aFst+IY_n=$cmpPXNxvmMOC5}4YkbR z1MG=J`AcNt3!${xppE3A0_cKKq)zm6^A^h_X6e-y)G(Ka0O$Zu^Yf-uBMmU;9v3&< zr_$2Au4rhk=p9kQ=e4rDWoei-E4QUmCR9FZ@_d{g6oZN*En#2{U)c8F5(5#wYpYb7 zFgtFL0hRV$-yTmn8q!o_E`4I|Ql}NlRuB;^4&DlweCgo>IQT2Iqa5YaWKMFWAhKeZ z=0`&}KLg`=!%lo5n&9)A?WIbf%+AfPDv4$rW<_IeqN}Yx8mQ0=fI~z8wC`5+n8Lkn?9J4J>Ef021B)7cO#wpq zK%ZLQuxvfB^=(P`O!xnF`+*xs=5s1w0{oX1;kFue{w)61|1i*OAt>2dzKpCniT$u< z&*-uBWbwI|t(yxKLkKS2X6YtM5oUt_dX@xkxnd~)RHfh(sA63J=l2>b2kZqzSGgm(mD5%r~(OAly}-;9N#4 z?1xMLdZae+z#y0!TC%XSWSdY6^#Nkmumv^v&P-}hwb^iOg^tDmfi{27CFnZ-m@)#2 z^a2w~z-3^Oc^9BO#d5MIfTjDgH)4nj(Af7Y@}EtEy5vm}%_K*AqumJ**Dmt-CHXO5 zIrNieb)P$Md1dVoG<(*?a*N#OJvV~{>?j3(ia7pg2dFERP+Mu$$fsOEjdIp%Mj)lq0mdmS6G~7?`bZu_tmBd6Laya>kPXPaR={$at8$IN4tKWa zG+pJ**%Mk)eWq|%KNg{`M4yN?y>{O~%2AqL7he>2Nmi}2&mZ+cK!M+6s7j)Xwnuv; z+22rLqnPA~35QW0N!->~jg{)uy_%buoe!M=-;1$EUl&yf@-3lZM?zvbbN;|jaC)6`x3V;@5u>{!*C z6K~HeO`R*mLnBh@r?ZkKovl>Nq$Oby4;jEJC2QU9x92M_z*FpTmi?_5sQ^wz>lT5< zrnc2dXFG@{ESkWE@<%-H)ts}Hj2U&_*6aNBb*mSIP8nc#Ng1%96AL2r%gP=+``)Ju z&WWx@UeV^`U7d$4sKGD^Y{nI>@3d43IP>f5ncqX zMp?liid+uG^~Tu%fUu;1*{O2>?)?159?M$jiq);jNBZXaERDQYlt$Acq8b>pOF2PQYyM(|R&< z4&5>Effg`8e>7=<4#>g$s3K`TT;oO=Fbi?8_VSUu)jS4b@II+x$T5NZ+PX@&atMG; z^Sp$%TmH2id0T+KBIv|?vsIb1D`f!rMqn@$6Ce0q&ibwvk^gNv4nE!vnmiMO0`mWU z&ztw(XwkKn0IkA*?_ZcEaaYN_NmP~pdk!p~9G#3V_VM1$M5HSG1orl42IzSSg~`(= z&x^vKs(heN2}A97HsH{R;x}{Et25c%huN@Nr<8VM%bK9=xH4KRx z1`n$;0Sk;((tKyzRwhF44n@Th|15=DW>BSpk~M9uTRIp2rhZP9b2E)bp~@t9Tu&K1 z+X1X;?OwhQSYMbu|1FiVznf{(33(U9uEYoO6PU%lZhTFabJLK7tqD+(y^YAWrmAvG z1_Q!7qTd2DWscmxj{7TD__3-oS`#1ml5krinz+%9f4xu9Z+_nuB`AYdzYfb1sI|Os=XFwM zN_^y1;)bRZy@9Kugjd(u+}%#EQqo~+a{HIK;v%i#zV@5FzR-jghM#){+c}sLTZ-%~ z%ufzGB5`OhswNcCH;o-T=nz5qsnrEJg1}VkiTHefmkj6jud<~`T|~1+;4GQOA7ya* z{}gR;Jpe;Uv)>>s6PIL<^n!Nc%}1vr41ETWvT z+lm5|(gAR^CGs<=DGDgbCwUemnh)(X6xz%fOYQsTH?B7(+cn#%Z&7MCT??i!&je0HtudfYKVy?rYgLpevM-b zdzNGJOYcwq2Z?yfMeCVti4wYga)bjyy5HG`M6aw^&t)(6O>QFD#A(`kA2Ga)toTD$ z*!Y@aEeTNUM%7SAQZ-tEtEx_u`SM*H884Aa!s6;deFg#C5U7d^p_)B8Fk@yGH*M4 zozP}6r!Pr%M!h1M4csP! z37UM-ge8SsUQ{yU`bok9QyuKdO6|BffG;k>6n!hq4YqfRbN#g*fHzPmDrmA*AP;!t z`v?;Lz>d14Uj=*jluMopUbdqc7ESIcKL&n{x};P#)9LH~@Ms2LGvGDkHQ6dFgeKBS z4EPZ)1|m>yNW*SNu$#hNtUH)Vog`swz&$mGshaw{;GplG(NG*!-Me$$e9;&xz&A1E z6G-g9Jnt0~ulYqzlD0bs0$_-HBd32vAw3T0sf2Q?!B_@k)93Yb?(IPx_$xC?oE(pM z|3&g_V!Ijvpx-nX!S1MNd!R~g2LW;dLK%yTti8BOO}@|NZy^$3i{e@{{|0s>iSI#R z8##U6CVvUaua~i{TEEEFdEnWC_Xcf2L(E(rqLv>GQX${12 zxbIF8&0^8AvF6)pPTx}=&MWJ}osh3-_7RT<3_u;sB{+?xz!$=GMHk_p8TM1>>*!=> z90KS7I7Y(NJoa$_ZzPMy<)6CnOhmvtszt$!$%H;>iO&?nTkC}=U0SeqCi{+dBk+)n z2Ys|#$OA5E8)7A}^K;YrbsIE^VUuEI@;pR?6(eOMUnti18_@9Wr6`j4)<1}9fh|Cx z^+&)5f(29NP_lAj#|k^N)5c5ni%$0JkJ*x>$?eK8s$M<@RQ>?MFRY0hT`K%{3%Qhg z1UCH?$phT{1<}xIZ3;gR{zUM3m=$ov0=bO(ZO3H zJ9OhO96oeGNGXsNmknA5`L%9uj?mRY&7eWgnLuE+(Zb|H*;Z9XZ_{%v%1qf7gpuM& zF*k2=dE$G&Ccl3}J(M$r!q27QoeqXCA0J4VO-@tujhyh@DKnJj&3@Y;Q^Z~;ZD4co zKUtJQiAP&OML;R%un~oLp5Lp1S9OF}|DNT%vp)Z{B4BY|o*RpCq?Q}3e+=7WKuwJR zmQ?~7XZRbdOr*=9$HUlFn4n!#Ksz5V=*fWu(3KYR*qW=t_yY_4&Yn|Xaa{s7XbxmT z1%@;=j-B4SFZC9K+nMHT+`w-OFnf}1TIWBw8E0c(t*C(g{?M5NI&70QD-A0iCKjEO zhRw7se*TZ*r_TN#+9MCJf0efQ;--IAeocK#?6V|!>dkH|0rn{_ zq>G$^G;oh=Pu`d|$t7%`Ag=)ji^t$iDFf9wEn=JXiW;^cqwgiU`W>xoI6)nS7VxZI z%5GL=MK>Qm=44#|und)BmPGpX&$$S%nEL6gkOBovl($9T;STs`*9%1hUgQSz)}1)T*$Yn2Gk{l7w~ zmCtc$dSyBjEnR^CiHzLfTg%BBQ&U?3B%H+njZmWEA*p}x4sVXCThVipOXKBr{i779 zfDT||)8thu$~T_427TuPyh%yr_`rPQUtAO*tO1IE)@YQ;Sy$3HK=-Fis5%?=u)#tN z3jZl~?$tIiU<`Bu{5uYG{|G=TEKv@n0FI13)KCt_G^K>TX=cmP;Gx4R6X9`H_vr)_ znZHsL%Se&7nqXtNuQs4hDfmFtk5>P`K5C>;c zqXl+WN}n~B9l+qd+d|Zm1$@>3=f|T*bgP#`-+-NMbv=shjGDalpvP1Kd&JkTr~8h; zega@G2p!OKlVLlj=ExS2wKCsS)GPkT;V&u8TTPYDRpO%oZkd!Vq515UFX~-a^`>Nb zWI``KX@b|!@`Ky#oJ$%3%zZ%NwN~r>byg<;Russ=UxO zH)^2ZlE{BPj{5&5(dznVlH|q^(qZ`E^{JLP)gt;tT(Ev5m5#$xOGXH8My{WruT$4U zKJ~IAQfCE|q4zFcaoQWVt`DUMQeva^k|U5LP20#y?wm#wo^TmEoOfLIpN%Y8Zyv_Z zcuzDCh)q^TaEgSCqhTOJ7kV4-qAeRM6+Avd7i5v&oZmd~*c2PIdT~HQyN8I=zY?DY z*C9V?$uum&rTv0?CS*x97@hju>~AdV{C6dI1NHs{)uAq5ra1}9Q44)xJ)a4KE_+?g zdwqp+Ti376I_rpZ>4M)Fz+H|MFn$e3Fdr3wsI}Q;HCbape$FVKIZ(8)dDlI@xhMvI zT54BQfL)#hr85HzH7}n4Up{e0TsVUi!Qk=e;kHbugBZf8!%V_O#(krmuKH=!Rwp)4 z$`HFNT?Ur8fmqevf(n2oLcbe=C8O0KH51|5MF)(wVWu(JqA?LZ>5 zGT8o7|1T=Cx3`G(-5wTjzD4}CvqTgL@Fij6tlhfCPW(nYcU1!BZv@)4>#imM8nOXv z7xMG)HpRYAUJ|CBP|n5}y_9kiGT zU1Kr+7hm98Rz>4AsK5ZSuWV*9Q{Oxb!F(zUg(jm=mf5AD$qIvby)=10nF2P}k?eno z*uuU6LtIeSMy%_@mQlTPl|jmn$Le?~0{{$IF}@}$duDnyKIAZSXY`^aUJ7;gBB1PhH6 zEm8z$I3ChRjAQCfsm@L`o9osebiSY5O1YqifZ9cbw4b5=Fwd|&RM0z}?Hpl6zchM( z%;hJ}#WIi@_ZC^p8i|oSTUQIkW6p$PBG3O+Q%3#hoZ7IcX*5LDm8WGgcdy}IIN!B> z#>gw1XbqtV8m+L#ksBc|s%U$!Zyl9P{}VZe5E=eyvGjf-7}so?DaPEy6XI^ce-!^C z@PFpSs!s@0R|@A^)N4OKkX;~m@sFdfD4RdzG{S$OjoF#t66wYkXEcX=D8oQ$A{`M> zz+bn}_Ko*&&_f53(60rw%U>4|oY1~YIu222u zwqxNsR^!xPcUfjTCHNTGb!UYmdz^tD6miVO1EYg`m%ugHTKN7cR=nvCB-!)OL8}nM z-&d1+M_O^@XGfp*(EnF!il9MCAT*tb7K0;tj~2pn3ds>% z)Koysw)p(1@vu_gTU)xv>swIQFN8+Sjjl!RcmMOUe`=)eFT~Jtco7m1tWY7bW6|y0 zzk0C6QBs=17LB-{i8k=+Zgdjngd9#{?s`c&cmAoF{?A~jp3)%c!O1zpGfJ+kJY5xL zvq3M!-exopUI^~pl@^iO+#qSVp;jkV|8q@H5VsBdb}IV+vY4*$(>52Sm;^LBKxr98 z?6BOFd69XwPkVrz^_-R(s{z+M+mG<~8cv(cq(Lsdf-U*9>c8Qx|24tjEY%n?br~7b zNoGv5*cy8g`ybp8SvYReA@t1>ogJY8ppqcMv20y8G8K581ChkfxOVTu|BN3siG9ps zgAXYYzE#i<tedJap(Bg`f7up! z8_lTTUDi*uf}So9$3)%_5m;|L+&I1F9Zj@Uj>S2IZ%Yu4vrdi~ZbUTq;Ap~eU_GR# z6i4AHfS{*7JS+$|@mMp=!QQmP15^Iiy4l}No@e8pq6{b)o@*!$Abe(^6@M^)u+6P) z2m_^PLYi$T%sf2lY9UOY_15-LeAR>mZc%)X-FD3e-K?Y6@mmcY`ZXG$r*=J|_z za@rIreyN#ON_DKAHqIN7Ko~|y2JGO;d{wX*5Yay=99!o!`r4pD8gTO{7^$zT9S~RZ z8ji3c>V+foPd3u1T5cvI-kUH>x1JQa`gMY9)C6An{^OQvwSI)hLIs}e^NLE_>vk;Z z9hS*%-zqEj=vK(X@bQPszp;aU7x1gjaoUS%yo*{q!Ff)?B$I7RKJ+31GvO+V%Sl5U z3^SUjnN0@kZbMF@Gvq43Fr6; zwNLT$A~lEREX%vh;C&8@aF(R`JceXhpU3RWSLdRWUqw;3yic(rf0&qSAH`>siaARw z7u*CR_qcQb`8J%^Xdkc*)^M1vN4}?Do#4z2e%>MzPP^4Ala#T3LvyJ;B60=rIuNaX zMt>^GhF}h=B_@=cb&0)IMtmvVYZo!QV~zL90W$b{lHYb^ji4sjsJ(nEXl6)HWpY}= z>Dj+?)QPEqq%R_IcY|m$9Gkm1B0<0AZU1@$CSpy~_P#z{9@CJMor@H9)`7LN1KWB& zp3as4Ot^mC+`0brOSwAulvjC;pNy^~MwgFgVM-|j*L0RSGT5AQlJ-3Kah}T}Cm5H3 zwl$P|KlNGV;ifB1L(2GMd{bLSH2%kRHE9}c05`kPRNzj2eq|>5^=#d%b$aqac_o`ZFzyE@ zFjM$ukD&3Fm7}^ht zvqJEw!FnAVvh(y~{rs?qctzQ>t{n7M9tH~wsVaXUc@itBS;Vju zIuGLEtb1P}A{8KYQVD-t-7zz-_4p8h^cJvc0zeN!{3@2p7&!Vau=L8Q%YUq^?(YiW zuL`@zK0=Bfe#G4PpH&Rx1iWgM%#E*pGm0v96hVzQa0?L)=mS=K`I>B#shYw5-O(Sf zG_^2@9Nu;CrewYu$(X(S<~RFF~pTuKp>e=T1UX|PzPn||4znivz2AUb-%Cp?_* z{pTZF5$BCvtF1?}?PrgVYblStSQcUp_c%)*W7X!WZ_wpsNH1bP2EEr!JM2ELsxt0n zNGNu(y)db5=99A#QD>*{KK)atHHu%+<_@`VJ$(!Cy;|Cz*;uit{>5mq1iD6)F8|bO zfGFL4-88Sgv?Y0j?6EhgKu!Lj--kz=R2l&ajLITi8jVY~g9J|@#L?axR+^Rb-<2O8B>~2L?sD^*49{;O|{yqQQ*X3+I_n3EH zITaSz{}hkJe)ID~?E<(e0gs?nIMLuUZ}O5m5;{K8Ve{jsH$Z{?#r#f~xx|*JuCQ)k z(Q$9WQTep_D_fX_Ft@jFEdw_ePyAqV$c1p^lstn*oOX7=WhCU@ewd^j(s^BdHKOW8 zt}P1bV(g+OQsWQzbF-a^-~xPB(B`3368cURia$+y%~_ ziQOX{54x*oaMdIP)7bXI$p={fiH}NEop(`O)oip^Rtl_%k&i9`qa8u1RmhYqb1enqd64Y*{Ff`Dd5I$q!OyRjfp6v{pDX z^JYU$%F2G#$wA~;GVj+gEbfLb_aRfAll90PB*X9w zyF94#r(Rk+Q|x#D2|$wwHWFTmNZCQoXj!^^anPO#r+8>e;I+pk+ORFnZ|}LI-2s)s zD!vV`X}htV2R_z?gPQsT$$qLSQrGoMyqUnG*gE@YhLCsDQT(y?zOB4Q84n}xm#{4G z9WuB0O;ZLWB0xNpvhJeeN_z?HLWy4T?Fkl41rL;+YM52NUe7d}i4rMt9s9qFmvpjT z>?hpp;!9%V{27})L-FH?#)Mn;F7GSKXuj7mJ#?6qrK|;*2xP(?mdnl5wKBbVI58TY z@W?xc1{aM?*1afE$tWo;x5pu_ksv_M684sD0-vkSp5AiH3;LY?bv;?7=;I%zC#vTT z-T}4(kAo+4dnTflD{enA+;_nmd}8#>S=_8qz0O>DBrHuOu$&R?_S(LpRO{MNeS5Q| zHi{EZ77wo~YaVm5`!OvTXuw7_6mcp+So^W*%_1cKSp;|GN>VN%d9uHI8t5c?^x=kg zzjM(|cv(2F9Kxa}xNngkHMs7^d;Wc4v`q9p_LRHGp$z~c3o-mPn2`oXe(O|o-f;;a z5_)J(R44n(S+J}=R6dz!^T>;<-gWUj;V8cTa#a8cA<#tDV?dOJc17X4dn?JsfsStu z`Izn78h(^0rk(1prNuV26*#=#2a!2!!;Ra#xA-_Cpe#W0!6Ok@$SV?yhr7jmr1hpx zR&0Id`w?=9PwF2xZvdu3mb>uz*hhFspg#%kFSlK-y@QsTK{|eoGP-ay<>{)0pf@^Z zBP&%PmK<6!Z4k%uyh_Yv!s9cmMb7xc1lz*Xff)=>dt7*d_2{dzRQV*EVsT&iCDR$Sdq+w)3$@~bLwG~#qPGrpJDM`H}SxCjXm6*8Wr1Ju#@k5J8sklCZm@Y zuPFtip=GYM``!jGd%MN=R)h-*m4Io*J?(=&e1T8#ZfIGW#f8L3$%NFjPa{LBwUi%f zhLq-qQ`t7`le72BH&!{m3%z^M4mjz~3JSrBNu=e~m(U|zkgy|@TFrYS*rPzU7#gus zjM8?@c)iqjRFcwyThZcgvDqYUJwo9FLyn}~sFBKjpVXhx1xNp^*U@0Q@5R5IiDlb! zm<4vgO=Ex2Wl{PI5Mip@ftcexHSx7IO?S!cI5z@g;>Y$>;tI&zC{FwjALcc6sH%-N z{Iz^OWouV_3p}Qyj!s5LrLKCJwm{vY|D(YBF}-?*zKD*`UcG;<^ooAvLZ44__T`(hm_Q~+-N z$+|wsAw6H#vsL(_&8)8c@J#iNO@1qRqZL_FDEVZ*mE1}oTeG^0q}JaFahpoj3;(k7 z6YtOJpkqpD)yG`DLjiY4p#98-p9>B{E1DY3Q@uV>jlR??v5HBe^oIzH|G?HqN1F0p zS>*>VUa_Ytqd=GNF@hD*Mha*DsLuAmf7ee0o8Aq3e(CJR=0x|#2YN9JqZTKRlZ)hG z{b@DbAH9noAUT-?HMYflF9kuawz*Wpb-9ELh#8T0Cl;4HNfN)gu-sZC)rD;2Fnl4F znj7h6&H2fYo-sxkd2fuMNe%5o#;-H-4L4Q4<&>G)zHt;w-8+pg(Qnl{i^LFq1Haml zDl!cPYup#xlc~$U9amX!`EU(T=!xUlL6Nljq7Wi?anWQ^bS>;<#j4WH1}uond+Cnb|eNaY!~2THOg16j6O^}0LHGY zUJ=tg=h*edeIJxNYigHCA{QAoAP8iFC)yBldER8LIg?}DZ@t=jTrfGYb=MPFE z8oMl^WF;jU=P^s5te!&I7X|#wQhH|1RxU#leccxg%mE6IH*F1H@N`D_bUAH3%^(-a zQcoR%++q(VjS@yL3_Y_%`LEbtQp7NSEXR++v*QXZVnsFEFQk)2KC40&0(Y6u z%s^pRaEzH;Gu#dx zmz`7rjERgG)gkt^p){A)^;}xe5FUbwO5D2DnVXrO9DZ;s*-Jv~>Vu%C8NxPQGR!WA zcGru&VEq1}pk@%PM{H~i`~4VVwtL0zm#3!PBMQ$!2S65qCYR>2x|w9t!*R~z?vh-& zO9UVxQ&dem(UdF7WuniT3o4Nm%G;{#=FwtqHHn9k*rCP!Gzou1SnKgt*Tpt-E(uY{ zl~9?(oh|;#Do&?|&9I9ApatlFc;&d657gzP_9m-nst0wPyW`tutc?Je zUbI2pg7i8L*vBj+7E93L14Jc9b#b&b!Zv&FIurf(XHx@ zKkJu`_4OuUId8o<5tkd+jx(I>>K`p*UNGRN6v7IZ5C^Z2-qbtY3q?RFO)@;a^tB{M zV=9H46dPP1UW>?U!cXM7CNz<3O_Ey$zD#>C*GvK)i-y{Hn0pwupy9huWn~u5rZ-=F zDgTmH3v>Nh7>izCy2n8%?mV-S5R|_-?>c>_W)F8oj;=QA?;kuyu2V(}VhWaCQYX4#0edas62BxJ2o;@*mlV}K%p5eW-L z;x6+&`Wc+FOsc;SY{5{o6|;M&esm(1vtC3I5QtFI2)l`pELPhu&TzE3LH;2S*|$4y*r70cXY*nRQ3H3e>K82pn^JQnRt)Rb>&fb z_6Xmf;7mCL?jsIREbSR%jxNj8CpO~hn&C3KZ`2XNuHqsee-dgC&eG8|fC)d*=S7z2 z*IOSqtJJ6-ka-3gur5p9zi7DDNUJzAd}cYGIQ~|Q^5IwQQzf;f%zSL9CsLCN`VLG_ zSP#$h6t$)ZNu+IY`%iEUukg;Vw7XFN8tmo5i^-A_H?NdM%CvNI(<}zRxES<;h3V#+ zCktQk;rZ`I_C+W;a`;x<5MBN^28on!ad_r=tti-UDny7Wb4;6gL-ntW#7f1V)K|)>r~E;` z2)c{>cCE{c@oIP2?%IJth`B&JW_YhbWIiErNLCKf7mHx%2S9~Z<-Ph}+>rrbi85>u zspbQfz!D?gAlhFV$#E;4|SW9J>RMB>k>M2q_% zMJnPN_R?aR&{SCqkl4igBeB|c|KTWd<>sAV@E{wY$K~N-QxSWc&ccv5*hO>W_qdp4 z)u0cj1n#Fm5r(i5`4(QqY*zn&1Jg$_28&wy_NB@)1O@lG>iAwcddeePoC}mn5=?68~G@UgD#=mYz~c@cH&V?PWf| z3oMr@A7j9_Nw>y*(y*~Xq5@A@uN^ipPn+#oCfj@1PoOQv%1!l|pEK*>$fSD(Gukgk zuiV(UPcxCI&WBt&5bgVa{mwK*c4)=_Bzp<8OH8^q$o}wDX$P4C2vxc$BvfR9!)02q z+-4GlM5vlu0eSz2^Ff+V=EN2PlibKH6B{@Y!=%mH`HY&Hpu1j4`O28$`}yLHxLoIP z>ur5dG2E^1BXx+~RMA^_p8BG#hAp5}_L5@v;owAeSPJM@2%Le=6p??9-hAqZ*-gz( z?&Hwyr$!c*==wOPCiHDsV~Pm}hTlRys+N?6a^Ix*rPmABFCc0MuJ*0#@S~PCjofuj zy-F^=4vfCDMTxeC&f{%y(Ek<}D324qSeWxYUD}Oo%xR19e0_c-G2lw53=b6bD5Jdk z#2m2ODLWUW%HG8FM=j`0b@#-Q3B0{Vysbv~L{71KB}G_do?{Tm}Q^ZyPw{ZsN!cA0ESZv377P1NDG&`(C*vOK-vE|24YXZKL3q2V7AU7s*2b>45f zA6*@z87RkMc?CURvhmpXF#awkO(WWFe~P;NDKvb;V;`7b(`3DKCrQkf-2W1Tw)2Xv zi4^`yAZHg$)Y;R*YOFy_6V-bfn;_Fw4QugwJTX1smwcifWyE9?q0;)^Tf!>&+Z);9 z^JJK4C6_%B%qw6Ev*9$XDNi%_Yjj*S{{ z1=1KNh7)zl{EUX?Ym?S}HHqnNLy^GNewez<25UnaMC9m@re%!93Hm?b5tMOBgTIz7 zl31frK(AfZboXpNS-d6s??#0*)c}ls437o#vOI}?V2pT4_2;4?SF}sk5y_Rt^Q~My ztx$E{!^e^mrL^~Y*wPMav)X_=D zwvTJyAk(7f7Eg8&4nbH#kXbm5!hSUe{#e@CNZeQsqgSVdK1N;yXwJC6IQ^rl((uRS zvaEoI#O|56p+nhw{ENDOeCV5%-^ICR0V&=j>3Z(lXn2R zSlv9^ew46gb5_HOg1^+oO8iq)KEX~xmiPa~@sRD*f0DcOThACRl--rA4KN&k)Z8dC zd4ks!_V4|f0_Ib=TZ5sS^LQG3Mc8NwLDU>_yI&mLv-mM4Dzb%+%>el|l0eq4yo+!3 zSt^vK+5XJIpFdLLwVxE5ixjtc;}s4u;~@;FTKrFv$Xy_M+CFqdefpP?H%uHTHD)Yh zOFm<@lO`IxPbq&D`m3|`&kg$V{Ew^YhNN{?xvrtm3@+TbET+c-$^#MiboTlhs!TC; zfT~(zqR`nQI5TeCqTKUg*|qiN>EA-~x#~XQb-_aM&v5H&9%U5KrCLnprTW>&kTwG5 zRvAALJ~L20`x02nrcFd)@I5wDm>U6YN!=zqs=7I zE5N!OCwA+4FzOE6%>qeTfP?^q*0|z~fUFE{eh+DsSLY5s$jeD#>ygUqZUk%TD|yWU z(S60!({5VvC!mktWzYNjF}hmwF+LF<&PcAioJhzKQ?D~Bq(OS{LBKq3ERB$-V&cnW zqztWo)p!)M0<)R{Z2>_#1I`;{{bWXFHjUD%p1V^+bCDXAx2jt}^O>vhMGB7;ur^8w zO~?LPOerCQ0$xFw4)#d6bTH{rQy%sC5dUzd%%5HTGP%h#`l6E7tq}ZEZbnY9^7H4i zqY=tRpO7kkF((y{MH8)$gsP|LKR>Sd+l%pF)z_%Og5R`Ryz8#*sB#C78)UYF(B}B# zz}N476>0P*NnRBpXQ_%L9e)T< zj66?)cH)xA`=<*l-ZYXr3HH7xp}#Sy6{Ol7PVpSDVe_E1L=MjMZ!GQcVrSWq=2VlJ zF8bppVX6H}kXgWwam%8ow(Hdys$zpBj|rQ$y39=I`JO=f;FfrpRvg3Y#Z|uFt|bI= zvo`d$C9)=d4i8f$3w1U)NwG5LH5++To&LHyu(=cwH{B7*ARgoT7g}O)p5LgA9)ir` zz>N2Pwi$%x%iF>=@r^JRuENPIEnS!KJ>?&9X6^j0d!T#m*Ns1`&dMJNZAZ!>l@qh} zX}?B}{-%Y-=2pcQC5}0UEna*%OAGa2T}k;6b3h~5=}My@Et>^l)8Q2G-79(7or&ge zG#>e?dg0Sen_)~6pI`Ed!$Gx;6L<&9@qPm7k6B;4mr{ozPzWIcimp zxCrjeTqcE&RvT<_sx5hp*cClVJ&Q?7$dQTC`ypoin%2m2^MyLHDNxD(Y>cZOOiw;I+w+iJeGkup>8=E^ z3fA(9ml#lh+c65*fn7;(xob+@y8LYt;NayE*At% zwy&4YSla)rDU2P2gyhC{e|X2T z@tWN5@|fwl&t&*qnyKP@o1U#4z?zhb&t*`3&9zMcg|v#7BN7;}k3Qio|BDztz)iw_ zB{!0fxIOppag${5KNvVM#l{duNC+m4dwDV035fXtLcsU!J&X4}gI`%y3~6&Q9?~8F zz($FtE_DmZoVI&OX&LWE;}N+Iua1c&m_rJ`G20b)SWKmiJb?>x4GI@&W;k z%3mxV2gP2_BgCEr(sEivzE4%KNq38hl~MB*^_OZ~Ln_$?;M7|822iK7@r_d=^axC; z6B`F(J~`Ow@IH@UtHiDceAeG({u)dCPUJ*-;T}kTEAy=4Z{HF*;NUrA3ayaNr&0{~ zlL0C2(7(()y%MxPD^ezpc@iXgK}a!bZg*~b%7CmeN@Kz^;Kd>NjVdVq`h$n)Qz7Uz z9Lv=8V$t-Shs|VAS7tS^F1*)=Ocr8va!-htmuR5tdNpq1)Y6Ftu zWXO-sS5~z2qOJe^UmHS!W891A4h4q4liN?PFS9{U6AybAlzAUC*aB){m2_^ogDsyq z!eXfqo| z%)}$ z21~Kvai?)QAu*4t`#I>V_1r8;b~647N|a23PeI8Mljv^67&@~4KcPrePB``*voYEI zi*(v?+qRZLton*5Gl9Px+Z)hJFROE>nSofo8M0MJ?#YYjU)MihI)$}28n;V$&(J!p zG!89lWn^~zBM&Hj!c8o}q(t&*EE9!<3m?Cso|6A{*xSHmF&>uLR61BLD<;qR84}Y= z81ahtBOB>WO-sR0M`VCW{nCLPY)!rQZ-%y?vA*4=pQ!I^kFj7>-+{_pQO9Hms8=kJ=d_Xlx6r=S7{u zef=d)Uv@nG;r>$G#_~q$M>#Y&1%nJQYQVMlU>#nYo(w}ufv{67HSKp?-Ej<_rI_3w?=_e7g! z-90jgDeM4b8omoJYX1U5(OzUp3xcHPC_u15%p4$*>o0b#b%?)X;_uwDRX&!y>V zjQkJ!|7By{kWd8NRav_R+TD2E+AR8n6o#ndE2F7>0mIB6fxU4|A&pfj1L1j_EHXwB zNWy>oxXx|r$AeVUk#G9g505RjT!vW&sIWO7T)p;*8|>B3j5ev|5xa|7aM%IJVD&^~ z6Rw-U`@UWsJXKAmyRY0t`tg zA*YQq&M*wKlnC3Uc;v-~e2pBxtSs_VGBQ!B;p_y$$pF0BG-LIJ4#IbU%f_mSPQPND zJ~F1bAs>$X5SrrHv25Qk2Z?CJsle(qVZf?D8L!VGqYOdYx<&<2dXhG#=#WDM66xYR zRL=P6)g%TnFwEgaG|FJd6MS*^bK<#ek6jj#yO@IjI{=x+R9FJ4B=Bzp{AavOnt2s zQGY3wC>?J3nFWD@S|>08U|0!Q(6Gt1NlLiVlAg4DpVE2PKkDpFLx7Qk@ZEWM^o+A0 zuGGxDa44<<96zF$#|Xn?)Uu6XW`G@l42JU2-OAo`@hfimnS~$@t1}5;6e6@k9OH&NWE84WRz4^b9f#!z?lC{j^0M$oP<;dTSVL_64vGgkn7KNX^s;Q~V>bEFFLhv-s2< z?6#@FHWoBdeBav83SR?x_$<7vP>`SV2FmIeuz%^DyKvjnlogB8ArjHaVUR*4X< z6#k@2Z`4XDWegqg<7Z-}Yc^+?`68O#bEwp)$liBcla<vK~s74#Rnb+e?eAZC+_xuyx6|?aY!Ubo|x3S~H*Hsq1b!)iS_Ecbn2X z6EyYf;ZpYrZ2=Lx{pKJb;%Hq#oe2y}Ar7DGz%cVnaNFi*yAp87GI_DdFiR8B?KLAQ z7{?M+s918u(L5QSVGfMQdi5o)Y4&f#4nU?S*a67k=)Isn0@(U6Mzz2;5GsEGux$wb z-o+L-kJR7hG)$Aj$rHlND)*K_DWbRM*0u_LxS9e47$Qh1rk(QK$v4=( zcuM=2N+hmDr}38EhBee9JGMIhI3Od#%mIpL^Y%1~DCv9`?!ghrFo!2`FGvCud=j0V z04YIa-}lNKPWs5h2Q~tbyG-VAN2kI+j~I5sVFN!0p!TOY&Mngu>;PnN?gPNL0BqW4 zNZHtYu)veo4gOQ8O+Zpl{14a3Y8k#qtk=H&+8rHMW*Q&eW1dxV1B_%{2-`tY@o z4G2;g>~{q||Hnz(wX+23Fn&_OOnG4%#R(PRb{VY+*NDqlk0EMN>-K1Rb>}qEZh|i) zw6L)%ML2I`8c#hs57mg;$e|e-rW@3$9i@0t^I0GXFy4GBV;E*Ckx@h)-m;5m^|S-; z>*~=>;Po&vGaoe z0RMTKg`aK)GI7)KljD|0u$=Z)fEngMnbDCv52H(Gj`zoOqJE$6bqTM!c|UI3U4*As zG@W=Y*NvuJrsSBs_RH9Q$;AxYlj%Z#g%IJw2A}+O1+O~0hBuy@hQlL~8D>GLuL3ON zC4?DsI$>=%Ulh2-@yrdAg z`qz`VZFfIbq#PXEKa8U7LmLYBMZcpkV7Ve{8DeC@@p>X09oXJt)E7)w;TXf#iM$kA zV-%0+Yi(sr==QT<3xl1N9R6Gx#5KRJ;rt_Oc+A=qNNWhGxN%~b1%_t;Fw8u)}sXZA@Gx77caZ1ga-+@eP0#ltz8Au1i`~Sng5@? zGl7$%sQdn}s=DXgdpFr6$S65g4>VFt| zh1@;sYil-Tz(7-O(-0@+5NI-W*Q< zx7ji58%$TofQ$}}m0*Cw085WAC*r?x|p3m&RbIWC{;XB6aee-q?*7 zW7#|^4dtfa^uL6Yg07aG-T*J;0LRwb~)FM0orXUg8dU~J=_h90B9J{w5b16 z$@n&v6JLGR(x)Xd^dF1>Xh0(1K1wqT@puqd+1i3QQ6bsH^D)&-7GOh<@jA04|E@R| zT)Fn)!g|oXB|EK3O6;Lhky0wFAF8hs2!Rw?6^(DR!|;dsX}qy2iPo%yB(!sYa!^Pq zinKjh2ypISpiZWaFF<68b=<&^R4`DI0T)5+;I#-vf%_KPcx*{4>Z!owu@avZ zR2|*(AKq0RAINoI2nmbY*pOkk{@D)vYh4t`5>P5Z2rbJ0>k5d)*H!vt-(m_Dd&TP@ zZy5*#)c+Pssmc=<1RI?iFk<1AtsJke*ou2DXu{YqLsGj>nTPbQ5!%~@(h7@LVJJJE z1(GnrgTc`IsL8O-lcTw>yyuw-zhl@Rk={gbe|S#l1fJC?{0=~(1^6mS^qbNf`WQ2| zFd&)%+MtYBm@P2clIQ@i#gc?%Dxe|B8{tUfE?_7R6ja4<+h6kZ`lTz{+rhW{B?MFo zqzT0@-pb-%Yr+^|w_)mtEG7=ML8u#p51quloll_mP4N}?|8iH${98?C9Aq4Uny{_v z(ejo6US4Zs?v?<4HM<4BICT&LLTk%q$Dh5ryF2j0%=E4BW`sXOSCBqiYNxLYV=xR< zr0>yTB9$2oJwtcjc0}K2E!J#)|Oam6Wj;jvo@iu z68g*DB&5{dd!}VH^c3N%Pra+N?Ow_2)So8BHf#p?dP0^3tVw0@%6myr9>i&5v$%c6 z5QO`AjN$bxvu9f_4I*1>VI7%^%XqE%tv-B9f8UTn=--jgb&FFs`NsJ4b4;0b@i zbb1YjK0ysKh?{vIs=w01c2YOt!f1(xa?@`HiP{6g5tK-MM`X<1VCWjAFJ#DP0gl@$ z^RLZ`J1P_vVqD=hhq>{pv~@BNpdX}oH|(znFR2Xa>@859D&>!oodr~M-<`KC^`JJl z#FCyUDSgt1F$46F`h2u_~Hq1%BhxA-_iSIXibA-A5I3P5D1@o8U^Vj?k zgPZH|=Ldd_D8k6+zw^w!OSx*RY|Emo)`Ld$+rP=x0xsQ5+{|PFHW+#tq6QE&*IpBn ze@d!JO5N=Cxc{w3ueouG>B}2>4grw_G*>|dRsFU7zh1c=9wEfN5{Ro0(ge9MbPXc_ z8sH`UusC1|B_=TMb0tA^FT}KthH}s;7erZ=8dhZ6*ENW-Zrvq9omHX7B@~npgsD3) zq09m(1#u&U`PM2Dr+gX2J6~(zivYN@pq1C5$j;TpTb8R!K}awPaN#B|*?B)bJO@H* z(Tl3>x3{&y;R03BY6L?Th@J@Il7kVt)6S(7Dpn$4Or6a{B87A+uD*{}RUsI(^T#ID zz6xhR=FSgc>ZLcPED*_FYj8vfc)8VHeC|!eV`-fF@#9e6Py@~d80-25J*&MQi+2)e zX>CO+l|mp8L_-y%)$F@vE`C~N=3mj&2h7@(-TfHU%CT8PSGh5?2yVFW+} z4rqs54PaCo+FL_7o=I%p3SdY(cqJti=S4|COq?`BckvQ{XH|Ga-U_LmW5Ok zh61H+Ckr{=%AdJ;&x=WslPbw4mT?6xQr1aap)KUc>B}3$T9Ob|hnQbta?HC`OJR zfm2VPgz2Xptt<&Wi$WnBP-@}9`=7z9FTI7KL!0o$uY3x%bzw-E?PQ6#CsbBv8bHgo zI9`8kF>33gIQ8_YYFY01UVLs29{K0M({KFa(DlaWIZM?ofSn zvfKBKcZ@%{<#(7hb1tSGI~lj%{>^S}%g}pxPUgxmwBH9cF&O#>z1sCLGKXQHlMbmr zpM5)klm*-cAUHdObYGXt&@V6opaJ2G`!|?{6VBibOFvkHPGvk|J)01V>488+9F?Ij z@#;}NvY4)9L@Mt{_3}cTkzNYPL39HyQHVuIIz>Ah;BufocuR`B3?#L+9r4rvzqhD= zB^2D2)Vk7VLadhg*PuHoz&lUI5-r0O+9RodC4F+BDofrCx97NrD8aeJuW!E(kN)%D zh_zV|lEV&U)iJ%lVgqd3!t9w#@Xv>y$M=449S%NZw6a8G#I@d|lqu2hhu^;oix#a^ zmUkAlK^Y*pqqe_j(P}*Z!oM+X+M)R7w?Bu$&2=iVis$yb?A`Tv>G{PNGj=epyCJvl z2&CxjCcGzWyxg{OWtlZ=5!SBRh@+>B#$iW{LxyJ&U^Z5--i8;SUyQoCYQz&A;11E( z2g>c2OAgBhYHFf5?zkiI)o*+X&CPY%Ew~#eU9XZ#5X9PSq?0rsXSPBjXjBeFx7lWN zWH9tH5}-m^dG}g(+nZn&`*4!gP{BwQLPz8(6!ZE{`*{!Aa%Z#N8p4Nhw~M*S8E$5{ z3|+$rfCj7>!w{JMkbC>Y8BDRS-HX<3h~OGTGsqj|re*g52x zsp>8y?*hDc1BDXNqC`l(9JX zypu4vIjZ)wX;TYcd~qh0&fkO;%iHjyTmFRK-}Nm-qBex!pp0R|`pvlMOFzPf^&McA zg=3CA7$=-C83zn+R&{Uf+Rga)f9Bx5^PrZ{m_XqL07ju=(SN)2zLNLIh zkObvM#mb`ui9{C9JUtr$yAdNt4#O|*_%iBhJLf*W=;BlHjhlXh*{{Ec_m-{0%r_R` zj5DV~kgO8WZvDysV&nQ4>>$M@mz|C4uDb}f-8q=rpF+BmpQ zK@&#pw4H4`+VI~u{Sr$TZO7a>%du?PN*r>?813dvcPq;6O0-j!r6t+}Wqm0mSZ=#) z=k>gO8hRg6R+oQ+!O$bh^x!iBpkctGD}KU;&4jDxNV|{AWau}To~Hrc*j5q}p%UL= zz-775jO=}g_Wcpz8_eh43)5RLbQ@375fDs3QqeiP11X4r-E}pWKFhK!tWNHxU4i6X zP0diZ6tYMJAS#z~7F-HYB0;GHAzZz;a@R3GX+X6ruPYQX5alAPmU}Pi3Phe5>pBZl zB~THXyOtafg-pXjZLe^>%MU?3oEg=nJ&YbypbIvjqtM9~9CgFw~J_^gGaCjD6IN;oY!yN>I3^#u9lbC$$ z7(~JpufFsq7QeF+mKAW1FL!Pz$!CGwTcmVdTV8#24z_M<$JA+)FnnY)9LasjL}cDo zJDEsAaDj}Ig(HM|B{NwxHPqsavyWG9(CL)G=8aq3H3hJ7<7O;byc{3AzXqK9vqB3c z{chN>1&bFg$NTTEMmkeEOO|)-b8F=46r&WIHf_h#PrZS^{`pD#q?oO|Ue6xsh%0O!qMS(b;W z!`*;@yXcn~48<4=Km$^G#vpD38B6t)v$@Ysg>;nj+6;I70KMT>uG3%=0vfu5R1w^y zmh0SDH^o^Ugl=6$5AGws6`-RfsNHYQhBFNSl0y=vHf9r`gL6E$DvsG3Db~d-)JK46gR_`1CW59QRXQIb6x)&x z9$MOp#@Zl0GA;-P4nefV8hSBCl0NQ8RRG~C3cFQe-dpeBop(OK5l4(IoB`q>>tu1z zK_hX`U%!uJ(m`!?G#?l5g%{?);Wlb&gShJIimlBlOo$6wibJ&dGqaQs7Z_WB8 zQVEGyUV0OUO&pUy>D}&AOMo#Osbm^2y)*|_(84LFP1VUgik+qlIhJL9$KJQDn{Ox> z0<)yDD)e9$#XWcb9e;n|X^bB~8u$F==cui%*6zDPWsAQ5fhX|idmqB6(Zg`>y|<&W zv8H5kiI;VmF#P#1Pve2VK7-90+L2CYVG)X8AcRq42jRxAT#6G;KH7D)rsiqXTSzY+ zX0E~p`?@p+!vH|)y!xbQm0RU&UG=17VF0(j{C4*d5EDeTBt7*^APs7>&4D{%K*;JQgM{NRKT zh?HOic&ClyE6=AfYmGotfTK17q%s^2&5q;Fp;`R+tXiBj$Q5)prLwr?v4F@p&jLG=JZ~qF$M&Ro=-HB7DPr|9EO~S#4jzU#5kUupeM-1~v zOhhIluyVx?I9YN_Q&1rCnJk9&rc;4Q|A$RXgzBiq$Id1|tmI;Sij6 zYqTbmbtc1tx8KE*MQbo-++a+Zc945d5T>#gy9cf1UP%!RN5L2c^IdnFx$kGbF(2t< z3Qf&64mzYDhFVq_cAyH(iWXVky6GTlP4>SL&l5<*cKOV{hlP_bMs#@V*zOB8b$y#VDSm=kZj?2v~L)VX-K)>uRo1cL`zZ} zWPhjN`v${aLVOu^_tr&s@Af0|!WA=hwp(=0@LTL|%fKkGF3oYvt7(LSQ9N{kg(I3Q zSVUrBi@-OYOX1gZ9eik{gY!mO*qRpj`m-IFyD5Y(o>+}b4^n#yZ?6Z z`KRJ|AL6UZi5$|e-rTz;DirNK~sGd zGQxGS_3lBU?@h)6m^XJZcC>DD*Oe4mhohq-i5Fk|4`#oy7}TP;{8JZT=+GwR#>|)n zDH)`&0U78#K=r~%2m&cAxDMc@5dup5Te^5H?z`t91OpbP9CHx9^PL;f*ifr}vvK1V zeCL~YVgB5;_}v|k54L5`x-6!IoCjb&cl&BQ6Eqvqa*J8%m zr{Trt-^Bd4-^a=o8?kWyYAjs191s2dS&Sby24|dgEIx9{X$VDJKS?1RWwAzxgFwL6 zXVISxnP|~UyVge#SJxMgcAX3m_CK~42Ib^5XS zvXGr7c_;`h+z2!dDwEFjR_I21SDcoj}P^Dq_1&?|$KR11GT>pp); zv|BU*DRRZvzmf&X{^=)PMk1C)Lt`WU`|DrKi+?$W4jqgyefiV4{J{7Y=A3sh z{j@16W+7n}5_@AmBv+Z7r4+ivvohzb9*|{Ah0$|!v zvB!o=o&{j37#{}1ULzri`vR3Z$KQT_2WuB3BIONwYU__gs3S0Ugkg^u0nmU$-UgNe zc=VJod228N_9@_)31`{h{k-w(BHn;gN|Q0nP#!1?apdSQ>Ohsl#lJqFs1I^{;z-wG zD=VQS+T43V1TGkF;n#yLyt1AkA*gbV6}e^7-Jaiibe|kK0UR#WaZWsPoO-2F9E;!i z0JCQ;!dvr}VEMaiu=t%-Sh{2dUVm*q{^w^mpte2&W-$bUM7h}VEZ18s2!NjWmq~FY ziCICSGTX+I3FQ_~2;h0OGX2`ygU@Y`F@`xamtftxO*rP5BQf#FvEbm!qL36>bqgWk zWV0ANs0qw=kDC$(DT0W_)98rD5bJR8=%X)V!ePxAHmpHeH3q=ckx}BxdnJWQA(b0A zk;P9EEg%wrQnxNFTlT&R1R4lbZC3$w1Xn`a-X0pI?zw&L+1J1w2Pd6+6f6r!^9*bn-f2=tg(9Q^ zU;lrf#i56dMAngDjKIlqbaZ6!@WapHsYhqwm6zwDs#f4fKmH;Lk2$gX`TY&+L*L?xcwE`FD??+gy0r?4Z5fWN^jpq*qMXzo-y|>65iJIDNR{o5>Zbwqz0#_M z+j$+K*U5VT=-=m-**=lkq7?N(rUqD2NPS<+e8%^IC|D8wpYSE`Fc=Iy!3%UPlaSh% zkDCXap=%fc&;YI<$Z4kfLSrP*tiGs_2eg+2K*gW8bTSar0WtIdt}RKFF^@mfbRUz9sDxw2=qgOPoa3bpX*{w#i#OM$@WP@D>gua;-H{Hy zFsTNTbF>SfITVGU3<6x$1InR9rd(|ZNr`GA5z7EcA;FZzSXcd>q_3Pkt5re>Sk%I^ z&%KIgpP7ZKXcexw>U@kHGYpPS6ep#C6_l7d?I0X}_?Pfs*WH38OV(q~n+vhMwG~bE zbr?5xD3&hVfE6p&W9`~47&)@pbyXz-lv;Ra!Mk|<^|_e+CdJqZ2Vhw9NbJ}WQ^_~R z9XJvebzO&mYtDydyB3owULp}niL$LAGMOw!j&$9FjR4p?NR=tk zefC=wnFUy)lKmTmKgfn~Tklpb@Y{v$cxDBG(;frKrt+~sIU#U#ru7g{f|6WIZL8&+ zFgS$4f`cGWK@ti9%eDHWxxW-4kphzrII+~kjUZHQkANe@Q?8H&OKs1gO#M!6p9a7g zLzspzzCMO)jvzR5R1KtX0Kx{)6WLYz)RUHwy!g(EgkU-(1T#W~VSj=s%l_PRJ~t>P z${o0`%d{tSzmoI0Lc-12DNKwDL)S0@pdk-0OwtAVx-7i`l?5w67%M4550RwEw4|)Q zEuL+Gd6;(x>399le=ACgYPqwVA`EdZP#0#nY+MMJjSFH`ieS$AH2&}=$G2Z}z(oxI zHLVVV0v*`U9!I-VgGOd4SM4mJ_}%Os7~B-WrQ^cb*q%jWh+uGlVtdL_@kc32XaC)) z9U>*a-TQN(En?!aIG+92zd;eeK?jY+_;DkU$!7BzXE_Hl*)+n@AjXX!iv1;DcmmWcY+AWF0opkUxi z%oBPmQh%Sje=XrU0eg`|bL&Vo4=!NIta>l?I<8GVRokMp>p*O46xT9|ceev*ib1sZb>YegC!I5f(Sg$sGxP!~(r)K`2s2Z$nE<3r1mx`hM83AMm0r0T5tRVtXQqnJd!R zzz5^|uWrVK<}ju<*h(0z^vkr*(9NXq*~}XZ1CjQR=yvQf0H^eIX$<`ZBLEuwf>@eJ z+~vi{p%wC6^^VVCdLU4F6Xb?{4Au20Z&T)t*`;aQRWLTPc?WmV^?VJYA1@g;CZe1)-0EffDX3_1<5@d=eST zREFE3LmP1JIVa(N?|uZ5H{zx*{t~C2HU(qGk3}TPkc>N6y7XPV`tnR9Vi{zz?U-@S zg$Rd&aCjEBZQ*NQzY1Tx{#I<>+<|-V`4`@Ldl9A`djy6LAEqq%)~#EQHEY%*6kz~$ zz--`>kDcSX(FpI@2!a-ala=_xZ~uvK#8TFE1*12qe(!JxHFZH;{>h6Fuq@1*yA1Cw zUyDNzJqS~$O;o=uW&NiQ8jRY?)wq;3op){;o0^o+$wdb39m)LiQDa6R5X^1E!Y_V) zA3k%Ao&Vd6CMhj8}YQIX9t( z#OAEP<12z7Ruo6p#_)&pBN!dnpN?HB3+TTMX~#ETN?|<);o0{Sn0ib=Spm`kVdd*S zWJR|l8FvAHEWv?rN)5eCQ5N9-V5!^U_9P{VdER@nzL}|PAO^#pFan?fYjl{TP1V4v z1|XDmKd-9m(_)nkQuNng=qY&{t)f1H_nop1EK7ykp9VJl2ifWWVhg~|BL=|*&KeoQ zd4~x+vLua5V;wkYEJcGQuxSUwQy&ntBn8etz79c1Olq?5tq)0j|Aja%{)dJ0#-`C+ z=U_`pU_*>z2e)x#gTxu5J(*9y5?Q>pnc(Bk$5rg8Dv?ry*(?W|Lmb~ewGlRva3lkh zw8w)c?}#a(N`SlOa~EUVwiZ0~#7xB6W4P~6PeTw1La9m;lFbMuYz2b?AHV!OT=wyE zRB{x?m~yurF={Yw|IN+#;rH*sqDAjx_RK|ibJj9QLBSoNEMGtgs%mP$;UJSv<%GiYmX2eTa|e(rQ$^zO!t9RbeM=;+{h{{H6?3fp;?ZA1By6&zGSg1T13 zI({YT4+Gb3;3eMK7UO77Qe-m(r;KMx{ByWemb-oF{Y>D+40VP4^A4`TZ{JQ~{dS6N zZMyVi07d2{vUjhC&r!m(oQ85Cy;=PJP^s!%H<2NKBmbsA2R?xrt$zc>8I@tYW9VIs z0BAr^kCT{?fuMu)0}fWjfw`fqB(eU{4DObEEHGKgVX_VTl3j&qlVzNd?)NudAK`c6 z>PmP~pEmw?QsP)(vZv^k6)PLJT?2_cm$HrI}P{^&Z4tWDsa z`3_!qH;y)*g%uPyVn_sMPYB{4b7T1VE5MJZHzGhN4ryZ8mW^Y@S|H0JU=k|qe3}!C zk0ij*pyS7hvR--Rk_sRYvhbZBT!&MpAA=WPcpV!yZo!Tn9msO7tn6y4n=pR-7@T?b zBuqKxU{~BCZlV-QB)B6ndQ3BZ_s6f{nP>iknXk{o+SS{TN@Za&3*kr*qedTqbIv^p ztJZA7uYPeio_zEbj2$x)S6umF)u}$@&|x_Fw1W|?iYN=a{AjpPTJ_IyIO^&{ux%Tg zH@9K-ti@<-s==wJAL(A-5~s(r_8T>7Fityj5~^#$sH>?`-!Z*R)dLk*P+;8nVfff5 z&cKTI*CLT@L2F9~YU`uQZ1-E={TxP&d>XUgScF6}pM7VbG)p-LW?s-STXbz0S-yI3J$Ki^s+Gckh;!w*VWt~ zR?q!>3o*`N*$m?%0R(atA(jQTEw$eXwL#o{NwYgBBoZ71 z4#H}aq|irU<}|QcZ|RV7o4PC9kCQrsU$4MP*qh0+ja-Q#~eEW%idj#=l?Yaqec%= z`{v;I_$SU*uf5@g=U>OFRjYBz^vO8<@B>wH5=wU-LVH$*r=KxRy$Z;kbVfobEQ{iX zFJDpkm7eeyQX*Ov#20S32-ke>JhZpBBNPmxy4p=JFys6Wsh5XHU>Sy;n$EA$m^z*qqGb_!Krz~2FJJ`MW>w?E&r_{;ka z_ZeViL=}~AefQFpGsR`-U5o%|KuXtwb{jHlxv>CqpPdTnPv)C(u2fDp8Hg5XD&!Ci z`wXd_eS{L0Km>@?*-A<0m(Z0`%upxSPL``p;}yedF!kg`f4+B&Tvf6;bxJ-lQU)QyFQ{IId-*O2H4F+#YV!$81BLqomqY)NpQRb}_Y zG$~~#A&?QOg4jEPt7EIK3aOWeBZOMVq7>JE;ZwNy;u$#fuu*U%S0WjgQd>A`m*M=e z`BS%e8JwA__w(kxgZhRj&N%Z!>RfZqOA3Jb<52t1);6@D$d?Mc<(&&cTv5=wR&F^t z>;S`{rUw06t_Ss=l_pW=iRvy??$}8_dvW2I+)T!9nZls}!l6d>JO}C0xA7yjs^mCro8E8$ip3F&0U(pxPP$%|srGl}#qW6a+3`XqPUOrN8o^(oEg zEpBYAQm-uMDq)5vo)N|hgT-=oO>?9#4__Dv)}bwu!UPJWp9FB|MmVqbb>Yg0Z-UtUJCF^e3_W6J zyQSXVi`&WFb9FTpIybeA?H9>;blthA714tpe(?n^(oh&Z| z?qOfVvmkSadhYD81cY+^5}i_z?CQ#N8pbP2G|#cE_uNoCi@Z-3r7H+Sg1$GcBeq=rmim!{kdtQtl1 zWXs+@4TW?kZ5SfAvy+P*=I%# zhJgxqJynj_5m2|&{9L=WJy3Cpt_;0{5daOi_I2Q&01jH|NGC~J!&!+ZRa}9}Xhyu@ zr$v34TMdS8;fXogWia>DCA28-YEJuo}igu2#NS4@uJ^|d?j(zYsW z?jUfo47Oybt|ltk!`7G^-qaQFN^A#S(tyGchWb+oNOwi5^FQXTQK;*l=1GX8Sh&)I zN6{&US0R8r*~zk}zaf>o@U8^HuHjiKdf78wS#+;Xhku;Em#Z2o6Dk3b?dwvM3x5Ug z+VoKWML=|#6cM}CkMb?Hp2bm)!W7t)b}(;s43kFGU}%^?3Zb4CP;vTLrx=k!N-Z2y z%-onj#^E?|L^UWEkdlJzZAgJz+cV^;jJbK zmU}SY1uUFc+t)=a3nmM&q1+H1FT|ynth9`4OSe#0MFJxu7XElq1O9Ju0xMf`y(5Cn z8G$)l2(l8WiFiR8_tYqm&ekIEr+e-^qtMyPxM-vPP+cY6#XNV>{+=tce6^PJNJKOk zT%;CVay8Sbm@4HDW>PT+wM#lri^$z(bN08`jsz?Qyq^@f@yQOnvYO(`L)&o2jJiV8 zEr~nPLkM14n}8)HJ~Se#tN<5mP2-Bkl4y4rzI<#9H%+O6#dUwO@=tqtk%h;lnTR(S zdWep?vY{3aGKm%d<3xSWTt~dv&r)`tVj+Y`Z`mH!Ls4@rz3;tsJBN=cIn)uw$FPfS z_d0tz?*5Ds0K0(Pg-d;LrS7k2;|OWRoN7^zhX}{B0Dh5)#aJQKZvRF4`pG6_SiFR8FPq8Rhf< z_Rq!jxL^iDf5ff%4ndl%ZR6etR}k!~7*?_nMOupY&=R}Q8Cc%jy9?Vk>^=Qb z04!3d{nqq+cWP~sLd|?VC_xAjMaw;Q)w{~=NVuK?h2QJ-SvQ6qMJ6U)Uyl%?Zl3On zk@=ZYiU5E!39uCv;nFcBj9^4+yf$51qcEdM&0E!WC@<3Bu9H zQmC`t?7Ct=xQiJKy$jJJ3_$>EPq}Uk`yquER%Pl0btg;m_+5mB5&*fRDi-YW7LG4I zlf*m8D1ylre0&1MO~==%tniYO{OB{chgVi}#1KK$wvg5_Vh^mfaoZUb-*|E>R`Ce# zo=ZWU1ipPj1h#ZzlTw`qs#J_mQuG=a5Y<5Iz>+(Y~FUnCS09|R`PSz;p;3M7Cb65Gpq>We(?aTfjdokER7I=!jf;i= zm;j(jAJ8TMOZ9rizs;4UPH($Zo+p$L2#E^%?Y0sAG*coVsBp)e``bhHijTa9KmYSUTy@b}I4lk!W9VqB zMGYAV%O*rh4oE8N!#mJwwIP!fXbjkR9CxerE$-I%esysS`z6x5Kiu#^WuB7qo^8Av zlU-(VERPar87wB1jG8+$LnA?qtxw~FEdl&>v4aWK2^`fN0GM>IBaubZo<6;=IA(m| zWB!X`2Zc_(3*Sn_Z`vsc-rbSLpBEBv1Td;DjRWh#YJh5y%D3(@7;sc&aN-A(0Ub0ZOoNM;13cmcVNpOosZv|!9GE_s;#cR@;J==3!G?4l{n?e%(kQp#mXXfSAqB%r%i^re#lyyeqVsQ#PV-pv3`(c+(<-2R>b@G}6b zeCvh*%mgsZw=ERSulS9E^{8Ko0LKD2d@t9#QwYsH!R492`lS`FYdU0Dy%=WH8*cL2NyV5P52u6-~k*~|5|x)J=kPh;G@+v*cYHpTH9D)7b*01Ziq_MU9G41D^6 z_i*212jYqg<^!2VTywspSqIAIkrJSx3djUfa`f{dac9c|IQQ}^==Sy@geQj}5CGcJ z5)5kOY1Aw@0W<)D`v`7_{+T|-hxbqEWgx+cAfuDq=1$SaE0DA5j zmF5-_34)XeBEl26dTJB_3KclsNPh-H&mq2P6=ISP^hep~0Du5V zL_t&vE~sih=WR{l#%J2G%&CQwZN-%nfuEk#gn*FXYG%icwJP;V_lv5BPwU58_M;_g8?(i+YRDS|X)^?<7+^+_o z{!45r&(?$50?+|)Fo2H&xTjbQD>9nOeYjp*>3R5dCU4zq0o+kE9hcCza~Qw~y`4({ zd=kKOzW9I5T9?@yY5@%D=6V%Ne-{7`)D_YrcXvLZD_JSaK(7yY5TFv>dVH-&0Q4&9 z6#%}e%~MO!0^olEeA{PYn%nR90i3v($21o}D}dAYa=n!R#_IRonzqO=cOA}-bB-wZ#e5pS`&q2gITnAMU{Q$5r8?u_1R}m(cR+$_B$ghPdLNO!sv` zdW-#LSlSE7LM&GO*$b46B*p%VbO(}fknGUQJ4Um}>u04|BmJ{oh_$NrNe^|)p z?pcN|*p|hHxPuS`rVeF#Z|-n_Qi`mUnABk7PvQJMs z3JcCf1T60m>L7%Lhm-=#{+|F= z=-(U+;4-bh3F>PtuGHn> zdcEbo^=!Qzl!!xE5%gC9&?BoWr=u^{^;!T_7pp`}JffFX(}mXVzp&QmV~U(a_bPxpZ901f8<-I zb>OdmlN>??eKz`tp}P>j2~z%4e;Cno74iJ)R)!o7A&>AM|_{XAn;OWhepY@Xu;<B$T>qX~?saKq68|L;Tf zI>ZBUb+9%qvG*g9# zms514TXEZ&wP+5wS*9&t`P3*{_M#BBzf%6#D4pSj-53nTu$8{K%8Iwk@qA+H9)es>){dm{vh2(Q8vYJA0*=Ifa^(HS?@gJi<>u9 zyYs$O!X?(zB(Bz)MOJGvejF|6{nn?yTnwnBqkh%`cpkui0Q^yleSgp{yt(bZP@R;IrTfL`VFj3jcE;6vI+JYPY5rlYwE+MrEJq0LYI z>*v-<>k9EbebF}qxJmn?cldsHl=hE59l&$|zt`K%ZR^Xr@(Z6e;yC~w)a(CP`=nbw ze{?TQ)whb)J5#IUIexD9<6qBj>DaAGT9t_FcX3c*M# zLUu^m`EZ^=BAG=Z7DO@uw6w?3(h_Vr`7A67H&`#+zwL0ldRRnd+;ns@Ibuv~40{RP zUB|P>%8jQ^S7g|UK1IoJ+G=l102niNZ#@Efi(F-DhY&#!gpBxSFq9GL%lg)xpwayg zSy3&>o{mQFAec=sXIl#Y^>hbz$OhEN6h1#y;H#5sTqAALIk1kGV$z}n2}ms1PLY;2 zMnoMPRBtI^kPy#$+!Ya+?l+FBwsGf$)%eoWaV*>s!PD>CutTl*!|8PhI5xmUr>uU$ z7YDC$u1XK->BvH_PmPuQ0H^a^C4?KxnNjzy-LXB3w`M0X`&D4|`wq6X##PMcXe5Pb z)J8DK5RQZqm6RMA2_x%>x{d@tp=BFD*VUl@oiXXdLUdEGHu>Hl@!d853%pdn^+3mo zEC9_8W2tn6|K+*UaphTY-1}^F-yXU?>>UxzU{ZMyLRR%k07Gw^U(f=e>+4d`R&oQC z8f{UB65=29I-k~|*K+(&i*5lgN0c{mx+VU^A&J+M=>)3(;-@wT8x(xdEQ=`}S zhjc4$dcF0+U0AOot^ZH~0-%R~PA^wM-|!ar{L?)_?c=%vmh!I3Ew55GweIo+fC~W3 z(aWi?vd;rBL&pI;0l+5!{7K&&N&WKU&m^vGo-1$OoNqUhoHF-BQP|d9{4QS%lDAxu+QJ2hLQ?8l9_H(gX^RYop5h6A zqJlcTyQUIlCg1VMB9nVaWC?){f_babSRJoIO-NwcD2cC5jwOi2gkLg-e~>#4lVu#w1u9(QrP;tn*qG0&*il~^H}e<%;$3s-*Y9MMrZ5gXB#e{JfJ`U3So}^L zv)>4^Ij;}KcfZ<(Nt5EZ^b=J$>_~S)K?nv)EAvJ-lowH)1y~fc<9xqXSIxce=N4Ar zu=0ir#YPOfThDV{t-OI?Payhi5ci$y8v3q|o?H4bj1u>8m z$0#t@(J(OR4eM8_^Ce0O&|sN2ZrBGD*9hX1egqhG!#hkK8OEqLTd_klV(vPL+vm67 zi$~R~SpJe0B~>EY`xn5%%?>ulY*e!(CO5m?OF@{LAf~=&uL_7ICBlqgZOXyzFSejv z)F7Qn;>>XzjX|9Zgzy1wm*#eaa!)itdP)A&;}$_pCg6dK+wm3x%BzbIhWD4J@#y_A z%z7=0aKy&6<82%_eJ~C>EQG3PiF?vlIW2c+Ne0*DUKS3S0GxEHlulOSgQZ#d=FAM8 z_3!kt+c!p!%`6yoQ#ji8lZ|WeKwY&Z5zewy^b%IND`-|PdlSGS07q*x)u^`8BZR0pnbP~~+lTZoD|JBKU9UR?wT)lE0c@@;0^QHO z=zVz2MJaG>M}lm_ljk)tYJ*1u*-_b){gcg`0+$UM+JPCDNpb2~HL_ul%>ZfT?t6#3 zLHXTauALG&DTp@7lAb8zX8$C?Fk_RBsllXUBrd$v!OPDPyztK?ZuwFS=YBMZ^DYme zu0Ho5aX=|E18&0}k)*Q~K_`gYF;%}S(>Q?pY~y#@nF{*Bz)7Vv>AeNnX<$~KJADt< ze|xnRza+V<3T7}=7}9N1S*MxV!W|)dU{&3Tof}X?KVat;un>pU5<~ z8((P0R;LC(c_jtrwBt)tYT!r#Mo3AE{kfYRBpr%jL5WGtRce`kF4%iw2rgkUiggK& z&p#c<>szZ;*5MhWlDO@(I)o?z*GZvd(bL9+btv=kn_PYdCH@iR?wg1cJn>*V9)B>Y z?!I$BYU9jvYcXs@VLZpV*S(U;*n^PL)3&)`H`mV^Aq25)f(a9YWWvNC&bc(m-*_>O zm!3*ZT=N0DzqU5@$#BH_S|(#J4sM^q1_k1Riln@9Mh5pjU2J1ic2p@HhHue=18unl zKwBqCeHg%v+S~ER0CxBcp-usCjBf7!=o9PUJH2(jEW8`@rS;`(@O=lZbtD0-(*j_l zPs`~IH|GIIB~4My1@M@z6jx|}?&6Aaoqj(Dz@xhmK*#EJw(7(mVmB4dn}%GZKM(i$ zxA&Xp((qRI_(r#(ZDb3M9@eH3pkQcQ?*t(G%=t0(*Fur+;k1Y3Bs+*!?kl|0|oZWMM*C4+_Bxjge_$3?(2s*+-`@`UsiNO>3#=quuvK z+Ry2Yp;$S$X@-i1fr`w>>$Bh`+^ephvKb$( zW8b-bkhtJelyj1t(b^_vQp&HCY4B($3gHCRRzH1qFQ=T(y5{WXzZbw+t6Z_sez#Xl z+?f{kxK4ulas9n__O8>n_Zk4t`GmgWZFrhXb{G5c1VJI?PIkS_Oy8B4N@|ap&J;tm zsp?1d_W@m@jsS3$UVe`@KlSii3VM0HTLGks-D9)#KWeQti$mHV(*}HlA5v7q^j*}Y*Tth?m|>G)fI-yAAUAAEP=a_s zB4w#Zb~!e!CT&D7kI4+>hd>~oR)ffbXPFldkh*z-C8gk8;KL&#_{I4(IMCh#VFz&A zn-o8r(*jOhL4d=x@L_~tLrh}H_7DP+WAY%52n8JB$%EQ`TnN=KT-KV!b&qVpyp||J zyaVTqj^kHn*Q1e9l|^1T<|vifiybIkQBW4Lx(^I7Py$q_tjqqG#>D+vL3`@R=T!$` zG64@fB!tK)BaeCiN#eU--hp*%fE&JDi?7_$j1gml+K_{S5(>(g8bhk6FSKjnvi!ZT z+}*3jg%seD!r?$o9iyMPrjdQ)M?pU5fGyQ+?WucMo%8G0AA$U8UA1JEQ1-@G%_t7* zUq+~FqqDM3j9pVBROm8M-z#=I)jC7&8~V=stkyenkLw9KK3|SM>*b|X$OJq$<$}&q zY(|l56((y>PN~DX4fd^{DY2fRn^e-sqFVc%&(@XWb49W8uGL~-i_XSd-s2shnJs<-dA#n1LFz&dZ0Yd{R#91|NpH1-X zS6h{pyf z-~UA&rq8HWV@IxH0y3Y_SDsp;gg`=68zUr;6%vOW9$~lKK13dON{Y9)#&4K*E*|`i z*c95jCJ0M4Nfg(f+e=~VbWWp{jd*2EJs!`!7lV66*YO78cj~`K>a4t;))E2G4&Yw> zd;bA@H~Lon%5ku^X%Iz`5>C@O75+~zuhkZZxoxcQmBmm7D(OUej{bM)zf*NQKhI?K zQvJJ!0K|&YKYMVUod(+9ytZ=)KJ94-Tms;~eX#)#(97fcCg@i%FP4Us5zCRcF-LwZ z_u0ynFAd12KDMqHA~ew==)*h5uDX z2oYdJDl_t7!^8Oc4;yg&>0$io4;}c&pSLKHFxRm&GN7TD-W1q~YRcSfwO#D85W!`J zQMPtloMJ=5k;EAP^<&JC#xQURxWa&3wUvO2VM)TPV+54-G|+#(OHnZmqyIpP2bh)B ze(XBE5-IVCz#^{8-jt>QetSU#V{0;SXbAVtm$>=G7}|LNOu7@&v(_b$6d^Q*984JO z2GF27#7D0GPbtOgTM54KSUXmxY9Snn%MO#c^^9sXsJN6;SrW>fi&Jz22KYt%GF4IY zv>ZrC2$2P)7M}k9?0pHG9YwYNcdEK?-);7ptRz4P$PNOsiQ@aqLq$PA6vYJrckw+T zCV(hDL|i}+*+fJ@@hK{sA`euMMKFXV2pD#<@0s<^-Mg#K|97ja@7$TpB$H%Z((x&iT$At8vw3ec1CL4`2UoD|Vh4H{QPjVbs{xK&=YfNN|9;G{kOcU#YR zvRnOIF`jN@)Isk6u*4qwvOV^3>qhF=7zjt2kzcUC+X3ubvvC~7rtfd9)8DmRgRtML z)VCPG8hdP;nel6=P7)G0TQE*Q7;rmg{UaIcuq5A()E-ppMEsz)r*!(ejwuFTp1p(P zc!Gj}2a_~&6y?FypD_pd5K==cAlaz!<#Urb>R1oIy1t0JZ|jC5q;dBi2nea8j&QKi zw7y5mzey1#Uu)$#^siL~V1B1xXUMIijykq6grS{G8a;u=i=7HAy-sN3)GW6|1t7ay zs|aw%4U15;#^Q}oRgVYMn0dH1LDE2gX%uX#BaK#&@z6Po$WhtcCg}o1vz1{vjKqF1Mw5zGM~RtPA)O2t-KG>5q}3 z{E1_F%)rea$wUl65`0EJr{?T;XfZhb%M&FDf-(flL&5?m48vf<{u2E)maf#oZZ-(R zC80IJ^)$Y4RvQjGvI#d|na2}zb8uxvKDAw`Z*^=KW&ja`t>7`Ggfl?JSi@}W<#$F@?uu>o10zpRatJl)>%$1OkL zhWrm50FT-7!e$M=r^bOQ;`jTt%{Z)T=+?dkV41b8Y|ZQ2t}q^l;3&yQ-iG=9+9m)Y znGTsSQ2xG|zTA~lW_0N<%}z_{5-A<={e?fZni6jy_`{sn z;c!>^y?LQMRHb#Kin6GIL<0EIISrV$QxZSBtRE|u`o`sSwMFfaNu`Ba|5d#m_=?m~M;+T5xN64fW#Vh+7pcs#lB0M5 zG`T`=*^pK1Gd6x^@S*%xDp3yentV5NgDw63I;2f8PKIDl6G{NF~dS=i2YrgcHBe7@Ei z4vzrP3ZMnRUI1RSohEO;caaSr-sUXSFHQ_Q{<0?Yubznix2z36tbATIb<4f z=!a+@5T2uM>I+iA)$@v!wLC^zh4D<|cX?Px9d&Grh{7h#{HlPMApB}UKhIy=K=GLK zG!%DQoo{ewSuAmHCuJl?=Sr2Xh2bw_h)O;*cg~r1M+)M$56Orj9o& zYB#UhB!v9UO<62_o$;AjaZH--nss34eJ83JF*djrVmpi0+SWN_{TMlvm?iwCEq}=- zxT)0a#sq!tt8LWUk(GsfaD#Npje(yqBa(_=y=Av$(Dj6=45pQmm{v@Na+HQkZ?IWN z*}2yp;x``|{G@e)T5BDmsK)luV9O}BFT|0`B{s3#xPbHT7GNuYYP|hUw$|OK1C?w?9!Uj^!hN3saFPur zZn0TzDnFgJ12@-n7RMgj)qdXg=A+i|*C!q=;BRYyVkz{~o&J(WLvcm76LwWG%0P|C*ysqrR~ozT9mfYhn#b>c&Y1VGk3-+(8m@=2%1d0wwhJ2M zVKC4kDxWLePShHTH+e*;?OmfDqBRjxnUa_vcat|ix~%^*KbYGcTy$8gLqWK}=JD+$ zQb!$iY-6Y{@hS^B2l(5HB47DfPQ>KIbw1JGMX0Y0H-c8C>rLLEq^%tw^@gH0xKeHh zgZq%lc902=4lm`R5v2$@BJiP!sZd zy0woyZzChA#_Q&HHYY-*u0=KVMuT|4I$B+8vkC7Dpu=9nGV4tBYXENraKClr`UZfr z?PRBh$aOzh5iowr9iHOpFHu;LuIDsAD{0yubV1?xh{1kxa09;;^Fl1x0ZtgXXDH_*=Ci%bb z=F;3<(ICs_+KQb`TQ;kn{y_yzeIz-pS#(b-6w?>%x~Jfyk8L)52o4E58f;iWic~JP zvpfml?|;qUZ}<9GxuSq#A!fRAOM4D`?F)SHu(iOir3vUx|2X=5_z{-<6cPVD`Z&$FbbN zQmo%~>pi)SI_h|1VMBg3BOn3KAw0CI$YKr)P95UCey#_ z7)wOqHDq?wgEDZ(FU#nd$oS}~X=v7#6n6Aup9vM$Z0M$*c`}cC?kHgCB8_|@%;eqF zps?q@j1PR+!}J*m6I#5fX)vSryrO4o4ZPz&8|CAF&**~_O6>&ubcQXrFz6E zJGAPXH~F3n+n_;b8s|th`44`?jbmm2oNs>yog=f6VphR74UwdF59HBd!gg!z7|~Gp zLQUe;;qZ3=mslIjPlk9I*2?;b{TZ&k4PE1g?XD?{8n>}mEXUs@%Og0|jyTJ$)%a!m{mB51x0dXyhQyvm`sM>Q z?R5<~HyomK4-X^fnCh?-`aqdVr;j^L9^*wJjWzwOm~@={rp3_?0+9xET1=XzRA*1q zmjab*J@Jz<KUWqKvwe6t6xMeSZgFwz_F(gF8M|`9=o4#^as-jDkHn8RBdO< zcO4Pd3;V4(Zb-_-<{JoWX$5uF`*q_GWcqvpIlO}jL> zqADW;0c*>;HFyzKM;&$4v9&Y$_*Vq!z)i&|gc@N;D?SebAB@Vj2z zdQ%>SLK~!`;3a$rcCluqK-cORo}HJ$sb3--epJGE{)%eO(9x~+sx4Ofr9*V$Cq4c0 zf|8fZH-9bW;xke;iyyu)R&2C(1+BRZ4wH8p_rBY%N5jcrb~gs7Z#Z zs!OKO27^Hl8S61B&=5o4w*h?5(yfhT+#I&OO}NHQEjKQ(tr`F$l&qWSP(%;0x5h9y zwBr;;C>uf55OA%wKSL39oc{)}62N)ZGjO;!JHL_kM#9GA++qawDoYYV2}}}f+ssVFX4oPdtK|;ZlNr{klCQLVG5Ccz!C3_?cAIG9t{jr0em=c zZFoV5k*njVc~@)73l6^Ir4r}dwMd@)*vb-4*)y*{ut$PsG`UCZqfR zHymNOmJjA-KI5?^KHu`368~Bw$#+s~9LaYo9jp?N31QNMiEuY%{Yo^Ep=24==*o6o zqzQ1wO%~vx*gh1J>H?cWFCgAxhnKAbVK@dEOroj-Y#&bu;gxD(v=+1IuO8=tozX1~ zq0lwey!jq~9h(j7-@;*1NUa0hcc;SC$xV32yHkd%(}A>ZWV``t0Bz`^n8P|8$Hm-- zbGYqhMohF|;?5Zy^?nc2W&jQo7W`A=fj{@7(A$ikU6DciRKm=+#8FlOr0^jqJ__&Q z0SE~&Ys}muF7`aA#7{mdKQ?h<@V%*V=k;KX7N;G{`lh?LbVLa6l~(Fun~T1ZW)x*3DhAEW8{5Q-5_POM(2qve&ArC$0b9)gxc|@Q;Gdtp z1pEDL4x|R=fBq62aQz(2|J)^G-BhF<4w?q6rJ06tO|5=qI5Lr8I%o7PjmTm&nIJeS_J-F zO(+O(f)UcL8U1{H^3uu>*oV*n=xmZsR2k;B^=7EGENtJbpDfx!NU z<#FYAdeOByjk|u+jXB?ro5WNh6|FD^J(GxXl-o`=0*#J3CZ^^;kx%yaH=Hu985gl2 z+E{GePB+cQ;?pk0jyKJQs~NAI@k7kGaW)p7e(B&wDsbe7DOxUwZNnTs)F3z%5m&3V z<5d3U=-7C|&OiSe7M<}!?0Um21OzNP<1+00^Ruz&jLWxr+heW@Id{!A)^zHu{Cy6+ z00x$om}F5?#PB1)_^%W09x~{C>b#_)-q*RtqyHy!Ila zlx>FV74Wgs+R)f2Pz(a&)*TvZ8HfKz8c#p58jt)nhNm9am^LGCjEJ76u=8#XCQV4x zG&l-(ZY%2nhk9skck$Q*1!DkYG2G5m9orKq%3g`F?gE=%*}x)$G-)Lq4$`Hvx`*BP zqj5L&`F=lf$fK)x(gVwNqte<%WQwOPbos5LjymcX1qiH#Y{k_ihbjJDViM_^5x1KTPzenha4F*=NuY|H9iU*W0Sb2g4bWoV%1UsCW$qxe5_s71c)+@`>1P- zf2Bad6`^mB)(X4rnZ&!^1KjybjfF2Z;{Wa}A>F7U1Y@UN2|MiI)ojI!jjSU><6~uw zu>0N~O`TrG^4IaG(g~pw00Wj@Q#apEWYa; z=;I?BO2|T%{jjQLLB_jY9;*lOZ^aPX$)IE71s%Vh4HOtte>oek9_A8WE(v+tS9QZO zpXD823nL&;x@@bpKV}9%_l{Xem4M!Z_dxRD1@uCj5Kce}4ykmJu@5z2MQW^Cd(*)f z2sdWYezU@{6S1REjw$>m8j@TMO+k3FQN=i|V(4{CvUA~5B`3E+-Z2O2#1T9?WgkH` z>aU-X+c^ku#6AZwH8waV`G>F`c!Tbe{SesQo#l(m_{C3q@ahW!rJ@H-3K9vkv)a&F z#MEgi!w=y~22t3JeVgrc#_oiN{}Q0Pvl&-kQU+1kgdC@u@;L6q0**ha#k5Beufql& zS2%zZ+Bzimd#i`%p9;|5!)RzBFf044>pXzl8-(?c)k9|RvEF*C?OCHAti$C96La9W z3{W!|1upY%1z}~%kxs&qO+;o}Q&x*?8ykmp)KSMa3aY5I%s#WuGNV7xW@FWba1xq@ z8BU2s=s{ImDg>MF0$oH{X#d_M??2;ESQ_iAdb41>j^6{Dt*jVRJc0M_Ot|tHiFw^g zeDcmN9JZrpGJBekQy$pRGBY9bfUSulZ#{xzckfIHv0Yf#<{_ zWG80q)?l1Qqzw7oO|UwLlux_t;X=y-3dKg;e2ovSwc!d(r;9l9|M~dnXOnPZ;rcPO zO>H11hr(nao+rcshiG2I5|_>cz5dzpNtv%l(i#ma1WDN3>L?1i$)VOqzwfU>mH&l;{wA4=wP zcx9J%+2TX>pp4{QbC5rN%p+$F#>4>V+&&9_Qxmd1y^!-3$is3C>aWS1iu-722>61= zvR?gPy}MsFeWx&`c)(~A%Dt`s`Z`mp;cgCi5dH>)N5Z&91g_1{SIodiB(3v zS0Ul5K!1jD-4A-O_?2de7^AVtf$J6l%A!~V_CF**JRX|wh-6hctV|e{N3qAAP1s@j z8su_uq>>UuaTJOQ#lAFt{nIQGP7d!qF>R<4#KtypA~IJ3_B%9%#~)N!^s}# zBYgJT_Vce}(-A#&8Y?c}K^RC_BZjoOX>cN?J0KvYB&`dWmXOA70M-tuTBz@%*HK3u zZxBpSR%POjk=F{=}h%(p*;VH`rH0to*TDI}J1K_rX(7?ua(@g}xR}M;J z!K!ZjV{Hl#_O#=nwPkAVwR{ZIAc_{9|X8?nDA9x$Zj`srYx!SEN;go56+g?y^?|zOtyp zix|g4xZys+FAv6}f{_96ZvU__8gYysMIBoiGrI_{cfe~~>i=)X!&9PMzFZ3`cgld` zWfG;nK9&029F(Mw;6pze)3&$B07!qBjh38-kCiCo96^l@%=xV9bd>98oKZ~Te`HQg z^c4T;w3&}z(UaGDQfYgc?zSCx*5D@@Yr@1_9% z6;Sz4q!>^B=MVDX z2t$7fPtDEXwHMP!B{hydv4nTLrx9QeAO})9Xq_k^qz?^eudIzqfjynEL7%itU*F+FL0m2$f%Ct#CW27sL z|CQ1B^D7g^_a|JSvjV2M8oxZY0arg=#G{Kc=qUhYcA>Pc8uPTtz)M!Wi^2wV)#x`c z(~>nYYHRcpHe8o%+Zv34#1+x(Y--SCzemtQhkc7Q8i_EyC5e;w_3)7yvC!?5g!LmF z&h#~Yo>d)nY&pWU)vGU-Or+J!-NNue=8^Jfhatm45MFtqglC?J!%YH5{AUU8JHdl1 z2NKe>b~M5D0M?;VanSk^jMg^B*Ds!i-fm@H*@`57fpNz#3;5@}20Z>y5$`@Whv_>d zpmpf+yCFm@Vms_8skI|OZ)ejx_TM%3!zGK0`lYE0;NCSGek;LST@qHj1vm zQU5y^4}RoO5jfLH3Q~Tf3pa}oJl$Nln1Fqu^^!o)Czhqkzr8uOP)}Z`dD~AsJpLeU z8Z`k*I~q4h;8p;SIUreVa{^jjq8QPFJk-e~aW{JTUNF8%j(plt0df;aI{9oE4f%hw zz&9(b>r+|>4$5V*-^A&8bWBKw!{i3^9BnoHMX%evbXtg7#un|9i;sDID zy5l>vQf8o`mC(>CtAQ31rzP;-;|h3XK?c3OKDs)~NHsd(io!@6O6I}U9jA$t9W-?G zWAVbufff)2_7$p!0B>*5mNJiNYdrbd8pkxzOtb>|2iSPf>}<1^*Dcv~)KSNq4Ur|w zwH|--Ah39IjDKV+P?Z^b6GpnZbLW{5Fd~6)t*C|NeK!7RL7<%7SB-(|e`lm=u(r%_xddyzS?MIqNJ)VyDK8|bG$AB0E8SoZ#Bq%w zoWc>!aO8oz#h$oUHPW;_81=UwP)(rxYcFTvHTal3tsy)|!%{;SL__P+#r-G*9-7*F zanh&S(bnb;BA%?MzM7pu*{N%Sh|T|rq+@<+@xhN3@$54>Wb=g8%Zr%4Q>umpd;`}* zp+{pwgQQ8*`>}du$KDfnf)v74txNVIKL`kYg21GTP>>q$ov)T(%mN3HmP=)zKe@*v zul@SDQ{JX&O*fTXS<`4JZ>*%#BGl=DV*PE{R#0+@r5{jSyDO$G%I*E)jQE#W$_st% z-zJr~Uus;S8FPhVpcIozu@)e9|9B3({w%T|zh;Zt-!yC7U9*tTKqAlZ0??mNIaGM% z%9~1%0Ush(_Fd!?{!?n?h;ZKIytuYos7zcDE!4tJLB9(wJ0{G+ zu;loXIQ_s<&h6DkffQFbnZj+VA$Be#eVJ0u`x2S*)tdRc09K{5MP$>BvT3WEp zJFSfu9q~+h(fk7^w@-{gI%GKe1P!nF)DEM*ui-1eO_a@!t7aM)XvwN#R?%9|dC#5? z73*+E*O1AQ11}!Bc{95pmS*>cO)$rXKvg4R%mdmcCa`X85h@^aO|aSC>e#+enPQcw zQUq;9pNlay0J3#mP}T_82gV+jNI%#voMY;!qmFF>WcAa^0N825ehF(g$M}aRORKfJ z&?r14=FWZG{4SNUNF*AB>&CcPEHKf4-J+snl#3-aH8;zL9(u%BcOHKD5fi1rtn7C* z*DhMQCVGoVKWD9k4IAZSHd0L2&z)ow8!wx%aQ!jHIqcYLD15vouh7-qkAbqp;N1R5G6 zx-GF@sQyqWRw%0guIIz`7+Tw&7gbJF^H$|_`=J*GiM$1wGI!0OXdo6Y>{u{z%T2tBWeS2Zy0whCpbaD`h| zzg3=?tp(1?$8l7=T)xrKxClgx179KG2`Am@tLA0Px$9T6&>I)fCykttbwe2Stl9qS zIp~CfQvmfR9Wl8(P(2A!#I!4EKELeZRG|gpd39AHpzrsjX(efZB*``v)8D@%%l(1E zYT+aP`_G~H;p+z~pY=ZAvU@k{0_xauG?l}cnkJugy3EVm0PA`Iq{@Je*k-|y3cOrP zai&u$-$ai4og&nk(#2^~@dZI5eho2|GJ6SndBtP$&Vw!vPQB~Dr<8TMW6E^T4Bf0W zSZ+Lxx7nE?MxX;I#o^>m1X=aAvNQj%)eP`5ns5=rn4lpdV@+ki4_uT=!nliZqfRHzT5zT>zkm35#sPk6?3+f3+14{$qH|oA-oS0BTK0rJ9A-T3gS(sQ`90?*E}f z;VKoV7XjphAV4e`6G0hT%0Zhp4FK1;pbSbG#mu^(wJMo)t7+3PmN1R5QM!Gj_QBZ@ zT&iWVt+~Zvwi)vr$Cjgeh}Ti1YYx|~D5SI*sqA)-Ej3eEEv0@}#}~raSHdq7x;qn? zzLSQG$toB7#!z|hwb_7S!}A^*qh(b;AN;Nk<3VD_m1t#0f~ucchb*$CGMl6#X3QSN z`Y~K65Lze30k51c#!EXpczOAvJzXDt&JI+@x!DAN*)b_U^z};f#q3}ylnKp~eY;K4 zJ7!((&TGUJ(E8IkSn&BvsxLv#;k)7iFyrTQ@cL0_GvRV`e6Q>0U~MY=R!1I~kOdw& zD@-V{(@k^G69*bfKx+|r?hE1Z!>^u$g-t-w0XlL(QxSOT%<$aIpU**e0!D_mJgf{M zcKF2{^m{;S9%wEC&wMF7x7W|+U~O_hhKX6=;UO|KmVlNT84kT>4i+~K$WW>8$r>5D z5`eb}GVEz(s62&AeUr1mgJ*~5cD#8GdcE~!IOLi+Sh5*1O#Q_iWH%r~1?@RtNuxEPv*o069Ep?`A`|xnv4)D3qs<_^=Y}8S@wO#S{v9(Z zMHMA#S+Mp$|750yJ%4kq=yX^XHM8{c=sEuC0N;NeICmdR`_r9!$u0I#AHTW{`Fr0!y zy1S@XeD$YBhW4w@q+*jJDDEJZDWdB$S1a(P0L@1FXJ-n($t9r}$=;&w`r>t_OvjJU zq-={6sv(Y8wup}9{c6#9HyRm|Kl`#MH@YClft$!8e@rODem8ytuQe1zUT6rZkZ#U# z-@ASu)_3b1>UMonM2LAsl;3}?#tpv(K65e}em{r$T}7hoBi&o%%;&B*Wg33+dE}d1 zq0%1OInD=V8~MJEpA$aQKYxQdl|tZa?0^K0dd}xp0Gyml_04>#uzN-Y!F{)%OMCXj zf_XDrOqofN)ry2Cp_l@wr_t)XFSQ~#PRC>4^Cd1T#N_9kfX`2QvD^Q3=wE}Nu%pzH z(=nkEjme0FW%Nodkv4?MwaT=%+1+Y)YBmLmHHu}1*t@h_Ouz!6&POS4sp$ zjkw!@TW{z_=bCPO;3F*%&M0(rOGMhxsx=8MI=ZJT;vnwzr_U<6!8e#x5}NLf`{IKB z$?_?5meE#nV(}OSvLsc`@zJP2MJWtnRYTaE#(dBGal{%A84oDMSiJqZ%Nbt?U%N_~ ztOk`_APyiY1~&K88lotGm<-QF*2Ja~kYWfeftUc20}i~VzC6q(72=T$FU$e22}YOhI$GRRsPUYP^zb_vo9QKsSLuEUqCIoG3bRw3hl}0)NYi(PzQVrDiL7t-X&*Vj z1!TUIZCM0oD60Hs1WvJfPLkA2%0z%FVW*L7 zI{ZygY+ZSk3M+=nJ<1{iHFj13@|pI@a)kZ_?UHlT3q zx6Qeo{`4bsdFxcUoCo@@2HIzfoCyBdnrQt?$uE4iEcrrT&~5IL(tQ&%Q!iNAte))1 z?Lghca2x&5TDAd08`TW76=llv7(HDL`1K9_NHqCn(08J46lojlS|eah zbdT{SH90M7cBnT1dq9N0D_|{xZ`Z$DrJr$i4j|+KEUfaLqu#aasH2WI986V3z>H0? zO-L59QB=m}8Z~MC#<_CRB_d<2Q$e84C8F;R_?nrav9bn|VP-Pys+2l6kw|=m86T0- zd0J~Fh$7WmGyt3)XkooUwH3ggL^zrVr?FNa0I1XWeQe>@(e?_q>+OH6_3_wp1#)75 z)^?*eoR1?ux}k^swiFy?EyFHSoKdaO=+tShBDaU!2_rH%1%wPP7$INX1Br68?C^J*Q?82V6xy z9{?fdJEh>;!5&lp;5@$;9V-QtKj@n*pLji zOl+tOVl!nB!^^P2`%oi;8Y+VrkYUKZGxa&OGB_KNK@65b*PJU0)+HHEK*m-xGY-6% z7kI~0+P(X2e<>x}|3dw%P9iv$l^opoi_>;FvoWvFFL`udvdp0`(q;^`qcZ?9$aheA zdp@>cm(+g|V?S$r>*ZwcHJopMfQUs)(cfi_=Uf0K6fv}yb!Qhp_}wm3hvrX`(uXrx z3x(%2@YBj+spARK{7oSP&o%C^^fLf28Q}tbuuC{eYc7#OCx9O#Ft?3e#izT3$;MEs z=>Y(9D`i~8lRbrxLBl5r9ccOmfUG@tWK3ZXCRR$*!W{5`kwGcX)i^#DKsptDL7U^N zE9YX0XZQiXm6Wm9^#k}@7>#1;J4`Dy2vxZbyaE`4>dNs(SMh$Npd7_oXqq2c4IRZh zNyV*@&|uXAU*e2uF1$CfQbOP%q+Cl z-PLRLg-e>-D&weifbNQ>%rqL(lKSF+&yH}4kG14;Fq~#7vAQ|e+F8_UlA<6U^YW3W z=FWbHvmn+TD=MWYwtPz!mS+6UMk!q(iaJ@i3Yo-REchHn{N;6x`n~M*|LBQb7tV`! z?V*Jmm})c_f`;#MgIEnJFI7RKbEShjZYjZ~B>vct$BCyhjyWl9CIYchpX-{=YgZJL zCcC)sM=f~nDIa(KI*a9tTk*$VWifN-0uFgs0$KrLJ<7Gg?ZiESK>3EFr*0wHej!F< z*xTan@rG!%Q6plk2?y(_qmDYZ0AseaXFIqVFOCh2%ZD8Dc0BRKlY*I55a=%wiSLCK zP(`o0nxXGz<|QVp1F$zie3F?yETx;vFnkoXP>`FU1aZ2qNhzZ;WAStX#~S2*+TZu~Rfj+CoGeyO5+o9{3POv3zt z1RO3^t%l|tgvlVRVPdfdudJx%7Zb9@GS(FO@jq{CG2Q@j`@HS{9#{jQ*k;&Q>KGlu z_q4LcQp!g%*^P$r$Dh(_X`(P}MHLwoGv?9)dh+n6(Y?d6&oL5^ z;Cx$F-L}r-KR{`k*r!{UwW(`e4VM*2F}{K0E?WS@fx*XVh0~-0`x?st{5N7@Cd^D6 zKgoEQm@e@hWyUW7n6O75*he$I4HZ(>28_=GFv%DK!Yr+EL}k#_jE4bSQ8```XbBiU zg)&wOp%~`_c)`|pJZqdv+R)i90dSVdz65Zyln5?Nm}FM z2xV9Z;A_?U09*@=NmhoR0=UnXIY2SKNudk{`z%5kI5-y?hg24oq;P*~LFt;@9_@P9 zYxe6{K83GGu~!R@^*81G6S|t5BS8A1Yy$UE3HK1wB`D+X#5FI^C@~rXAjJ(&8cTqW z@B-}TOM)gY38qxCKRzM%{%Mr>(?TkErB+9~pFkkt!9_=5@`VdS!xNO_AC*w_Da`_@ zApPeqYXDq0>9vGlK2IQqoXcpo2&qz2A~FCjO<-m=PL~CO5$8BM*6j4BK6{hbC2?lS#YvS(KB>}`2WG8 zo`kryBsHAMQ*-pO0RB`twX zS)G;#1fF;h4nxNP>B#ATd0~-FxmRdceozP?)!X>jGH`U|`12)hD%=J3YToV);PH`BV%lH4*r{I&7$G-Rb)7Ryx zRNRxy6fN`y)zWB>{hdd(VCCW*7%^0?YwhwP3fVON_xdc_CKqtX+f$oh1dQ??RDPGt zZqgy|h-1b~!VfOUVCCW#JaA7h4t#q&OzN@0dI-5}05|4B%F%lv)-nEIM%iJyOXS^a z{5Ljp{T8OBF&hDI^u~65AH9w`>KK=ZtcfFy%g;RXFU*^l6NyAh<@5bBgrLi8Y-bOH zjwPYznivfm>dDNDh}7LgvC6{Wdkozn1YMQT&aovLG?rEh^aJ{LLroQxx|CAClv2*O z9D~PdrDhRv*2ax(U`(#fd8;-#r{8!)G$qHRz=2a3g(WKx7YyO*>gKM3fFc);G4W{# z4y@ZLnd6Ea1eM=#phy~n;IIuTjmdEzJGI8FS+Q>YdqWXL64w=%aRwWPA|xEgK{i(c zkg}n?BU|c(wG1A3a2uXIybHyCz;lx5?sSmJr18gF3utWX#c?OKR9@ZDTa9VpkIEWx z@BNb4Ww#hE{Z0e%!!(OksNC5dAQ(1`%XD2P&I_2CRLCMA3voA8Y>&!DA% zwt|EA6txDXKU(*mplFa4>DpLOrtud!U#*=Kg{N>7RfG1Xd`k+~<1t=-PQ|A?1rM)k+<&zpZm6t~KX+(Q_lX`A>wni1mA6 zoa*kkm>yr$`n>@m6n zB508oBw8xts|-}$abz-IaNICBVq^s=4Al*_<~{dI<6BpxQ7(rn@REfk{N#!rEL)hu zgMaP8!H1_df!MiLhjz^X?JGvdWDiFjTf&V$&S1q-fz>OD*kPx{Fodp*+U=r3zA`BZ zNLk-=+@4{w08`C;udAsw1{BgQTo15yA8~4WaeawVM;&$4v1uS{BCF^+?g)E#l?A5| zM2SQig+k^_72Q4zMdPm#$+zrz(#1`l*_6!k&ur8_$YuTB5GwGjRBB{{9QMLgjyLRDS*q~I~mI}1!M55y*twW zglX@?EVWTsu%vLi*cq74N7P%H8J_E5ax!K%>%^EQ8`;)TSsDuvjl#8Rb75Dnm3La# zZ~gdzeU^?RkxaVC_J>9dAvT-@B%*MdK0Sd8F4@8Gv(vq0EvWyV?WZ21#gvy1Q6eV0(-eE9IhY6%Yv%sftkoa7_=hu;WR*}P8p_=NsI6oFW%?)G zDf~hZ*!5YNJoUSTG-BPZ$}r>My_T!v`%_fqJ==G=esp>olS&dgRt9?q%HF4jkM zCn2QBGCd0T~{PWLTje+95e0gQOZ6mh00yllTHD zARP?chuv1`m6NiVnu%2=X;3mi_91)jfED_R&IEo+wKCZIaL6)!Xs-t;0#H&9+=u;F z>IZg6W0J3-lL{PHT$SOFWjJj{4`Ky}kSwEg#~zhF7^Ci3dDRD=%y!)H!|MtYZaM3~ z&L(*gG@YU=6P2%il;|maj~f!})I%k5OU@=M0HfM6EV)3XcR`R8j3*IG9uq4RKcodN zBog1dbKi#3SgZ5;cW%35`3kvazaV16{K_^ zXr+(PWk$ubH7(0N3FuT^`?Wb?lOUNu}>=48!v2W~$R{iD)On5A=oMun7o5ug{> zd$G%2nFQT8lo`O{H41-fc|fAS{y6*{qg4Q1RjqB%UNGGYuuXYf(-w<7n7&_SDA2XM z1oW6DeypOkC?|^2E0|h+$N4^D0RjaSoPlfgb?}NG#~H@X%>pGAAGqAc{;SJ=%hezU zK^bUXW^(=V%$HL7h22>uBtP6e(fLHrtLv(FJ-bieZOAv?R%v#H?*ctO(PImQ43x@t z;AC%E*Gz6ne=8BVXOZFA2`=7$>0YXM;te4eD{ab<|Nu9h(duNW=(^jr_f(OP8wV z=1J0#&U-a$X6Dz0(7ypFkE>3l~I#z+Lx+dYVDGBq32tk*U-M50=`t+B3tk4wZnLQ5MX*DGr{+Bg25 z`AVBU=fxR;R+utf;^22GEO?rc&6LsKr;Gt`b2ik?CV>pOVkb>2Bh^$wwqKy9w;v%> z*GL7!0e$UKpBnl)`1mJ2^_go=I6D>dW0xpNUwCZ_PS)W6+3A+6um8u6)N|CUz!57n z9-JxR+!KmS(pS9R&uXsTI^1gk3d;-*7M5gfwTwcOCactqqihYG7n>j zjL@4zp)ZbtP=jwo&I9)(k?Hq<3<+d=z4c|tL^3#51~sU@ zd?t>(3QmoM3DQC3qHm9vg8viJ`FzQt3$>KDi>}yx{c*bB z@m=W2GW8>-eaBS^6WtxSG3p0VMM?y{j>G>_Msd-MPi zfr%cKVG0g&R@Eesku|T*0PVj?GZJ=}un`{U@|vqH}dCikR#I6hS-{9kW zp2Abzo)D87+G%xEri}14MMB74g3I35A~S&Xgq5YU5Xh+LTmV za*M_t7=zbCa_BjWp^G4{@c~{_U)+O{_zF#-Wgggplw1C zD;5EMNgNq;zQ*Z2>!9UJ#Si`P-R_;UJ{$a6NI&CP`?+JN%;e1x>7{l$?%KSG9g7H)~1Pi5=hS_f`Ww;pj3cB}+hX z?B!TGy3Sn1s5}5ye>zMneBvKwAyp)ed_UkOWLZ#wJzUY034WJN$#a!fUx9SrOS1PT z$soA6#I&MAw4+n5mZeE4g?IOIt1={O{kp;7S^;|I{vz#9bz9DrwQ&Xte9qT0@yn&bWnzd=-{@`cXYi$CMa${OET3-eVO zP7Qy5`p1S=I56SLHTdk+(P5MTlO3drPySdb+gPm(>I*-LF2|{$2;Gm;2c3K-U;16_Zu|*3T{-^8I`WG6zLe;s=-7d z#FYT~MjIhwhK@7yu0(W{{mx@r>)BoF(jh&19a{v#=1-U#Gd0w)F@&xb8Z^j1mB#Yk zM(YT@<<5k4yoq3(Vc0lVOqmKiKhH;3Cop+h8O*V&=f}|g!yJIfL06~3|7Y(z;N+;y z_McapEw|;W&%I%T4cM4&YzzTH4WWe|O85f_3F$xxNeExUM<}6x5K0O?B$Qwb1_A*S ziUBt;)nIV%_Vu!LM$-Scnwf34cfPal>@&>c-+K3UXC#eABfWawHskSDw6z%UabUyx z7OY=V17$Hx+$Vxi*w7n#p1%*Jg6d0@uk3${v;|=1*OzA`VI`xe9u&ifF}49o@gkAG zuQe^G#*IV)@~yq@s^JYc%p|t!efKCycyiJw>bWn*vx}34x>_kY9qVWoSNN?cV{=%{ z!e={G@b=Sg6odYLskVrld-1Jy1>bAX=`85QD>QlW*3WNI_~4h>`e$Bo6FS?L%)0cZ zlHWgeRp;lt^otVb9I21TYdy396tmOw*Im{>X`a8M2S2o{xFK-F`n5`p8B1BFx76Y< zbQUGTD>EDLpMOOVEDIu*w7`up3|}*jI*#M;V=C-8eVYjgn}e(OmA3mY0c%&L@aUgg zvEuV67`Jf9k!1)3I7|r`B`mpYYeS9=>jawWA{aNp=X}qLBo9wq{HlQElYqT`+f_k5 zr9OM`;K753?!&bL?98lADWZ5Hi7G`tB3p1!N}1_&JN*7=K{%NlT&E99a2T#PsZmsI z1~rV)PfRn7wl@15AtaO?HYoGOe9N-VV2p1dl1!=Y*hjfVD^ES-q5q*MHi~k-awYPu zmS;9&AM0U9hCP>!-#v;M4=1pCrG+6QxF+B_Wbk#M-Gn(|{+x|?=AV9qgOy0O1Af0B z%t&GHsU}W3+ulFAPGP0WDOt9T3@S9i*HCBS@xR5e;Egg}>3#<%F=S8#mQdhD=D==D zK~6wJy_D-#gc$dw77z`@`6n%<2Y&o>$=hrB;B{tV>3H9*(g;0@RO|{ybP0gNLoMQd zlW;AUY8h~>b~mNMhG|={p$&MF6x!DU=hFr-we|M39<~vbZJPwHNPyHN|Ekp0CdWxc zNGZjx465Lt6ymM;yp0qTGZhmdQy>``g7Rj8=5x1<{KJ-{4QoBaP6)H~AD5vrnO2SC zl9pUj#+V`!QY@5}<;PKT({Toj>)ZYE#&#olA-C|`6JJU`b?FfSDoboGu8D_j4T{@# zuc>6x@TIYjU!Xn#EDKnmG=q}1GqED2jtD%p=^9~RO3eGFOn$Ra_+ zhP0SsLev4rwU6MG-v z!{o1p?CDgoJV%YqL_|}z1s@opwOQc)JKM16LxJ{K7-l+!!;X|V{J1I|6R^{~9@>4F z1<5*uBi|H?7@Np4j~r1oc7)o6gJDwy0p>U=UtpCl4Mxc-RW$Q)Hd<2BfA; z-XE*iY-op~B%neBR#{awjN-8v63KYSGaQQ##*Cn2{g;t9VntQM&H{`%mStL|X_B*b z3o&A20SpIlfMFQ_25<~xe7z8Yl-iuE>`ES99AXbU2vlCij;+ny;XED8Q;vS=Kn`^_ zdQ1?}h{Pu!Br*A*3UrmcS1Fs$Fw+W#--q%lVA9?eDr*FGodg_wL==Xf=?uU+7+81b zOl5}}6Bffi{?dSVUgL;0N0Bm3?6yxEPCKU>mX0gMoJ~Pdrgx{RWuL`Ztd${L-Tuat zk2K(g>Gd?>gCp?CxKG6V$y}2=V6+hdc4?s1dQckd_Xz_R5cs(~A#XVP`d$7IN-I<4 zXBKcSp}7o)aAjxmRpBk}b`RSg#7$sh1yvepc|9WZeNzGcr2Lyy>RK~GE2JWj056-@ zAk1=~C^H4(r`gG&o42y^EuJ^)&$RL&L6WuV_>{$`2{#X*nV+yEn|eW zl0IA$G}UgV5BE;~$ZrwAN6eQh&6Q{gFw`s?1Xy87#8DlI&f`8`-TZe zEJ#ccs`|UZ9QqkLQ z&tXc*5l)6W1<;ZO3l_S?x`NJHrL1oVp^iL04PLFlt;o-k+O9I&B+iY}SNG8L%t^ zzEA@$`Co+}{wj>GA6o(LCp$|ovwGAPrS1P1>eeRl%1jIGtyS=cO-wtg6_@?K0+EQJ zUEQe!`Df=D*Y&A|^GqfLOGd&~Z@lnQW=|QEpIr7GeNfL4h+jqb^=|*rv|64qOFu-~5jESH9f(shTS+cBl;FgE`n^h}2z5~S7eS6AqW|Fv z4IuFAek7>MgWNo~DnDuN|5!h=y{Z`WN3f;mSk1VWjMP2h)@l;QmERe^jtGOrYLtU)EQ zYIzJxK22l)Y2^q8nVoKhywKZURb+0zAarm?=$)~UUy9KuBP_E^6;6^qcrWkey}Z}9 zeQ9i~t%`D@>L~_Lu=FvOWvNY@>R8%Jw+LaqpV?|MhAB&yd@_FWPoX zlkB;#3~fQrc<|5{u(hL18r zru^`ILj^;=*KWH%u-w-|EVVdlFMr4X5%}gGA$$zJctQ=@B!psMAjQ%Ky#i_Ol`zCn z1f;1=7G1p)fPV7DM4_o2_+!@5TZbrpjKS2B7Fb2K)g=pS?G!ei|gRNnH;Pc@I1ouL< z_D2M)+GX5`cCc10D;M$4Sr(2tNj_LM+i&Kjl&w$UB`5FBR|RcC zt89&<6b#CUVaSNECKoehx^WviT@r}l&6ks~WEuw@8q6NMnX^26*+F^3u1{hC_72>v zNZfvmm?J|83y{7NayWJ4ayBD__4zqno!6hw>FT_%Qr~rTp4SiC_MEQH>+N%dZBS1R zS;m}z9ACu(mUcAhaZxB<&b-4DoPm11Q|@tZT-Gy=C;TU*({frFVPq)Mji zWYAZU2Cyp;e&p=NHYg=uC|Vt|RWPRPV8j`zgp|lgA5yl~6Gc}7s0L7*P80Y0Nf4DR z!-EG8dFa}o$it2a%5DeuJ0OV1L+yC;Kfs>*M)Cr0cOs=~t+ok9GOJ-CQh9cBh0|*1 zYbj+9&|{1#y@wP$fkDHW{?d{HJR3WzV_TH=>Ltv3Ge*tzj4G<}Xy$v1DH8F*;}fUb z;nnK?bs1_>khDageh6`svlJ(m5=`klmQ+*&>`b&bF!w^37iMiof^zJWBx9Q@fA{^& zFkXDx_#Yr8r<}YfH zx&*Cf+}N8{dguj=Zd7UofZdu@b6XI*E1;zwz>p>-S_c5H{O&Gb^-I9VFEh%_faMid zqNxe=^aH@R?kvb=?1K3-BCa9BYyu4*hSy{5c=wI}#P>Ps#FVIrGTvzfR(-OvCx7A7 z+LPR2QYT98T6uN?$J2THohZtF% zl(cn8dUfMZ4TiHA;H_ zm`n-z>hp>W7F^*E#Gn1{*A)kf+{aC;NQv7r=2h3qJia3Ru9rpWdqQ z>@TqF!aQRe%l7&`Y_%U217>Afy)uBFIXQdSURZMejldgM;Qb%ms6tO(PHlUaL!Ee& zq-QS&4!siTKCZv_B`pW_XW2mto6fyGE6Z-Y;I_W1&cjxqw#}BiYnm-t6`{|CBe@4t zQYj~6N8s(}U>aP?Q*H;Iz69#zuJ7do&lAr3rcx;@&S8a2U#R`2jq7}mz432>6V3=} z^CO-&`Y11*NA2J;1Of)uuL_}T2W30&>L%M zlY?39V~_pZ@&|L;iCVQKV@wM@oFLKPL=^#kvK6G+XGSV% zyHJqxg{yOPSs^G#z#^x$xwfe+Gb3|fko~UzInuhysw<^ayjxG%VX)bwC{AbsgixyoqM}2o(s3{m?FrMeJ6_UPT{>(J?gu{=YlfFkxp-9 zF@|=-C|m2}_{_)grVx17ve4pJi0a3Jl-Fc<8FyxnSKsO;k*j6k zq+GYDjK$I)PLG7Ay!)RSv3(~WXo^9@4W1DW3S#d$gx!IMRN_BR*Q2pMiiiHB@WS*s zeEzhK=|EE1Q?B!n?!gQ%?W+Y@vr!T&PCIp>7+ zDG~_zuR1ZvNIVw#DL0%Ehf19 zhAJS496ltE=qR9Yl++BR${y&pma@m>$S{Y5b%-pLC+TIckfhT-mL*R#$&o*=QK?TKZuIjmTR9|K69=mzwhjKr|;a~i%25MwhdWwCI1>a_MHNe>{;KH zxYpdgb;gQwoyfL}y480mdNf#xzRMS=^ZPDqiPqV@bqGMaeN=dFRhDma%)nHMF-~3g z9ZKC>)yZ>Fs_**txloi(0`~B^$o5@{=R)P{*n0Y0*m?5FHkZ&WSZkV&!10Irs(%~ zq?F&|oFd_H_`=rK*8gFQkr18dEcW1G$Aii(vTQ4SfLODT&p&Q5pHaSI@JMmrh82o2 zzY>(}tq!uQTUCu$n9@mMcUJ>!{;`*8@bD#uM*w=z9=CeVO3E+BJ< zJ;6iI8Tj@w4Jw(knBfbUA)maYsXp+=lMlDyCs$T0Aweup6JuK+0kwk}et1Oyf4;o| zb?XP>i_aw_8QO9ovngY_9aY|jOgff5tVl56$ZR_d4j8@`eEm3%Q_iW;R!%w~Y=_N& zvbB~@9S`2K3CT2Y<^|Q@KBWo3KCw>lu;ZbG0P1e0RS#`93d`Wm###2T2zzrbz({4D zB&piC-+yi_B`;xCGIZRiI8K{bCiWWY#{j=U;Z9TE6q(a@`?uoby7QduDCKRgtj!(M zmu>6s<#nfDdb97kTW5F6^X|o#x2^Os4Qanx)F#z`zA)KSi^atHxN#HteTM_MMo4O9 zgpdIxdULIHOX?~@(t!sah?=*$L7N?AjsO|#cRVJ-uI zo(OdSBTJ+tD|9Qk9>6ds$w#WFV_;df+p}d^PcX*H9kQ??)7)4rCW66EGM03S?%rz; z9(G`O!~3fNZCr2Slyf3@@6AR$^5+Kp?;S&6_z1T3wSAaN0G5>jKmK(s?zyoE%NIwn za(NI!E6A(@^^~o2%*fboqy{QbLb^GfNC5^@Ft~;N4vOP~AC1(3Y~fnrcTl?7`&iC^ z>3?rlYgU%AaAn&st#q`tE!NJ%;R^ZSvt_;0H%^5~1EwME3sK$3AMe2fJOa=I^29~K zk&!kp0#L3Az)lH*?n7%xXt4;W@jTvSycvq7?wvcEez4mMjn)yzRvV^J+R_g0v`o+$ ze<$o+fvazg;{63CHf)STm?`Z>q$GDHZg<+XweUH0hs>2FGrw7ou;33W?6GG#ChQru z?Go}9j$AL;k&2`=beo=gvK4Q=LHN!OIYy21>qW>G?!Cw6s}5<0l+1;)R5{tgB>}hA zP*gd7W4zn4E@j3#Nsv-Xqyv86Rc(pX+51(uLb7Zk~5Ri2w%zlr8-}2j`3M>?5Hmi4Gzo(k<3W`iL#6wnud|vi`aTu+ zL(iUzQhnFebJ5wege`b3+%t$2YJ3JATmu|jqj1JVDerqXfxmxh9>aX0(LQcnmKMe; zMk>TWyy#0YD4L38u*z*OR=I%3TDwB~4*TKYV(l zneB#uZl-DOaWSRT|52V$!aP?44?8T#v0U%2f?^BcJUJw{XPjjZeGTFYQUC)7894J| z!kt$u{Oh4MoOMyTjvAYpifxYy*NB8Le54;&-ZT&k=f<&NZ5rv6v_l)eZHN9SB_rVpI@=wF-n95ym7cAMzuX(3>6p3}`RrAff58ivD;_5%oPeM20$ zb3IXR!nU_zroHW!T$6tvFG%B|yVKb3pa4!dy+Uhr7#Y}Nvn~%`Dk#^*+zEECR1q^k zfN-8G+2iLZpB~A<%F|1hg4E)GK_sZn2(aHIlIgQg*(olr`+5Z%;y1WZ%HHsk>*_`Y z%u~M40CgJLmjhOcT$SA1p$9= zxHBo_&C#0?Ck{I3ApQRKTl1h4S7u5o#X5iBE2J2X!Dll2tW_0jsqJ242UCD6~}WviMh=HDict#UMSzkYhhY3#eGQ%moINm zCa0VeZ?Asq-W%J?e{nVBh@Je*l7Qh%s+tO9w!m&{XY%b7ifIbiR(l0@2Zh_Gtab=? zTn5)er*NZUKnkX{Nz0a`ar<>m7%1aDO0ka4>;fe_q`-R*c~t}|FGU(ggkiY zRgkMZ$PaM3HBh;7J*oUl4uTyoN-6xmA4ai$MFakNOFPQT3481xglQ=-BX8$NT<;x) z5?Y-mvi9$G;;ekX07dyyb-qo$`<1KZ2RNe~`IiTm7&JIT-9}5@eO)UhT52qR1m}RM zO@TocSCkTEzN%jC7(1g?g3M*v>7V*KcdUmm3p@hQ12#qNS+6;W_6_AYKIn*{8!EOu z{$D=+GChp*PuK{+FE_0{jRyjOkE7+O)0#GgXWns5v+uHN%H`l;ekMd3TxWK2hzfGe ztc%U7I)XY(+?4klT$`;TVkK9u(5}e9ie)L>@IQ^PAn~iKt1x(2zz+7x`p}iZ!wv^R ze!_^ptS2X&Y{nIsh>)zJwD#UjL_q|~@59E<&4AFX$iHDyX(ThEjxrDHO8zLh*SjM_c8%5ZA zv^;bRp32Yz>c`vfm8Oa6784b9c2Lx4(^kveSv>IUAgxJBn?8|>O9O=eL?Xtyb?c(? z#^1EErHfL6a|S8Xc`+H#4Tm}VMOwGW9QUctT&P%wZrM zRg0Z>4MSR)g~xkLJQRY8NI)08v+(A|DA$%U6B-7G3&yb1eWe0}ka6eH> zD(Hli+|Ia7S&>|I;L3}is*V?1`natD`hnTp zm1_<7lYJ}LuHkl-O3MqlkB=g#_Pn#U3hYbhAjOn~@GJm5^eed8ekuPOO~4z@wt{=1 z$~^P{nUTa9U|PW75k6dUbvdFHL0o@DJLb<$z|Z|os0Z0$)yXb3WXGV*296|Ev`$gU zJtV23#m5n$6%>S2Ft~yBYb^Q4UpHX+=RsB$;Xf7@J(`YN22@BS_XL=%?W=Bq-%@|@ z33a9Z#@zaK(!*B+p2Xk*0~guv?FM7*WxO^P;nB?BsX*_}BSOU>l!>sPS;-hP7{e36 zka1$e#_F~kew*g6y_~QZE7PV-mPB6Y^4_3TinbyuGjW^XWa>rX<>wpmzrSh$ln=lC zLk;%a+Yif9VB7-8?2WyLhc6Kr`Y!M^<<-&62|6RzqE2^z< zIC!2P{4UdyoKd5uyRD(XgU@bBM)&Tut3HAsHHC0t=Cg`=QoV=n!6N`YAY?gO1i>r( ziY@jtcD??ac(1yMKRh#vkS~mYABYS}S&6hXlwi-7Rmvk0$>?Wy-z$W)=l40Fs2kd}yrm=~xoCUu$CUNQSF!31Qbg1CW-2q18of^O4s) zeC43rSoYhbrpte|lCu2mQs zHkFl96G5~uBo2ns={u|~nY}2R;~(Jto&V_}vhl2__vuY3rM@4GF>uc8cgC`R6A`4e zb_gPnaRzQ=+D;u@Q~M8`4B$lB{&sHWdH?``07*naRJO6>)7IqtC!I@@Tf6&o%jviG z(nIgVu>HNb)Rx@CcH;Q$ZDCBD0I5 zPZb`v5xf|H9$-5BR>A;DhAKtSfd#gDfi8i+&LEtBtchr45-h+hD-q;AWy}j!sA()& z_NNz~lHY!J-bS4K?Q(0{!C@BgYv&*xAILV{CMPv}cVhA>JsW0(Q(HjI2voLkl}>WZ zezgVvdN_f)4IHPQ6Tq40SD>s+L0C#_b!>F(1TPw(hs+u)9T|d3hYsEfsJwR*fN!%U z8>A>6*H9z1L>*p}4_wLwM`23oXsd2$dLWd;NW zLE&&HyatAtBfEBc->)SBWzX4sGg*LlO!WSYE~Nltws82#C(H5ShhLz+z5$kHArcB< z)hi|JD^Il5B;rwJBQ8m5$ra*emsH{q{G0gt!3h!-K<x}$W9 zhphxT1{q54UB& zl7dy8j6FrzFScp(%@|A1s*wM5DoKa;Gbr?*2X z1AlwA?+r*4;d!Od?sSpIhkNK2JOa=In43;tftUiA(u@OmA9)`<@PZE)oR|bN6jERU zDZ%65jV!BuDS1))VP;t#Bk*CK5^ioegyrT*(z=YD`xnx?sL#_;0wB$h5#7(YqkqF;ou*M1RwmXM?~0c*mLkd414oz02z z2tW@GRGt#CcLxRUV7a;(NY1%mB*o~#6-1*WLH(RCCqpC^5P-c+R(kN@!9(wW6m&-a z06@D`GyHdHENsA6DZtkPR0-ff0IzMaJ8))Z^rYS{V6P5RlVcp8e!2{I-SHSccz+pU zu_RKdwBA4)oFVAa!&Yth*`z2|@P0D^o@ynZ(Rz zU&X3bjR=LyaKzzL9i?#pS_NH8%Eh0qz&{>(83P9o#PKH{je!Gns+WG&+RdZ5y}imz zY=b0h9{MY6!J9Lw_k;d`i8C*(z+O}0c@T+G%?fcu2Cj&yJxoHNi zMJLt4$2lIDS<}}Yp`7kGFHgtsJrW+e1y2<6fFW&As{ssaMQeQsCm0GV>jBiZK{WSK zo%cTz#2Lq!@cA{(Aq)W;PytFN@kC8U!^^4kz#pzzLKoe8lQnY2<1tLyS7N^dxwX^S z5LHAub!Ny!h0)3NRNvZas+C_P)ttA4@!`8B)~rrq(nbQ4UB&dMvYi_xC<;h6fKGJZvG9 z6VFcB+Pop5l=7J=W|T$!X3&=`k7K8zkc1Ve@l0yhY)t*uzKY!ljB6rO(ab*x;m3OC&ROGKjqTisM3hq=hb z475qYLJ`jXNok_XJ*F^>e^+gG_ra7)K9R4OV+_$~1u82Dp-|X8%k~a|<6V-sgWkIS zbKCEhCG*PdUfF)X4;AH6M8je2Y6!P~vfeMYoT~D?1#*2_vQ;)SkjHRr3v0qYl{s9) zg}$uALq7&Ni9Hxdkxu5g>x2ldx-o*;vr>3*dIRphwGmG|62N}b24eCwKgNtRP+4W< z{Lza`Eco8uL&n;LTD>BU1#={uKhI)oRu~9SWJ5S&J`k-i@2qd}um6ANfL1jNN^=P2 zQ;^9j{Pm@6K(Ex>Z=d`D*jNGV4`9G?*{?l#=n@_Q=mCBwE0%<} zzo-ojaqjUcw6<{-mI=i<KUVJ7s?Y}RjjOuEJ;lnsa z?VP}%AxhQ`463paIhlHy4xF4!T4>yqqV;PGwqhBhb!#j%Y+^{sptWM;7qs`;X@29` zQxOOmZySN#dGb`#NvfeXKTI==CZgE64CLLZk%r18neOYrBrr(@|C zb(lYY2_F6Xv$*(2r)#%7gP0R~#kQi+>mtip(&TH*+(Z|4ygSl)@8P&0YtIW-=t3uz z%DTg5>d2gzBeSHANoae1@PdR|`ZS@Sq~p@36u5Jqoy}9)awK(M#*@mirCAPQ|dP< zq;p?-n?_Sw@KLJo&5_)Mt{%RMFtln2#I(X&WF8ko;EX4AyokN?|u_Ycp;Udcq+;Y}Oap>-?GiSN!~!^n_6?l-|Xv~Z(^4PnRnOC&j@ ziL{@G*9HvwAmGQV!)o#N(^IEEKYF)a1_grClxYew433=K06gX3rrGT_Uj`%Z+xU!0m%dE2zH5rJR;6&1D>o*nbf7Qm9pG~)3%gb>#0Y+7puV9^*Ee85rIUch4gb=; zb<-sb;RNo;h@W+v>QPo!hG==%F+R-SW)%tyn!;lx>QZ)ayJAO|O#i!tofHy{O)WYj zYI%87-z$`e#b}d-Hi;UKC-jSPhKllXooGb&u~1-q2a^@@EC6?~mV4+m6yE*JaF$@& zqbum=IOwnl4mu3ju%Qeey&uEkg<&lDG>uo5CJ~RBwv5aTP-YWp06yZd6r+?P5=_ao zFWJH(a=ke+wYYBR!X=T>|G06~Lr>JJ6$8KqwR1&~E%nV}`0#23N1ckl&+LOtzg@xB zaj3V(;4P-m3g93yPA_wwuHVCE;Sqoy;LBPnsi05byM~2N0nGBJB};FkiRAugY~kf6 zXDT!`RjXh~2~ueyrq~%{NyOUJ`%3Wl8#m#WBM$LL*KP8TNT}5CSRy_GL{SD+E`*k1 z+6+Hhqmhc0K0~b?G0a*${{>$RgMmNZN4W9t7m!TvV!KlqvOr6WY|;czobb>!@;!0# zq4Hw`ZZVu7+1<87aAN$C1+MA8yrvcHfjnW^g9i`&8Trnk9BKHJS^Tp}z(6Qeg3#RE zgdohbg6qga}kKjFm>AQn*1|O6Pyf9>VXub5->#?l~rM!e)b`F?9qS2 z7Z6yrdL;(bOhP(s;`Un}!qVl-apto63s1bI^kPIML9-{s>QkAIS~^kj>!^K^>xj-_2$2!p}qy*yXZXB*VW;P zC!R-RLo0~UlzU1;!=H4#><~cRJO+JD_1pbs{FiJl+~NQID(_s4`w?t z%UC__WjTKB;bKFb4}%7lWzkQnGVaJ0<0D1toCl19xVMF3h z4m=KEmTeThZRO=VAInXoKR8(_1_-w2MkrCvh=bJ*fn5GM+ETzG={FXc=?IkKS`%wo z5~P@p1!1KDB`vf!5#m!n<)PKP(LJ|69l)R$A+*a#MzG4cmZ+@7RN)3B|91}_Tu3Ji zuoJE-r`UHw4`qI1TR3RkacJiN=32?OS@DA4P}d5%=5(}V@C0QK9z5(A$joN~q&kQo znd!|Bg<23Ax+BMulTTd$@OhJ92PoIl4ulUr_#CY*Dfj{gj{3#{HsQq_OEpqZ%5um; zAZ;b^ty8~_ZytY$F2}I-xO(Hg@V%AzbkQ;>s}e8H{1^)tE<{<#fF;(Wsj(Tr2)r}< zBV6y3IFtBzI z)@<4czdwMFK3t5(hBo}-in9wW6FWmXWntdD#aO+v5e8p~haY?ui#}Nlf55=T4b4cW z1sWO}@b>Hvz*QMeKcg+%5DBI&2Ai4^c<1dUn)E#V^mx{CPf_7ZgOpCh@;|@22d}^S z34}-@QpphrhOv5mJr;hr7;nD$KCZjra_qg&F0h1v!8snde1fS19MU@Y+ zRuijNti}89t-!(s%W%u>mm(4^>%~mYlufkIXHthFzGq}DFAK1TLO5>0j@HL@o6n5x zK!9Q3U_S;926o*ebF8J8kY*&+B{TnxR-RgmGWj;lKsAIxWEue$9~fXF?Mns({_(8e z8u-Xh=(TgdxxFtk3%@{^l%xIcU@-5&Luq&fpa&$JQPAdtN*kKE&{1;CzRzCXCGMSW zuj&_^wjB3QA1uFr=2DqX4ntb_Nx~wKNq&E3sy8!JJvh$@rwrUjN(st*ufY83^Sz&x z12>+bc8@9_1cxFwV6@7oN(w+lpFIN5Lq0SC$nxS!6~_?I2&K+*rM4m@h(himwDR28 zx;mq{ot}o=g9i^^IuNS58Eu#=81Ay<^u9k-SROGT5D1{UsuoL^EJZSz!qkKIz}T^a(c0dM>H!sq$5Qyi@9)Ff zl_`wdbqv0D@rf9}`$!!;ci!Aj@xWhSKx17S{&Meh+;-O`SzT6HI08}uEL*x3JMA(Y zKmOS%7(QYUni|{i<+y%@>!!dgF;LVd^LpXA&Y>J5}_m#ZoPeHDUm2Eic! zx%W7D=pH-*&;u%xz()IBL{s?OC$Od&KqLjkws+$I-2dcG`u)$3W!)oGPdK@eC;x&T zn6WF)IsU1vg;oy5FD$eSzA$>r!h zyB<7zHIRAW!#$bavi3;cB+&u36}#Or&CQMai(r`_i@o!ShJ270cFR@|1}fGo}@% zo^cosIec&D7l~1$hGNai4S3}KSFn0j9U26C<$1}vxDHgYRi<~t#! zeyJkK)Q%-g*(4#KezFQLy)ai>F&=sBKKRL{r@EzJ?3m#gJ$e{^eDNQ#d`T-_cy2b% zJO3NlxSK7>@>Ct`%T^@TmCNNhNHbLh9!&Fpt3TYO)Sz^R(l;}k*%dEyez;T zIz`u`BJ+$I&LNUWyUR09y|ij z1Fn1p>O26mE>$fUirul!wy6KX>#(lg-i_-+xORXZJoFUV1R#BE9ZGq^IP2DE?G2Y- zCn%`mdJ|;t3wu^lzSCZB^iQesvMqAbF57=^XR~Dw9z1LdC`W$nmVrLGn>OX-5%$ml zZuqxcrd}%8=<1BV0A7Fn9X#;2>8P$849is7+AkWZKz-wS3>#Ud2|!H>l0rJ2!ibT> z@sppPtI4#qB{a#zh+))DL-4yld^cNuDyh)i)Q0x<7H}Wn4{-Q{2HIl@)YUa;0x;7a z#+W7*4?BDxP5z~P8(NE>UUFLAwVK+qzBiF?!AMSgwyZ|9w=|>5lj1#W zIoz=$?Xd7#&{mtZG3h59nTH2qg5IX#x-O4&+$e7o;K9RJ2&(5Mzf1wri;>g;_F87I4wdBkLkx>QT?2(Q%7V7VpiR^PmStkps9^|)0}x8W z=lBjwZ*CsM{1rBH0FsRxm^mjaI_4l2F3l(z;f0P!hvO40YUe z{dTM`Fui;YA2tO3fFG%38tXSSWiODyCNKw7SLx$2Z6}@BgdrFV>Q{SP9Ent-2mQui zSr*2R9|^-C+l4lBjY8!2Z07}ZA}~;iEMA*7Ho_DVVSfc~z4=japQYnpOJV2)EmBG( z<7otgz^WB%&>l~svLb}v{N{W3!~gybYu40b#`IV5?-$;{(4m8|%ebM~ck%=rd+b!5 zv_wiLW?HXU0A@N^YL{^r(iSPevq=ZHvI^G%ut)wt9z1k}8zRC`?q6avgQ|Z~Y?9$@ zCwcyp&Z_7=5gs-Rj{x+5(GLSF0Nl0z(YWK-m8cl5fc=44L%J^reHl=Y+0|ncJahq8 zAf)#$D4UF?`+0ynMNivE1ffWW5s68O`#xyKnid8>&upKRE%s1O>t4zdG9&ZLqL7Fv z?ytOLDej4^lk#K{aY=b*Kc+M>d1M(*9aCm+V7=$og9i^qP>vEIGsjro98nVPd^={$ zNNv>@i^p`Fy%8e@>I52`Gkwe{Cm*d}F5Z7{DK7cR9T0*+3cGV{=Q*XV@}lLodnqZ~ zqR^hEB}`M|=084!nKNETDiuTZfEtV#F&t%~8uZbhc+HwFRPckR8jA7|Mb{J0@5jf2^$%9TUjSCI84r=9-vfD365PY8=Zp zXARdy+3wJ)#9EwblaLO%$qHkXX%aParH(?*5*COt9p^e+R)Js{!4xU|w@xV{m3G;d zk|SI$wHtP(-}j%g8y@)k)p+Bz1z51)eJuH6C04Cmhh@u_;kDQ1<6n-$#APYwsO`MAg)Jai4|rUDTFLM~zl~Ac#gxv7|D>$+ z2tW_rgGT^*$jB>t!gzY(+=`7B{@c71&jR>;d9<9jX8i%bBJef`9=ZX&y7t*vVR`nc zTe*_EZR|lI8DWSM@Y%)$u6vy!VMLL(q@6{Rs30tgR`eIkN*!1{Dr^H4W=+nbRC&cL zq8*M|9~0n&1WRMe%2pgPEP!f1>&u*j9z1y1f#J?6TbiBy%MO?L*=z6JQCSs6V`CFu zo%J>jJ!~JnDrlmL?Ackzt`h>B2q`lLMX3(S)RZE9oJ%U%uB`SuV+^xic>_;B^%_ED z5q#tLDfs>m&cL8SHCY4nzd!gY{_oaDQCS_#D_1#XDQwuVG5Z-#7GshMEv+q@+LO{k zb#+DFnRd)Qhi`LeM2GYvmMyC%>jycSEH_S{yQY!DPHDmu3YHX5B%QXZY*x}lG65w> zcWXXDXp^K=5Ns@39?;ih$uxd_)%ln-X(wH7<`}&u5+NA0sW8hDFpM0TS6v;!Nhcqw zUoEXMEL*xpCrf#L=4`BA*NPjiyBGIAd;_Yg!%ha|J_@Blxuj^l6n#3oZOK8-umXDoA3xg57`v@CIbgyZvY>|7GC=4bl1DcH|F+| zfOzN*q@8(73$d(U8IBfg!E#=5u2*k2N?@Amn(WZHJ6Qk0iD zl4~bia&|Cw_ChD1G7*^LT1F0|XU7HN&L$~Wmc5s9BLz9-=}lSIo<^<8sNZP&5L@eL7BHOKVIMa;CCiaKHZi z+N<-xm>*K53M5hD_S$K8mR;DcyYB=)=SZ}suz2xeOrAUjLNQIkW{ydl7VfzHDKylz zW7ml_IP0wAF>l@?JoV(OFboqH{qS^*96l70Xb5}nJzl^3{s11i|5Y?MC9rJS3hZ0G z2YN^6Puee$T*B6U21!A1hp^=yQ{kaML1|ev^RlFJnS6Vu=rC0KSFCBKuoMby`Va51 z^3Y9q1fT~vUK>c06JEs*I+hh`zRV~#kjh#KY{xcv=>Jgpo<6G266b9-wuG1@hlF#3 zAv0#;hC>XDFK2m6M(JF<9c!dcO#e_qQYkhEEd86(mXUF)rJ>3^y!fcH4c7 z{@!-$E!2YbhMY%5N@M#92_So&9653b`~e1G`7nL@OW1$%ZWu72QoBY!{K$)V<)!&3 zkJh5KJzlWnX_As`r51C7FHW7hCq|DMfECMH@YEBpV$VIsW71ys^~GZ*{(A2-c<6zd zh$R|u&0as&t;*E>%F8c9T4fkeJq$m)^mK5CyGSO}sH<*Q`t&;!jyLQL=qHix2Lp}FqQ0?d7}pp9=>v*yhLB*ZI4PoW#?|8 zB!&&I!3{TEf?r*JJJzmh#pC~+g_$$vptiOaem`JYQfJJqtKX!PZ4mqzvrDaZABID? z+427JGYJ>Y{Zppwjc2CMMyxG_E3dd$XZ5vAiTB@Mj0N)-VdtGkqph_K!cut@qHR$o zgutj#BQX7`m+`^-OR(E+J7MkG4fu4?YMmiC7y`cc{nIp22)Ob80y<+ZQ~6R~j#(sP zAZ^*xJ?kDj6onmsk1O{a$5N3Pgj5(dxEhluPr!@Mzm3lqnfTrXzr(okqtM(K!+Y<2 ziot_MAQ=aI%nwGndi8i!hA!PD(ISyD{P<^Q;fi0}f(`2kmtXQ%Oqetn;PfnJ2w;Yhb$v zvD}|Zfkd7=l6$d)!VMj%Z1RvM1zh)PlO_&-b4WSDMEZPkGl%3`gcJ){ia8{o$~t`& z5Qq9tpB?O7C3Jl(TPE*EM63r79{Lr?-qWe%_HIajL~gb76|$gjs!4n++ZAX*#0=-q{C5DQ=#W>&KRUfLRc|aR>~>c ziMhvY%fKU!IsnVQSc}L1`5!F*VgnX^{3w(XsI08S`RAXCNt1TNmA|xyRJrzTT9gAyz{~+F5uo!_LLpZ`Qd{iZVe#v*S@4mZ12os$9w4yGZPQtR{ zkUCkW9jDJ;E;uACSfUx0)dp#GGfgxBLbW52YDC&}%5>KLoB=%H$3Hs*tt~D1;DaSt zx27G-m&}LZlQ`$x6EJq?U2)yDcK~EU!Sy#@ia*~o z1Is>d#H<(BKq7`v*g$o4IZiqCNL=*8)3jT#(1}|JmtX#U1pWWSte58Fz4?pq)*Eq6 zh?a#6M9Kr0I%OYRdinXf|8y+FUR*yV>qD73KbD}k=6l!*C^t-iE2}a$;Sf5Zm;2e) zcwV08erh|n#lsE*j{x+5HBOx5`moZU%WPQ&YolKsI?PL(vi(%)-rop*Naa zER=4~h=RGNK+|wCvpM3q%ze5|r7R?PHhIVspC<5^MP)iG;m(7SxL{%!(sHvLs{CkB zB_2A*bx0LiA&|<}@17zUgMuH74961FWB)vO@K74A72sDZt65Lr+L|#i7SstR1`Qm5 zU;g^LsN2wtC10#TU0s9D4jc^lQBhHWQ9BL6@Zm!cD6`ju%sGr1+O71O>wbiEI*sxQ z+qINu6H6#NWP=30|D%&})X~%M*{4g;(%Ouws!ELCbtjA-GZe{q8V~>D8l8Q3=&(Uf z6A9YrTNy~D5;*RJ<1um4E?D^9VkF`*3>z^B`|P_bqLGlcz~kKB;#5_aXs9(oH|g?@p1PsyXU zKWsg@8uA66BcOd9a3(Fx+9r7L&?P(q&;!bB1tYkGFKFT*zlHU2;Ip6!iuIeBu8&Z} zwRp-z58VOfIH>}tUeQR)yzS~%VvC;MJaa~7#{%8l<+$YECR5@cAEaPJqF_?s!H;7& zWtRZTeMT;s2j$m8g{A1-9V@U%QJq{w&VkE&Vm)~9&^eT&wjhrH+zfJ&@fh=Ii!dn_ zY6n;9S7+x4Av9seoY;4aD~uj9tdryI`C5^J6ef1sd60hP!}|a1od>)fMcwy*GrN1* z?Kd~Q_YQ$15C{+nAdx17Ac%?xf{mh&J_rhkfartohxp^B9;n{ci*Y4>KW4r7KcP;LKPM7p_7 ziXMZ?b)bOGoo+-Iq)_MNZ5`yN@;yjyr8?>gly;!#06dlL*8RXYFc5A6o#q6>w%~U> zusYjCcSwwMYCvTjI2aY+hd3B}qG)#(uWYQv>I#7{VF>UzR_+&gZc_ui93AOdRc6H) zaWhO($8y3XENx0DeW;u6zw2&Y`^+Z+nGKoytQML3;=a!n*}|UKb60Q>-F#}fVBkCn z&P{cldjUlDrOsWea)7i(X4nWBJ5QrhbYjc`v$%}^Xa8lK%g=>iQfwm`iW!lgbIwzO zU6Vb@1Q+(bmUPNbmo+pp+tES}+-xFUziz5VFphacAN7 zEnZoqQ6J9TV`MGXw<+B1Ba-r1n>&Rz)D4|s@0+m%7>mn(MaIgNpELJ7x<)&Krat4` zYVxe1#5p&Narc!z&ra;cMk{udK+|dJ_|w}kS1@#~^RxCqb)VZ^9c;gr`zq#wyMOsU+oAHHc@?IPw z;)!Q7Qkmp#ApNm@vlNsP=3 zr0!7Z4onXBT2-Tq>HADt#Rx7ZqKC2)Q5+h3VoKz|Tgy0~_T;ql0qNU=NSW}+fc!-B zecqjJ`HIdW`!L}ecZ9mTHa&#YxX5+c^?#UwXtIs`XQTVM`CS;!^PriTh_qz$tOY%B zj1$z?$5C5bBVB;y?PdnJJQuqYBamA=Bgouz>F;Vj>tfp*_5PW z>9=oanYx-3^Mip#(q$BJPSoBnc5R5eqOUjA+mV>btpa zU5T2?DLO+&n2=@~lV=^+@mPSFur}u>Ow;U?r`7t9X2p)yHj(j$D8UPxqgc5&fZ;(C zR~;ReR)FuWti`4Sd3bI^J+7Wy2*#Y)MBZFPc-uo@TT9=S(@9;uoSD1z6b{wyJ9=NVXFKP&;cy5i z&OR0u<<%H9+Ho;XXV2y7%(HDOzO(mpd(G=xil5t-^4qhYTa0xR3V>AI z(u%C%t=^L!Wm;-!xF=lh@{+s3Jp6Nw!Hj?2+Oi{X)#G1QyI`1-^cW))HS zcsYGc5Ra}8;F&d1Ts)4UP|J)nl*-*fDV|wfhxhjtf|FwWX?ZQ~ol^|Tldvd-CNoH8 zl8Q7WM9Na6qj=V4>(_3_ZMXgwL8A~vXep}@CoeHWFsPy5z&@Bbc@)n4!W@*ABlo|E_3O8zyrLYT5XHbjWte}_894uf)1}2FIZPs}5Kb(!-~aB< zcbZq^ zhbDXiQ73QSc+AA64OPgC0PzMBNi!)kBXYr|U7*R6(De;?{nf>I=IQrw+Z|V7%G6OZ z+oPt$p*F+06U%GI_PzMQwZF!`T@5JdQ;2aB#$ea3eOSM?0k_}!1nTQ8eD$(yN-~i zcIroTbI*pgX110MsZ8`DyzAzUBQ#f?pej$PB1aHz{~`zhxBC>$7j~doaztWtZ`Ilm zW(XoHwM>M&yHbjtf)W4~czLq(_x%w%77qhLZojv5%#j%`JC~<;irnCESdS8EU)TNk z7eWK612?2Rsc96ooZ+cu2_%t+d7~OIqfb!A+aes8(Z|5N5mDT~hT^f6Nt`o|VxaEJ zbL`%1e_q{y6*XZDEn)}~6Why!c;b@=+&Qlp;gnml6AvjhEK8XYimY7=dqNqGga9px z&!0UVGiOdfBAJ9?7%)u}HMQ0F_><4DarHjz+nvB&cRY^!9=Z_)1);RaDulFJgRh># z3#d;sgE0!>O=t-;nT1hEQ8X2O#x+57vR`HnNSiM+6Ir&eL-X9V8w+Vp;teNaq!aX? zXPgu7_o-v^d5Q}(UuJ7K7MB|S@wZoTa9=I@_3ww9ZoLdAojMg;x9-NzesUkSZnp5k z^RHw6MRO4e2ig&w-?tye9XnO`JUa0PTRRutmwF4sqqGR&od0xLaIaV6)4-TxElUWM z`}3b$SqJ<4HoAsRLKN=CCUb;&ukNkwD|Otd`jJ#SYpJa?sh|Cxr1z07Q@B=;>AK5A z4u>^PW5@8a08=k^tD?u!6WZI6i%7dr3eq%{vPh37dV+zK;z+|cvhN8Ns?KV-*j0>I zsX!Hg@w|_lzfYk!1WEu@*pqN)mOywuWw@U(ya(U`nlQE8S?cIQnz7*O0bReMyXm3< zERj=HAuZ0xVU2GsGUacP`Kise28MsEH4#s8Xy6cfcD5NwN^cws{laTdaY$qd8tf$zT&$KUrxV17`C zK0yndQzR{c;ROcfj}N0DNFDL83(kN|A|Au^nIrMV`6o8{&E;2|jXQ3;AMgBo4YqHq z#wVYAhPiW(2WL!9c%(3+HQ$3sko`|3B!*dn+ELbZ(TnwFU zw~Z2>G;SA8IZf;C2y!ALR#>K6WOMt;Y%5x$bJSeSMJK~(%gY{(+jUN(ga?J9OAxXT z^)mjq@SDuxMHcp{ZfQl9X=#j+qYW*oA+8B0c7IM-4Jdj9N&r-(`Z`V!q`;-FAou_k zM~+XMS}swv2fMv+>MnBoKF_TiMl^AYYQ27qvYom=<^*8w$4{@Q!cFgK2u1Rdv^W^i z5!CGyk;MGb!1U4p1Wy1|cVljG98(s=S;On`_*#mG))JgKUZ9^&U~&ONCwOjc3~Q-bjq>P zIxJ~2Ec$2_Hf`F4%E}6aLjm+DE5dQd9feV&2f4zO6QhnXkrH)2S+W|RF5Q6Y>S}~T zVT>3t5GT%_hO)jzX=^w_uy)N>?A);jc?Ch7G`WEmK0fi>GOmu2omDc!-bo%Q^U%Y>#=O zc@$1OX_|CZ7Q&L(B9Soh@#2+;HY6~5{4k6jH4N{*`w3Po+X%sdfrI+u#1p4t=VBP9XQuLd3;&j9sO#JKhMcBP-FCuv%j2k~3b3Qi{hGDnLgu7^9@0Us; zZtdDFSh#Q%%J)|z6bz!@fHKTFVIoG19F(>sB?8H$iI118LbM@@(PM^T4mqk zX4NM2A5exbefdn}=jX{|vD@C#Pu61JzWsf^Ew&%ud`qGur7fhXKp z!P#9HxU=Nlo^wk^i{o@M|kg@-?RK!9j zFJl~+%_zppTch}7e*iCTsKqxY7eS-I?zn}gR}*lq(;wgaF2^&^ zzJTpp_97W)sHtkeNvBN5hV{E}_b>j8)vNcRrmhZLSkUc$nlkk3SBS4&b{;Of;=Gh= zDWyPtT@=6k#eI0^?Nz9)OM+P(l;{Ws3=A9oBCh-XC7650Ojwo$UDxs4v+v;H2VcaL zsiU#|l3G0Uz!Qko3pCUwP*PlqDMyXOf8P59_x$cjY}&F54fP2GbpwJEd2E9Q7UTOr zx(uhEc{~_5p^?_JT{;4#z!xt#1J6GD4r(eAcRLF^}v)}aJ+PVbp{-1~N?wc!6Qym58(qC-3R_Pf4^etioZmwV#4ZvWw)r{p=U zs;rmsGbxx%XfvRH3BG#y+4%Yu=fGkNx~8F~CWbq2e;nJl?!~2FIS&vPo`2>wXfz+8 zFvHx_rz0E*<9~ns7rge`LX;n^9d+~wxs9$HC@3gJL0%s` z`sj10sN9QwWu-{OlE}*oNejfge*Q34t=Nk`{qr&J+_@MwY#?IMC_Y}i3Y#}>!5@GB z1cnUhhq<4h1;#lz6ZqN9_v7uiK0!fo2xp!>1Cu9>M{RW-7Jal5Teob(Ew}s*;qXm3 z;Up&jY%mx=zkYpTB@I0Ar+*?AH8F5tf7AraQBV{>O-(&+zvW)k#|VZG8-f#NAB91K z`lI4NCEkB$6?X5az%TE37{i7Sz_Nwov-$sp|GG4ShO--nl8Scby2A<+Q#q$aVUZmx`?$Djm21!_aivpHs< zp(cbc2i%UNCVT|M|8qIKvDOql9pW1VG>LcPE^<>Z+g-cwB7b46TT*2ga>yWSj5`Kz zy0r333x!_Ji*)=tFPka>6S{oRTyaboZ*4KLq@n=--Vw(a#zgS+3KKCF!1)t6CKd&y zt8oxs5)B=iy)OaZ-D!crf<{9!S&@_}JScJIIc0pig*G<&tzeVLi3-7+Q z3`b8J<(wrB2#dfpV@M?H@Qo|a#r*km<>&T3w`|^l-Mea}`|TO?rr}37T%Ia(|3#KVB4hF@dl@@;tJ zp%*Z1`dEa+0dOk;&f|#3V$coXhMTUyDRYiOJf1{hVF5l~v5Rgvc}T201^oc+qUjUI2=MvO%?JZ`JgcH%e(&z zlco%m2~j@&bRBNH`S&oBCVu?Wt1)lh@lxp2T1kH5?TZT&LYT044X*yqMfk>*=cm3s z|AJk(@khVK?p;-Q{LvRN=W{a<&U3_T;)`a*1&2lq?AW;%%Rk*91+>%7I10D??5b2; zzAu06Ox*CpyRqoQwfNwJWw`3w3s6`XbdtIt36ojisKHNey$Tn6@pGv<{O&hT;Gchg z59J4|v3T)kIQQI>otg^^g2WJwF^n8ljNkt8MhqO-M}D{WKox#?{jb1Gj)fntMrCCU zN=u8da>W+><8L1z6pG-~(~iQeKl>JfL1&1UNSL_&)(7$S8=vCQN1w;+Imcko;8N)h z%tQ==ByjLx6(&p`f@`n86hnsfMME@(!lD9v^!`fZN5UvB3F6-SZbUeo7IuI0zmMa& zXWqlARm-qr=N^n0Hpp=;KjgJJ{755S`+kdecTE(E-b>2kK#;B-SNnzqXC{bAE5u^VhQ?^hj zT8H>WK%bAT6#iUSf$X%U&Io|bWVdP4??sbOQwyPlz#zy-m;mYK+VZ}2Hw)>^QXOZG zw{ZV54S!i9u)eAuo9ps1y0`&fof>hjMfrhk*t>UMhRmu_1JU{z&N}ZzTzl=s8QUey1ngcp5DH@DXB)9- z@d`|xIsti+Ao}+&!5{y08!8XhA`l8lvCJ;`tv46Tn1RDb_RA3eZ2I>r!8fkB1ULNG zZ?R#+4y;+TMY@uLYtTp#iI|CReeZlM_|n{r?ZWwauy_LDPz3M3vjk(t4Z*~TBc&VZ zv|~r%zDI9FeSJ*2zRK&SwbX6}m|)O}A>$q1dh;K#WbrD5Lk1>J7>mst_k)=X58nSQ z?zrnpS>B$#dr?zuN^!ZQDB_mVa)VAU+7s!D9*@;y($tZ-?6NPq*>M>xZer}1A-G`v z>G=I`|B2nZDzS9gTAX~cBid5=KjOxhbR5~r`jp@gf4UhBbq$y>aYRN&-B3WooKt4v zgZEY-8m-5^eFsojIMlHarWEzjC}z%@fb+ljIVoNW#-STJ&Od)1UVmj7$}7sTd(R%< zX>#1o6UijLbnzup{4-4xOb`t0SAw~7XW{;}FQK8%L}gXI6aZg&^&P|#CQ3@fxbC{I zro=x;3r_>zx%MI~S@IdmE2^+)(K5`x@H96LBn9J1l$8}qYs?|T`(~U^!_Z~>PDC~Q z^Y3q9-Z?YTw{NNZ?!SKcb)56X&x6t=`ji$q=Pn1uKM$E6Q9a}piY|kAR)EfvS^m3A zV)Xf~vD7`S=mf%Rt;Bgb+4lYJJua!iMb7w)NsT>X;{PBJ1@KcOl|751Z72ayk&+m= z41D?VkBX`J-MqX1KS1ZcLOa+5T)Y;o2BWFmzi67d8Ytb`*1 zxmHb-QuHY+#PH!|xbUL6IAh*y>2ga5L7%c>`KqpIz{@Yak3G9%h_^S@7l$vB17jU7l(Sy6|=f&$c3 z*I@nn4N?H)@)&`egzwcw`j-9fo;y#7w+pjELRvX%&Ml@Jv6642>gk}&hOF+|g2qL8T zHEGg#ghN5p*Vkj)mfa}<&?oW3vttQ`gBU)npZs>o;#F9`ZWj(7s6lmA9TLfU?B7=_ z-FD-#n2i6JI$l`^hM_|TI)U&7WZXrY!lD91!eLmOUWC*O&37!D(ZG+fT(_TJa zR$Zl{18^_+G-=}fD?j73;{u_7-I2*Cd!3k3cfzz)u7b*d-kq7$b3mA&G$@6O<4b~= zHzZwSbY)G`jg1@InAo0W_clM}`Lxki?{Mu$o{Z@#DoR`tXbBeTHFB4pVu_<}JuO3AR{(&7G@|}c zj1>x-LsO0NSkv{O4nxF?;nX(>q1SOgYC6HYok)80Jgd7l-rr}*;Rc@HekU8_bI66Y z9B@l=GUs)l@3gn`;6JBIh=Wul$uZ)@HGpXd5N)crSnFL*%vQ%Ap(u?w#!;4XMwJyB zZHI?o`}l}EzgaF>yBY9TH-2yZ1m?C9-QT;o$EVQDwx$Nt225C3zW?L^GtTM@**N+z z3_|SS*WLFdX}+B(2Z|^R8U8d3y~lEye8-#D1_7Skpg?128ccfK$gTe>S$Df&fOJY) z7;``WbxCX_Wyq(Xabfe%$e)JpsOE|y3HkYq^c=7l$`y8{=o?T1`i+FoyZ893HG0ui zXK;%0=@T*t!9(-+0Ho9avcyefpLbS-u^vqqaW${UQqP&~cCAeBR9GLjGq_=LpkJp` z0*|ybmj%mLzC5YpGf>ecJ9|61OGeB}vvqw>3TYX!YV!W1r=W%F7Hbn4BlwqG z(P`Koh}4*82$=9)^?XzEeLi#68^3B_thZvFuU?GW)cHgV7Qw`2ap3NCzlq@U+ckI1 zZL5IrAH@F_t`k`>1FW6RNrMdLHYkqX45c@=HJJz2o{g;l^b_f~kJ7qB7ZB`1`qHbZdXsS{k^J1am3sY!FVyzV?14b)|E=w7P7(W6@X@hm`rMB10p zwl6xjOEP*I<_uz{lY(^%lHIu%f2)y{XXV3{s@L-=*{Nn zFV$7tc58DGmAgvxY)e^&sglxe3cbXy_+V6|B^GA)3&UwXdzt7y)0n9YZ(Xj|rFek{ z{4d8jLDSV`l|T65b920q!O-c%N!ROyJ8tj6nvWay{|K`e%^>@^bq^Sq>{hSR@o9yF zUa|6M41fvi$fny?z4Hkr-Ipbf`(^bo_;U~4QQhaE#-1ow#RNHBq$g@JW&Ge}x&h*B zGBJw{tX0?)6}j2?m4FkFiZZhxJW)8r0L^i|V-q6{YyP#=oMuLOptF|vUW~uurgrzz@m^CL z3g?v@iHFaCZZE)3hXwY8Qy>3YM)V1)ma2q+qZH$y$4Z3uBSus|kOXqA?YjpdUf=_6 z&?|FI3C<(LRM#cRNVk87cLmx_z-;gir#lKkd@r(eyri^(F5Fs*{0N(;fXKe{RVHLG ztl5)1q_Yo$1)dE?luIzf-ajtWxGL81aAgFHL;)^OI(clrm3OqDs0a#=^CA@|QiIMQ zbWUI^fC7HavE;RhBvcTo0qI*XO~bQeur z1Fv_>DRoD3irNAi$*!fABe3hv(&cJ!hG5Dfq7=QIg%2N8-hKpj0X3>lg%k_SZ|8S5 zdPYHF!LuCm*0pOhxM|V3rNZjhJV9g@!G67u!D*XADP#RX65r&_u{YLOJ2T8Qz0hcv zfpj28ZjEmqw1?eaZzQ(Qz!`(yvbj0=C)yu!W11;v;8Ws3iA(TPBlMG`wy%jlOokj) zIfJFtQbL6kkwmOhesV4(0hZ^?`uz3}9#KI-0Z4Kg(!fq%2U1}|qS9%YrC#wmOYEG| z+aSzUgunNbn&HxD&ElKw+cY&cvvYxu?*aS|!@1oi$9Fqqqm#QGmagkEc^xLB56zSm zbd$NOPpPa&F1ITs`KMv*daEJ*1kxbC&%2(VrSQzB|E5<(v>i~b8rq$f+%I-i7;1LR zXLEw2lfx(0J|LSK+;=dy{qBp`JDTw8dfpf?b$xhbqM~%8yZ+#1H}@AW$4;DnWNTt_ z1vgK3eLkHJ%t+quc15EslTuO94Q3i}u~U?_i2uET5RzXi@w7;{bC|=1K1xI9_qf~+ z4hySj{)NZa?tg#Mu5S1J>l1u=WlTvIcQD?e6AZ`%;5qVe=;?sKj1Eo;dT@gK8HV-m zi?n#pk#bVZR2!ju`%2n;>e*DRQVZ4MaXNzAwVF@-i;VuY58DZ9tr3W(QZ!bS_G zWE(WC=AQE%hsDoxZN*T)QRQOv7Sxu|%L&xh(Gx-TIu4Rywe$}8kxNW1-@|6zw^ri0 zlJWKiMUIA`o@Bf}e^C2^^^-k8lIRzqx4Q;0V7_$A9QOdtoBt%1iy0cW-t8XgW51Kv z>66{#;F2@_tL2s{RnF6IMKz<8u#kK8f($v6S-UUm*M3zLJ=KHF^<(m1v@Bb~Prc7G8RL&#)YfIk;ao`(-Z)c;lNK%l zCGdw>M3iEoB*&};u#}BPkYh=|oLjQX>p+bkZw0nA^G?g+2g`(d)A9I13WFj^2@5WT z4WSUg3z|g_s;C_UNo=}3_~68r`oR*K(b(&&sF@W1`7RGc9C^AwCPnbw9yC=6*8@5C zbYsfDo_OHy;1lIw)|}^?4vU=qvoKSZN>%B%v3(>7_(4rZV+K}dQavvY(gE=lOtzka znskxuzuWdtumlq9`wk+mqO8I08F>hR#||hkhac_nf8qTP4GoDbHl(EQetshD{=QPK z4AO1i$B)V961h_1M6e7)!gVy+m+B%NTb^dPPf;;Hx((M4&78m4>dH@vl$M#2F}fvB z^XtTo7SyLWT70CZ70q8v!rT$!zkDd!7?T2Zxz6M1eqWUY;sl&=xy%?|uP7J0z5pWM zdVJ{T6`6&VMq`%13DIZ4a7C&h*}h*;bhAAlj5E4J(|u3Ki2Na4SM6BuPr31tDiS7p zy}>VQ=R`(3VRlmFNf{oi(%4)T*kzdkMi7N61W<_m1!1(cASROW5UhLRENSuHSFd;s zUw0cU?t5oycsJ|CKTW=OtskgysnC8Y7M0^7v$H9_Z=YG&1i7A2vEhbDh!(J2@cvX- zMDD67AsEH!E+NdCFH{kYa_l_k5qNGsx;={~GMp%K^ZZweoY)+I!#XO;89))8c95Db z7+{F2An42RXPR4K_p}r0FAsp};?v7}P46~+2<9)>qf1l0{iQ;+b_eYD{qHfyyNRVj z8>p(^HaC+~*W>Hyu)mR1;@!m;bP3qo1GmMCx@eR1xEj2X{+yROuY;*Lh3rRA9V)v-~+LYrpT8NK%G+EokMG;0%|H`WF-pLy6!QF#^LO@$mR0 zGwZ05nigpC+Jq^3xCrIMz6j;&%jDWDkfX&Hz~43H^iGdF$iy~W?6l0TdNc@WL4paX z?{j4rlh4L z8*#1I2GS`!pmnccu=~hxsS>eu)sE@0^9lky=preiSB-CrVwH&zu}N@{nNb%VM>ARL z_YaqDej9wD{7xen))VwcPfP9B=yg}8VRboQ3oj__B|OdXrZHZ?-M+o|R?^iCE14jM zNnZO~-Sl%%O~Zir{6fovWBG@o+CsqQ<}YenIHnx0r(4g*R1YA;slfLgAAeJ~mKM-N zK8c)WGQFC0?x}t0Br%*q9(1XMh^J>GymC? zCGPBu1|m4XD(k9T`wU5%GMQz_l?pxNM4)cSh=6m?ox=Y0-*vs~KX<;T`QM(04H~7> z_L)u)q9}6t0vMcM4=mWA8W&g)ZtTpe8mq*ZE?ZsRa~o^B0%Peum-m`&PW(Wa&E(^x z!T;c2UoUd(nN*fSjL?kKelu&_KzCD|M-#?ro=M~ly_9B;ttOceLE>gKWaY4+c$_|9$iF^yvc zU|HVph{am5Zxnp+YiG~`M4vq=hw_)VBxzPJ#w4e{6U^~^L9}|VN>Jvr>$RNGbq$DZ`v+s$b2gu3h9IU+dIV=N9S%v%(E?ZtdOTg$- zX=4B#vozJ6E8?$l*}wDLd~1unpE5U(v~y5sN^&`TlN%OItD*mBMJtb1eg1SGDJd%l zr0e^DpPab;$5#M0z<_)LxLIxoq+MTc92r?RV8pYzZ68r04Xo8=MR#|DrKFvdCZnSK zIHx;8ShA2CB=i5`*7rLQTyOR&mmBWK5mr(H4MVz)Zko%Cg+&XOEmT-?Wl9`wwmQ1~ zn}0p*GS?eEm*((auVb)~>FxPEn7f=XSmkH-zZ?gW<5yn$RZ}Zi_0=|r0U?;L6uQOn zBv_x@!DME?L&fg9yRP-7a_!ZjyrI-b+R?ua!H!SdE=;bLf?f<2t9SbwXrS>t=4^z% zhwj~9XZ|-E7CMUh`oWf>2nULUK;apX!IBlG^&=*IYy0*0`R!c!Zc3mhQp&Z2nHu|B zT52n<#k@o@M2;2}1GD4(T-{Izb#cArfkizXTB$FbBe2oJ^}el=YcmmP(n1x$`iCHx zWGIT+RxEQG&Q1{MHUWRr993ORK&iY6C_YuS;_F)hG_dtjIqIpgK#`$tQ(^)XBZv>&V?xIMtXE}g9#m? zqNN?J-bi*DJ^d-xKd*~*ww=S37}M3txlf-3P|SQ=5VW3{u_@p}CBC8xD{|5_*br%Q z1Bd(GMUSGJ=X$a~YR$4i1{#q_g2UK=Ew<`G{6AwKSY17p?6$O!pu`@fqkF>j|L!zd zUFw1q%GXe@eY}j?4(-qi5GUgtw&gA=v+6r?$ddRcv89zXy+I2xNFJ4KZuu9BJOU08 zGqjmYx_kuC`#|moEtkZ6xeAkh#ghiaoad#?yNzi~!AQwr4BwJ-Uv#4@_hDecashO; zMxO4uup#`kB5d5d97Mm-OEb`y{ciTsE&SH1Q!_0a`Hv{$c>R)mGN+Tq?ArZObsclQ zUQJHm|KZmHQISR;FDXfZvm8cJymZQ@7bM(mXbt~ouv5;MsiV{lCp|7s;ZlqeUww9J zsMHkn{V3YG#JwN%KhWwmh$JK2P&3xuGC3VueMp9QmOOlL69;!gq{*0!l3e`gmVyfY znLNZLw`P?Dm&{_F%0O_q=y zI|=2MU|Mf;zYa)rx%S5xr(*W$te- zZ7|zUmqTjta2lul_guv#B`#^!9qJtP~0EW8nJjW^t6)poBD|E%sk|10ESoAbQ3y=&XOl0$m-Ez z4=t>kTbo1*^QvK^WNfjT%;#vt6hNDS3tcQte%HkN994@vOI9S z(Xoptg4>J7?TcelvJ}yT8+O(;ESevRo~()v8%6*$K*Sg$oqqrQl>$;BG;Igz1|_DP zj&@t}xjUUJC#eVKfX*&iXh~~t>p%8F507a{zqA`DVJ`0(u0Fo>-Yiry?(*6UWHfYk zidz2utZ_Mg+QJ=Sr9D*6nSdI`n7v5nv#`I5pMGQ4uz;5DoYKBy{lSn;mbGzWg&R5{ zKPD4z$&=2zi{8BbiFT?BFm6BLI*d6rp136Wom~>F{9UXIUJ8~5XcTxN z!S2kfiU(Zv9y?@`Jvjd{1)&s+3euLn>Uw%b(Y3F`>%?PL4q%4oSt(J|(z%78&9n>w zc=aOgIEDy~nqrz#r9+JTX3~W43<51-ON-64=GpbM&$V>9L}(s^h9XsM ziAFznpXji9&VjSqH&EqAb6Jkv<>1PfH3XA07xNYwqS>r|&UUyMi!3(7P-ocE z?t00<=MJNgb*S`ByC1<7iAYT;d20v{Sz)>NTpqQYXHsW5XJTmAVkjQkT>Cw)QLi1R zo#V{3&`(?bCiQSfnnsScwNQ!)0bH)=)p_s} zj)JOGo6sn|0ndCI63z1TUs(uho5`190BX7AAKaQ!!G`^{8Ie>bai!@u@1ku00P~QX zGBE?pTe#Z;qIDd6)jMKnzi@JKHwnoI+xG;;;-O3d8-9NP-}uFF8cU=qt`#ALZRUmjhEBj|kdnDzMI0lDMSOF58~Wjb1)o1lyx2 z#9GY8HEWwLmSxDv<%F#YI<+Hl=U0U44nmCyY4!IXOkhUdxf<}N;jtF)oRbkW=~t^J(+_A1DWEQ zV>e6Tgt?01Y||3sd6LP4z}d7F^3qa1AdQR1r^b{rBKQ$4am6otg)j{eNz+dL zZVI^xr{e;X3H1doNTQCJkQ#A!UR*8wY*1I)0$R&%S`^{3@m$+glEvq1rq6aLAlXu! zvAd@+|ISD(-#ea@TG7!l_c@=p5m>DX+qn$1Ypk-4n)0b*64Polx|(+hK5J{<`Ingq zs(j7rI68uMIPdk*$R-4>t@7QfMHX7xnav@cj43!-N3R|63&gC^zL10k{fu6@~Xd{AF0 zG7l;CIK^hjO%kKJ8qXRg_~00%mBe=y#}dJlelN6pra}i0r=>{5zr(aH9u(SJnZ=9T z_Ibh&jJP32UJx<7#&W>o4hrUq-yg85%CZK@_323(GI)#+9u>{Z9Y6VEDSbswjS}?G zxS-%p2f5Yi(W!Paxj*L>l={U`qkl`nGos;x%frFu7Te%Buq zgFkLGQ9>C-UAmZ&rwcrkR0oE!DPKsYWA>#t`)mbOfy%ox_@S}{X|vch?D1Na{Z22p zH|f@k@5(|e6$5vmkSYlC>-9E^g6c67aMb}Fx!TIf6GUU+*mQ!#+*>SEMZ@RY9gU{s5xDkAneB9^67&xse960A|$3o$XPtbbA_ps zb6`P6N@@_oqTnR36~r%5f21fPl<;qv)+$7=TZ9+YL^Y1##28GSNxQqNHpXPbM!FX9 zM4iZmx)&Pv3xcL%Uh~$X@M}1%4Cu-C&nP4MX<(M!JowT1;enzfW#8=3Wxn1uq{Pn( z=HMb$m_?ZidN4AqbDuEN<2Sx+a>P@9^gJWKAtw)1{~9Hmi6K!rk}7^;`H zqI>`boOraSUZf(_5Zs87a66Q=9;(rbY-3s82T*Nhc7vv0<$Y?xsoR*o9{(pUHP@#a z!#T%@sQ~J(cRY8K+8GzP^7%eOW6prU#|lBk@N>5~wN#KdwtzKvkCRRS2Djt_P6Vff z5CeP3bQIff{#IoTtF6;41cfWKL2c^!dyvNkdruIC8()RQ7#hD2@Z;kRt+MBil+OIa$vbL zleLOeJYfGwpS^+WY7F1X!gj-IVC|dGhswTaJU3oLKcEHqPOaocZ$Wep>&U?X*gp6Fr5bnC~yG=jJLo*mZ#>MT#qV z_m#XydH)@V33WNM;~y*Pnv|jjDlLB&y)89J82nW?k#zgI^KEmJw>O?A}i@ z@k|9h>JRbPfbD8%c{92M-sQa`F+spi?nxl=TrMP}gak;|d)YJjkgd>>%pX`F-8FpC`GXKV0;` zI;IzI`vD`!28Pp}hMN>Z=wzzkuV2JyZXa}`k%y^0`VB?m5{tmnZON_eCbo8&=P)o#*3uW zCl2Uc!!rfyEQQpI4i{R)9+52;*=2-)4kwO9Cbi^9NKW7Hu_-1kn_fJfi>?*u2pG(D zz5R#0W9F5lZ#UbZb_ybzHw#E~mb=KrkY@Y6@lpnA|#mRttVwJ-S~<0$$>< z0Z~teZ3Gfaw80xK9XIKVqYTWJ*$Jsu7K67Xxj^yFDT4DUrqhrkzi^H z`e(=L=VeW^&3CI3YQOK5wM$cKlAe;x<~Y%QV;M;*BFs zR}U}}|CdS0ae)BG-8TDHP$HO61GbVUsK+?B^)nN`JxV= zbtF*+x78E)1Q*~}MvMc7#vF;Ax3Hdgf@vWZP~jcQUoLB2TEqpUu=+ru=c$^_u&yGj8{p&&HiAU{gIS=%L5A|@HrZG^PMRngL zJExC~CNrZxo+zh2TN=J!@dHS}FC0b%W-qZ~LUlhcc4v9|93#-+eh!&OjBLH|O(E)aD&j0+dx#@0)B;vu4kTp|e|^ksrC1`{nB zOdWCldvwPl{_GMX92%7LdV?!KTeN@!W@;HXHnB0*-={RIvj`1lOu=;^^6}Q{4k-mC7fg12HcKtWm6PGB2rz=1WIux5Op!p_`lH4c6_e}dnUrT1ysVO zIm<_rL%LjS@K|RYn1PmoRO{abU;!-WVZpYmu~g%)q7x%x6eu+ORIAWNjDkQqK_)>` z#$airP^yBx9!dTNO%hM&H5=r^J2G~ZV(i4GZ_N3P-(wM%U-l4=@==2D`;$|410!RbEY|AgMKxQRm0+d zWPO5)+E`^-thnN+r}A3!B{Uw4yQIAEhY8k70WdOkS0IphBLSwRd2ovd3; zj}>5l0i7;A!8UoKK`?mrOlJ`T-qVIXhC&0y!DJ^$8)^N^aN`b6)HY5SMua$GWv4|2 zpZO+5wuB#~(iDnK&({5_l>r@O=RE`Mv{ca*-5=ACrah);-$(euZ#6x8H(AbEmS=(M zjM@XTpX`TodQtMRI<2}Ze3{KD){-d)3Da1$ra-SZtL|fJ6cXunphXiQ!_{f^5YCWi zlXLTi^DzoTK`3I-2qxl6Uu#(&IdO@e0~YcC23#m9?K(l)NTnRb%~cssV~+rgb;9F* zkemCZNIUbrDRtn*N=FxSd*q-dWEA3kCU^s}mx(Ai3{?j1CM^=$^vsLSP==?8FWB_> zZ;;b$P(pGmYKr%EevwwXV2r5cJN}d2A4n5sDni`dob1yIiax@A#uZjIMRpZRL11Qx zvwMC@!?Jicic0u(Ti3G2JvzJvXHbf+K{5MFT6R*G49mO38TIx2j8b1crpV*xye7J2 z0sAvziU^$mHt}ocON676Xn~b{C~A`W z1<&v82GvM7Af~N;Z56MaCLc5jA)e~9^bYPp-59bch#rA6o6J{PNP^q1#tiPCmJjVr zv7&@^G?lQa^9L%bb|imicF(+V33%xlWKAnT835if0Wrjefb)io@%S$zr65A4j(OO` zd$F+(adyX0kwjjYXTiZo(&qQy?*f&Wcr@@*Oa=7ly36eoXIW3Wm$~^UKXj5JEPg-j zOx@Xx^#jr>sv>PrVppCKhp)Fa3CR1jM%(3JrXe8E76o(*$1y9N8i@_%h=pK*?THal z*`ySRXlu3%2l#}7fb+02Q*Ni@#zk>i%MWXa!uRKfKA;+}ukr9_L(ZhJZru z+VXGAq$)jN1FM7n0lrfQx_%|J{fc6RQ+3gJgwLOamcSGpW?jv)_k?frEoZIQYRIju zm&7*2zwM(y!rrSbY6KR70wU&gGeNvi5&s{|T!JKsDtO)|_6f4T^v3-!kPxN_|hLw0ID(`;{rDb2^qb1_(q{yoR`YX@0jgh`{ zn|E=2!C611^d<1LR|*ewAl?g~)qnj=U1Y=; z?5{$%wWK{To{F%q7-NpUFZ`1$Sfc)4+Mkf`_=So@-_JC=|JR|9)0AOBUx9Tu(Yn5x z3%wa0I&Ziw-rapZ2DOICDpK$~vAg`cqf+SmV2iz48^#EExq}yHIzi$i8W`+ z@Eq8bEJWe!XbAj1<`+Q=>|KdTun0NF*p?A)NVy#)dH$=Z=v`&xV6^2CB?JqxG<1kz zSN2!O(uW@(sjl~4uS~GJag`h@biUxtPaIQfuWer!4mfBs?d8gb{cxs-Vi_jXAi~8* zOo&tgzz)Z759}U6wGST)*>3x`)Jy#+&$l0-Nr@-5fjR5G80c|qn!4Fu`c!WJ*&D^D ztBE$+@zf`t_uow}dVK{4M6ZH*r4tYoq7edDpazaWdacGKed=%-ImO+82TK1AfTO5` zoLH+={$2+*F8-ta}!G z$o8sT!oRL1(|6k|+Vr+S9%T8inpx4My+1Oajcte&_#s>b)Z;jBlP}BAj$M_>_Oq=Dx82=7B{Z+&wg(8K zt1j1H6KDuYh+E2#C1M5^m2#9piNOixq16RQgt{h^+dDUKx{tgP5Q+V3jN((qU)7g| z;j2SAMj`QJt?g3|J{5}=_bSj{yWG;4V&@ijDlrkXzrb4a;TH7Cd|*KhK1Sk6ZYy6J zd%nWyB8`4%ZAd5rC)Z;ff)X7SSiy1QLEW87UvZu<&{+`O|Dx+J%DfdWziE?EDSNcm znHa>YQhfhbXL((6)VJIk!`N&w@S-lu=j026RJnf{2Iaviu_|C`Cw3^aU1T_D@ZDm= zS`u})?;lDm8XxFO;`%XB}3dQCK;@IZSYI-z|N`^J7l5*JtG%+2!4h{Lh$ZjWWdwUpYJBQ z3zMRpuf7}3LrZl#K0#<7X9)%a z3x-rcfQ=ZrYwqe_+>QMF_*l##%%wwS0y-GLV92QWIX^1o0Oi^S^?ceGr;{B=$j-*2 zGFGKwGo}dq#Jo);U+_aYrL^|u-W$5*_hhuVz1_ao5++fB=-ytqz&;H8q1Ml;h4>;h z2#k6*`Q?Aw2VX)k=Y#09Ntk;m5KC+~d;!kC7nnp!mVZ$S83JyChiuyHylwVzsA_j9 zRzD5JPOZgRpNkbu5fy$(xZ`jhU8kpkNfW&M{uI)!Kn11!pFD@kF*!gisUGf7^P=V; zbein6LuN%tWC7_oMEPYi#(Qbq1d1_6ibi(Fh_n8t#10WZMM>rkF_7Hi&;TL=%2gyd zBux1DIFsh}C-Aoqv+# zaC)=N8~<}~jI`CKLjH~96=d@65mdA%C|9WA)DvczibDG;6@WVy%0`T{e9sOhnrDdx z_M=zl&9@Bkm#lk16d42w|6jD6`|r73?|y0J)c3uoN|o~8v#+^79y;iBpY~x;sV5rx9U6zJo%fUXYO}ft*}U}9 zF3kwbL8^6es#KOnr`-#^clpM80)hAk*wxTrUNz}EnMViS%b z5Id?JT`#l(=pip5P57}T1Js~qF1U~lnXnOxLqSr(*eJMiL{XfH?#Zj)e}5A5v1}H= zkNgdWLB(EX)nP=_^znt#508yWsZEBuY{1KNNOX=2ShJU|+X5e_HCHtHY=)%?dk~CV zglQnFYi;2B7)2HLs7~}4Qx@P3Yd3IHLpBO0M4P?yIf20Q zA{S!y`=@2Wfgy;zCsYi1Kq?C8JeybW08?rV>edKcq0X+9@_XF|A5;dWPSrixzSJkZ~rlD~l0WBlxM6tri;1FQr)t zbKdzX@{_jTVMUhmDGSKUjBqKl>fO?ZpJA!~-D^yVihV<|AlXKPdkeXjb;yNf4Zs9I zed}XAfv8{}9f7su^fT5NruFth3RS%<7-D1o42PwrggXjcP^JQlQ|mR^LDf0|X*Eja zvWUg=_wa}Q%|sIZPsM3QbIy=S73YA77OFra(jTmAl;*4B&HPkV18huM98QSk7R*!i zrrNcb>Vp#gS8hm)riiIHpdhHI(Ao-t!wmg+9{+GlCe7TWB`!NgQuWo3mLNRp`<}@x z6E4upU@T*sMYS^y|1+ARQZc)A#ulyn2PU!a-dP`itbC-J6j_|h>@W0}r3Ew%QZ2lL zO@tNrf9vQFmHHPOiDiU*Oyo?KS}psY5ol!N4hH4S_K0ZlC?JUAXX^8(AYSkQRLJjZ z5d!n6Xi~C)0mQI{{$Gw5olq&{Rm;j*%YDBm52_`8h`VG%1si>fiy42|fgUg?pn^)$f03k6iwN$&9HY16Zg{2k2VJumA2V;sDQo|8 zuYSFp>VEJnTBiYl9V6o$#hF86SX5_~`jwON**OU2#K@R59?6xYkBNB-9YRD}QbQhb zC)Lr|9t(&21K(5({E#Ia`xL78?mb5xj*wf*%a@oiqMSmS*o1xX57;4#jy4o+AU;LH z9SZ;-)}?N8zJRbxy3T)`g)4{Nid&1ea%$e75JA3vLtM9P>Wm~XM~awYiRMz=3I|sW z?mXp!pa=nntC5Y*f7Lob(tYID%`?^!n6c=pC~gou#yN?aI&y6U39Yi!IR1t3{>7#ttguD1!>tPc$C?=7VPmS%eddSc0ydiAEK=*Ec(`vziuTy8IgH5qM&n>@#52z z1^WkAHb103;7#a$PVXW>@qgBmD*t^FU}Bs_(QqlNvU01Es_n-ie`Fl2btxto3NZq1 zGHup=1rR$NbnQmZ{@WW}tbjjs?f#wcoRQeKb4-L$gG4l(CB{VFK=jzzn74}=z(M@- zj}fw%KuDH+nptjd(~wCHMfZst+K?BG18`vG%ZQu&63+6x_jy_Nbk_wdTRS&U2F40O z|L?uVr{UG)xefEp-~mFN{j{MX@tFftOyAQfJjou)?yC0i2@JJIowoBLGvnY&MEOaw zdKuDvY)eR;(frRn%@tm=R0;;QXI$(b5$G&a4=23uE~IJUG-XTLi;y8mUp;I{=XyL2 zRF+tE503_fFOME)sLo4{X{rl}pd}OszQL(KABp+U|JHUS z&QAC-aW>8S6jm+AmmW9vc8Kt6!Ol3I4xnq>%fn7$r;VkTKK5{gh!*On>?x9+b~f@h zFkrO%Kx5;G109c*j5u1>!Sw$JOShxc4VAAs(l2+0p-3as`b$Zr)DoU7{HM0S0Hec* zH#zyT34HOOcpbDYbFJurFQIoUfIfr?6m|`<845PuX!#WIil9WNOwA{oim@kk4Vae& zpAa0q+>kONe&(40s43nV6W6x zod#|;RaWgGWq{_Gih~-4X$%J<6_5C{WE9Yo`wmCrIgM}+Q)QC{HM3kvzt#9|-eD>rsEBjku0=bQZvD7AnB{t;0nY?#TcLJ_$WL0Q{L@N-vx^51Q$a!l^boC!t=lbnW6A-N+$7OQdsed+@l};PdY| zKLL#vZtx-MQYV9ftoGG#p4{pgVA75X^AA2OM$)! zv*>W0POpH1P*0iKB?5HdNNdIT%ofB;jyAL%Cxr736HKk1ISc!+CasaxK`)|+U)8XG zia4N^BV+^*&mDl53ppKAL#avTa^r1L~{rW%I(4vL;%+PWaIY0vS|V#1R!EoWlM;aR2dyEh6tMlHyqV`@BJjKe=NQSmU)2!6!{k7PP_Y zw`#|ePBg0GaCz3LhTS=%LCsG1V{MMJQ3daobrYuW_s%~uwIeiYLUmCAQZ9ZH*f{c! zM?Ds#lU0;2vLM*1bRg&!A^4l!YM!}IOJqK~BymROJV>OM? z9tJPWWM8o;6gw>GF_IaoNs?2KUq--SXp}IXMrfvQ)`EbyTVB$wSETVJj0v`R znr?Pum8Q6v-~+k1+(O{`E(8?~g~%F*UyFJCDX*_4WuW$Xhhc>cd-Fe9`|B4}Gj8AcXgklK=+iib=$?5sa#H9_9Gu`;&eVxM4 z`*~q-tGb4?o(3t~=$tt73le{s4W3BXm&Tk1>q(k+GDWEKDiS9Mr*@gWV0NmCNhl{I zfbS?EAUs1Y%_rDs7#O~aVrLZwB|ki@Kk|Bi#xxpZBjd}{a|@dn%gZyrEhViv5-Ow- z;|24}vUFS_0J9!jDlEhHW9>}2ZTVnjXlzdE;6kK|-$YLPr_p!@J^%xQ`ya%qAS_z_ zt)QIIr)>`XmGH>ox)DG_?Zp$%Y8%?G#{->xj=#%o!vSWXafr4I`!^fWPIlf^Gaw)V zAkt#OstbdnDcO#`?cnFL-7vcKHiqccS$&W>c!}h(hy_Ms|82RBp8Xn-KhK2@9*nzI zvfb@i{nY~FSN|Bc$w>o9Omwgj-h&1Oz-ZtD*r4u}GZ_u_zh&2#9ZSBKvP*%@{QxLn z0`3l>t1v0|EckJS{PI!t;EFY7$P@!j@1oix#wwzjp9EPjG$mT1Y$?NWknqPWq9Gt; zcqBSrLIgn0I!rzars@QR<0*d6*L_{TbfY<@ZU*|7$SMBvFmxKJ9{hMPMuxpGH5P*Z z_cd&Q=}eBwY4$^SG-&s)EO?U&od2RHnLr>-OhL*=@I&Y3i#6FWL}HPl=FtHSs_VYyYwX&G6D* zc{6{_QOT)cfQe25gw4>!=BBp?XPpa83NxsWvqizCp$z>9*%TYms62YoT%b|}|6t*2 z-N`P8NOGzj=3wkwBPTa9og+#ak^%*->Y;z`kp13p$OAP@#^sN(Q1j4_Xw%a(r zONNlBvY@1{1L_%24DGQW^PmQJ@oJ$;39}x}NWuoJdHK;#G_+4)@iO!&(=$4Lw#7HT zd)M%2zs*ro<6J;8;`mB4yv9wXP2HW4z0Trw3#Nf%XkAaJsrr`>PB^h~>|+}OlDx9- zV-YQ<>h?w&=aK8x#?-8U6>T!ad%Ohg)zhxcg?P;n(|{)aAD$vgoke-fzuHvy^PLfAJbW!*Y)i9+etkkPU|IvRVg^Any~T|u zMLw@my-iTp@vgQ>MLKkAbu(Ep4;bi)$*K*ymbUgNjJ^d*cIRG6Sv#kEjHmX*$a#>m z0T63`!T)`d%6@-bAhD`?>r@->=kA+-<36`4F`mw@Z@(+n?0*~NlJWdG{cLAD(v_&s zXG4{6j|NSrgYY~TTMWT7vD+Ic+)+6uRn-vW`%}2^loH+^m4E_+x-2wysV@A8+FhmMMYxqn}`00FPDOGAA#3(U+xi5cVa{<#98Dao0=`<0& zqj}mJ573s5A}xF&jTxzp>}B%bR|<;T98R#6o=d*?!_i)uHqBnAFG?5Tp)%!)wE%p) z^av2HGDli=iN9Gq8>p(y4$`TZ61@oLJ*($}H!o%Y*+=83xCl~$mm&(ol^9#qNWMWe z)bUCRb-hhpY)Viv5Zrm}ZI1brV7Ze9)<1>C@Io{Odjn!NADI}YXBGwVVHTi=NP*rQY<4eJgPxI%s z|5y+sz9Pw3Y^~Bp5S>PigUtH3;4F%X)CDI7y6v{=GMydpIVHk?#WdHM*$od!xvhkU zji5R}@r5E_g>k6I<-ZDTS^NS3<}Qo;>}Ch5opc6D!3v4NuD~>qRIx6NoeUBYm>bem zd;EVaU4vgH-qU@uz1g=rhI~JG@=;}Il7_Nz~&bz~+NJ%9Tv%*dI^;+gcqpFmA-LUbJ3yJ(87|fWJb<<9B z%K6ZQy(rkt*xEvP)(CDED2=>NNJPX0Vo@w#lG)gT)Y?}Z{je^$P`0LIxp-G!X)G;s zLv&{RZSOB%kt;j;AwqSl-)?!}X1fApN3dg6PRBGoKBzhT(~l!($%6AI$qTb+RFUo| zxF&aldS8UH;MgqGuS?w+SL$rl?D+$%Kk9Y`k~#&PmTg792Pgj{e^@3BDw5fdGv^4Q z<%cB;mG^45+uF(W*EJT}f1wKB5Bv=g_=gZG?q1b_q3@~KpM1o7PQRfn5avfkAv6>n zIO#!u;GKQ;iXT}O{}uOsPlU}*Y@vOZc@8Cf`nO}TPVPAz?0z)|q#p5FPg|x;qU-_utR{(`pA@Z-%xJ%es!q42d@C3U+wKB+6Vp&e z5A~U6g7FlA&>OE)%S2-_@+(9wjuUFGc|MN8cZvF~0t^vmMd~*d_s<$`L@edpKx(fn zO05uPg)*~qeh$q;qtFIA{rS|Ki22*)S^nB+@chw_mE1-+I@v{x58kQ|m1vS_S`=O; zb(W2kU_8FijRTgZsAAQ<*y;Gj;)jYy*45{UKlQn;vUFzevDyd?XpXs zbG2M3Ugyp|?|9A35K)2YN)Ut_!TE zpN8jHiK2XxXnj;2z@kn5F^$*5hX0!C@U~q1RveOkI|%`G&9Euch$dQB_O35mK2Gy$ z_m(Wtm1@s6Kel#tz_(SDUMXb1ue)kn*ZErUzcOHxq$Ro^hq%n?5&5yo$01oP_Yjf2 zB;0$6W`T1?H2*|1f;VOW6HM3rC`=oR?e2&>^1 zqUYRmKr>hLH_znzip2t2vPQ+!FEc|87hQ$#)R~2%LBsV>$x((Cb@36ilL(#~*Y%?Q z6B=elmygh$@Ll5?&)a#zJ6^#L*tpAbtobDAXVtb>%uv#*#~=KRY`Tduum?|i1%+pI zk?>P=gkMUx!Y-a99}hK-=F_U#^)@6wh#i->)P8Z>ekWjr9uyJm$bqQxyvol zZf1kc;AknOBSbiCsP9$6Rts@hI6sl>rkiOoBg}fECx?uGNx3_~r7{rt3MG&biCo^Pz&2RkJx-B?+FIzxs8A*vnD>nhy zfN$u`7taJ}bs1QZ>rerZTZQ~RTdH2!+WZq92xcDhmd%-?rw1|QmZ_xe)I=EnxCd;PGW}cCmduPvrsW&{1A0^A{A%Fp4 zT(9uSk(){x;%)Y@Hl~7{Ot4eWg7WB*e9j)Zgn=72I)}=GA-dVW>ad8ufYdh>!%Y0; zP2KGyR~S!kiS!jIIp8yLKny~Gvw|y94kANNWz)4!C@{>#i8aa}#Xup~36~{Dj2V$o z!kzfCG3PgcjS=6yNY%pCRz#n!#t*>LM?P*min#OR_MtJMHnxVZn@5{*FxeQ(^U5Bp;&KpoCUdGKgS`$Y7=ki_YE4dcm)lYtEt{NWP zTTQ$7ojbJD#X9(NFuHQhzMr4!QUBF3QWl75yy(4&-Pa4(H7K)P_;gl=4kpzD+Xwd^ zj6s*MNff*o$jpY#IhUZj-Q7oTrs{Xg*dC+O0HRLWKeo#H{*dnN4_fG;_tD!Z6&AC8 z*GyMBFgPGzWA9B#zei%C^sl|)_E3+Vk|CD2ct ztyTM6uKl+<=zjHauP^=`lFy6xYK{@**P$S`5cl6wy1F6{#MmZ=kX{oV|@4(>t~oD*z;474B9 z*~xW2h`-d-;_;K|%qM$4R}C5gu75UJvdWYGOAg$GY6yYrer9fWJM?k=!8rh(xrEL+ z{T3MtJiekd^_0(_<=@^MA5c!&%8mN*TksuTZ<|Tri8{;>n`#z<*%sG>wk|vfE<3}o z7LqrEcr+@QY~>=!GiS^~Tb@62xPJ!@iiQBfa3Q`+Bw2>{(1HN%I&lZv{_GlV)$g@ zgY$Pws)DpoU?BiU2AQXhEL*LcI3C*q^$?qln0;s+c!P_giv>kGIFVo7* zJV?453;5&G!K=`Qp8W^86vF~c(#5Si`vecKyM=}9L6CnUlyx5Q-%Uz=yV^`PJ5vOU z^tpPx@NnJOr%KjQ%Hl|^{a&^V2MgIjuR(;ul_kO)WE)Ea2KBI=5I_lMbDQJQ;m%G# z9hQO2q9k@_X$=(>6`*$FM(X18YMn*-|7R-od|*ca(hxbThnB|Dv^j^BR*fV)v(wBN;5@8NVK{W z3L&nMTrb4^(rawdxT{;8>7fENM|K&=j3C~X@Hn4hh>I{_PsuO(CkDO0*$Y?3si7D0 zW!E9T@H*{x`9_T?6-YYwps1X19YLP|x3IOuV|TwFj_*KWqAMG95&ozS+rG4qu$5AJ z0n$JPm|;z_(-ey1xl?u9YsKwAf%{coo0ukTn>r z8!f^m(=5T_TEMX-mVs+4E#Ub7LmLauAfy}|Ddb0!4a6j<4+M_H39Q1aDRb$b;pi-a zxK}naLUo%CeOQQeS&j@|c9*~q0S4wBkOIfO4=;61#_Ci1SFzc>0JiUScWgtWvyUKH2U#(+9e3QK zoIs{f3|6`So5E`ClbP2j(n#v{Gz)oDejO#Huh@ zOqUUb_TND%Z^_pgY?}5YH*5WAB-{RnzWpknw5d=~{j0 z=ov+M?zGlnG8y8E1Eg>tEGu|E*t!0;0wB$$pq)By0qd73cEGd#74PqM$7Jb^Q0wxh zH9wjFrz1yYJ17FaB5Kvnqo6YGi)uk|K~Q%>+W~Q6t$sr>DM5S$G@`cL67?@Zy#HM; z4t^4Na%}_B3O=>Lyx8(WlITl^lA>XOC`vTl*^45#AIuaLrv2gcpmxjs{gRR8Zz)FX zRqJ@UitC^m1K#>U%CLC1N8b%RU}L}*gkvtN z0Wd7_-_@?p_INv|bvF6&6T-ul@h<@h9l^Ird->;K2PjDR8%Km6A_}I1g_EkTHy1W#Ms=Y&Gkr_HxM5VCq(aT7- zQp({OHNoP{3i6t^UP4TBb8JoYV|Sq1XyMOe^I_d{F(MBHFRY`?>sPFQ8sBGpkd&E; z45qO}bDMIo)_#4;J>*S_t>2F^5C!E8*TF+oAE$#t2>ADzpLXXlt28Xnh9V+5=x6G2 zhN>9|nI+U(Wwjrp?g#rEeyz)rrJ*G<0Phh}>Bbh~VE5N!*{`vCj-YE~Csh=R;>H;f zlS4mUVU1Y^Ow_lg@F_SH6!4arN$-1qa$0ZcMU%Y3j8L~ai(Rcpl&}-6m?EFAH~fUO zGrW28dv6!SJv`V}b&xlnk&+(uIrF$s4Wc=!{~9sGqU_Kh%*M=T$d* zbK&U_%fcU~rH(Y^ISe(q_`~;(Vrng8Md$fL-Y9^+el`E|>to*v7GrLdXCrspKsEZ^US#mNm}xvmn!q}&fkjlMb|Dt^Tr)y0 zArK3V1w1NzqOTe47c>uo{DH=*8GI~&p zffMO}_B!(yN9q#YV4mHNCk^kAsk8eg*Q!#mL_WZiirc(?A= zc2oG}bB5T>I&BRk;wyI@XK$kdP6fHO(gV-8%<0m@q|=!LFJ&yFskoRD@z5SwSA_~b zB3qNn!7$GQI{_G!J?ie38VW%v*%L#}PDn#+*tYSreA&b`U<<2wI!u;2}wR_<9 zh&AlFP(u$U^7L*)9eHHQ0)JaF<*ou#OWLaSJt8uc6g>`3-ZtLdaWaSd&RhoLblIX$>j>h?J^HcLUB?m=j308m$=pxSO zId%}~60Idqt*4p%e61yCb`+rlzyvfV$igj3h+HP-VRA8`MU2*nf67gk4CL5}Jnh`QRH_ywe+Fc?h zh;T}RU32bS-dIC2)=f5nJ2HF~EeLp6T(PRhsQC^&*TgbKT_pEX+k>;yfv;3jQGfjX zYU&Z#D`IQ%69I#Tj!&Es5BV!1SK$`LKRj?YK?zmm#2#(1Vw8mP^fymbrm0CKqbxiT zQn}Olhwz_S-;Bj?#&&QN#B-0zE^bOTCX}`lnRvmPjLx+6a$8f&Z zs$VHb==Sn8lQ=i8p0lNc2EdsGALXbGFAUfoN!GU94ZKiAfmmcM_K6G*GyM+#PjJbN zTl|9OGXqAFxmgxzy4=TgYl=J-cbum_F6KYLyZoQp(wV$o65P;C(?GR<0bOQo@H!4h zwjI6TL>HTZ?pxQ4wn^{X8BjcM@cCQSMcL7NKUc3i=c)(6>7j2%zXZ2og7x9>9{6>q zg5xN#x8WeP2T$p~&tBv_%npF*6>Wvjq4v)FB?hyFxS|7G!d!s|OhF8v>Q2f63}+1# z4WX{QJM|gY&efp)5f(l^51|(N@H1%Oi7`LScEALbIc|%P@-dbn{g;7KR;?9UO$}sD z3sTt_03-U5MI`=`P{hJSwBveA=!yM>4rC8H=XoD=LT|J1TX{(a(wE9d>90@z(WNk8 zl1rsJ4sWot&-`B<5uJs$&TS2h-#Pv|y<#i2iAVb9-jNIkk<)p+sgA7qEeLV`k6N&J zMzCC$n)f85(?Sb+PnS(@+8XZgO>MdEEIt(H1*CqU@FmtTecXNB)b;CxdK+X`P&5{; zhEefnJ%$zuP!rjNWDDhQ+V`3!P8aUU=}?J2H1TeNNni@W43*l&Kf7 zF6AJToB?)2krF2H%RtvEV`^GG)+tXEj#y57NXyu8!`G-(fu_f|oyhWA0sZQieq+m4 z>?uR@_FNcaBo|=Ri25j9{o?83|1wIkdlNXcmW$ecY>hmd?i%0Zj%N2;>c9Is{(t|@ z*<>5)TT;UFOts#d4twL8F~6PkW94I6o=VB6xT$*51ic74Et0)`jH8%JwUI|aal?6` z*c7;b9|N;;SGDFRVN`jm!rHMKCW1G~z<`pWd}Ej64ry^gOD1Y(qZ&yTE!H6|W|1=? zBu1S<9G{C}8qAgSnKy9(e?sR{Y8S(|0ds%`|86{liYIRcwp=_#@kdHRs2xqNSI=or z)$aE?Ua@#VCG}o%WRX1-5Viw_eLfK#-hY$j6{CRJTYHb-*d>tWF*GkKjE^1R1cG}< zhtX;9XGSRgYk&=*@k-|RaoW2$XMGT(Xr%uN1GhM{nA+Fx2g8YvA-A8{cverTx>2eFif3X3#*l6KOsML z2Ru6$GL!Q|p?*jVX~@HON08!r!;Xqx>k|&aDu-0%Ub7fg?-bV?A+7B8eY8fo4hD=mBUsBNsGkIZgIRUbt5y+V!*A@q`AX+W!I}ZQz|h-enC-hj zO>3=CdaJ$qEx6z1=(i9`$|BU@IP+Ya|9sPLJb#~E8A${*-+fHWQ5ew8#PWp}$Wk|R zbBD@2J}V=%chg>rq~GseWq&G9*ajH!EYax}9Q7wGB&zQaga)x!)D#*3`U%eOo5T2b zSKROZ*N}(2#LN`&MapA3J1$AyvNCVey5>1+g*}xckz=P*@m?JG*8U;Qm&8}@KBxYU>)l1mm0Go($QXi zD_h9h#DEGgN8kPluAlogs<-uIk>YfWT-w3m`MFR)Whe(oN1xC^71BYw>-w#$T;sol z<{Ex}raxh}DF84KR&ytJIslZHwIkPGNK0GxPyqvCZ-6)R?*L}7WrL>ol&JWfh&$W0DTPKU%w{ZiGQW`1qN z^#V#1q!Lk?D|HEhsF`Nj?zx>GlZ4e-t|@vc+DvFu0T$icDvG^KQT0SPZr26M<;~$% zZW}aTzKb)}N0FM)2gW5(ZC~iyIKBV$b~F#x8rm%kj_;#aWsi2Zil-bFQambT4JmQFUrvuB z`idY7krqoJW21+dP@&{lbU}N*1)_1IFvd*NKNh%O7s{i0t&84rJA(Vsm5f^DuV7`g z$=XqMPx0qJ;#x`ySpbZgJ0zkoV0Y&M)jChCWQO&(Tew{7=cN1G$g~{;nxYvI4SOkY)`qV%3^>e!97**W;TC)La)e@MGEQc-cP z%<-)P8D3`#2d-I4&j$_dJuH|^TPF?tidmE^U?TD0k6;WecgDL2Ko4v?j@1^#1OSzs z4mzg4-gGy^Mxz4PSzX0cm{u}@ zH8DSE;UFtv-|M17xVrYux0i!tJnJnFVC&uI+8l#fR}TZ#{gl!1eWXbBLFac}A1&_^E_dex zS&AKyPTgHDD7%#~IglmAP+T1z%!04$m)8T>pqS{@`2K@2C1ANFvsjS06S_4JO8}L` zrq(aKQ0owL-BG=S?h8Pw)a4Iu823!y)+?a_&+Vl1Y6Z05JFxHlHyvH8mb3QFPp?0^ z|4A(iNVEeYWMGAJ>4X#h9iWvA?7h@ZxVxw|K{n>hgLc2C*rD6YjvE>}iU`fOt?=51 z9FV}qK$U_^26@5jZE#zf`5@xVCC`oih+|+>JJFp!$C|^l_g5VQX#1`T^!;b`l(BY? zi_diJ%^W4yZ@%y1+=-hNh227~Ve(?2PAo7iC4>+|cwDkuYPcxNRNwnNbc62)!mB~9 zr^id^bG6M7Yv$y$cQfb5L#KkBUCYQy!>;*MTE+}?bmu@Fx!r9$vys_L`_-OMlj1Qg z`Sf?@NJO+hGJw0R{BN9UBF~m&HyO_ufX&}f9nOF=Jd5s`nWY6l6?lyvzK`pvIN28T zm2WbOnbnCl0C5E3hz6fpj<9rgG0&TPD_lm=`+lncbB7s_O|)`}u<9;QoNDQxa|=cQ zv&|uVFyq(&$e6KiC=cxw?Wbq2YR*B}*6TdS|MXUW@GihQw;2IgcZKds5SzAgw#x`c zZp0lIBZAxk^phh2vrb=CNIGMr8yiU0_PjQ{16rf^RdAQuo(iPaZ~1ZgXfn;lGqMJ= zJ)y=0N#a&uC=%pbv!w&<#zd(gPEN^f4x7fubJp4|+|FeP>nyo)(l5S?9-dFwq!Irx z9zz@pBvD?k-5yPnVUlPRn16smVn-PD!aE54VDfR8gLvwD8M->#WYn7H#0I&|Keu>{ z&^uZEkaY6>Dpr~txc)Y~Ra@i4IccSfX4Z%)8mTjJ&AnNFWYyt1Gt*LHiec~bVDRSi zx$I{}E`Fz|btzFf$i^a z=TY-f7ETXXR;D*{-n5;gDD#VUy|kmIBsl^pz1=_hBF6p<)Zp$17n}SS-Q~poZH(cu z%vx4iIei7?do9~`NLqFLfl%imW*A7v;S@8#m4+d2cdg|HrufFG&*hIoru1G7zr5g%&7QcU1O1N&`6#a+ZS}v!g?H@jU`0odQR~;XzI9aoN&CT` z8@ku1>sB>#dJ2w*_q1SZF4*AQ?5XG~&wVl4!kthqlA@o;Kr> zLhS}cS4%?3gI1tOnERI<>m;`)>j$epk>LG)WsCXgucuSOc+-zl!I1iNg5M7g$5)wH z;<58bT`GS&`L}t|*m8*xS;0Edm)4ZN_9Y`=b2hywi6qf!N7H;iFb=Os+bI1{^q6N7 z4Hr8Gn_lI>K&Yg;`^)!Nn%biF?j_So#zgl;J@ak>ky&}w1|7e6~slNh!9@RR;Z$pUrK$;q92N#vVFXQ zyuGuo_P_bw>;jb0qtPp^Isil15m6d{-#;U3yTR0AOw*5iLIy_d-C4AOWGA{MkWLF3h@6X0}N2| z4gIlS@~WY(1R4K!LICN(I?#Yq^k)v`eiGNs-NeFt0p9=>v$9X@-)5}1#^<53z$$W( zC>jj9{r;K}VR}5`49bec#Dj-$jWBr^YxbI(@EO_`aHBFO0f=FG4WtAlAx7Nb)eaZ(;0zd4IQQs<}pN}s#QLB>4!JjXi zFC$n4-X;Xs9xS{!8I8*_yplXnRlN~|+ytSv#jc` zurvIJWf?KNDEzp_LhPC$fcRwU3tSFoO0hvpjY_nSs<&w_zP4qwB7dFMp?`~p4eOjf zwffKp37}A(n3gM-lyUuF^le`3wA)?c;{(+2ggw`5++u$u`=$+$Iu%X{M^s7EDoPAT zVspX#$O1tu`cLJCc$Mmr*jdt044E=*{%skxzRC3nlrTp3<4Dh|hvJ-q8z-ZsO%jzp z1LL#vv9nZq_|~Lu<+<_PZ>hgbayS|PI>jwFu_Dd1S;SbFRF3Bt<7Eb|?NA)0f4FsF z8kaAI62CXyXUcur0qOS`&2jM(8!dHt-b<;;<1ukOA!35+W=+>lryjAyjXD;r4A?7t zSm5^EByDkht#A2$4)Js|=MPNJd&QWv7Xo)MbOTq+YDb?SNgZfjqTe2jjo+>b9lHkU zV9^-{wp1CXp;w`L)J})5aS6l`SNQ^zTy23dgPa|nGb$cyvlvdjF;FKk2dR`*r6r0w zGlcG<#zq5{K%wWtt>VTC-w=JyMSti%BgxN5r6JF?+x{uMBSuCtqFUY^rGzf8Z^fbrJjmG&yY`}qGNvEsOEA4VH(am3pZ_@9Y0##> z&g`2KPO>#Wp#8JWiX#|B3G-c+sFdjRaTjVyLuM44KWKg|B0|fb+Et8K zr23X1VCytbPXwka#DL3XK_6y&IU?Uu({T?|C6zA-ByNiw&7*$NEvS)SUJLlTioT=^ zQ~+Gm24|CCe3*WZU=(Lce?+n6>3yIH)9yTflKdUcq~j%x!sO>2?f#TVOZj$CT*C0i z0IH&gh6EP$rx#vnW}}fWQ@FP1sZwoye6=ye8(LVRbi66kUcZV3?|R7O=d9d1CB`pH znZKs67Bjh?T8K8wM}ia5A*nHISVrF5{D9Y6{D5C?b;s~)x<-35>kEE5f01$=jlq2S zINc0|NM>v)cg9$BvmNN}zA@<+zFH232+ZCBLj&(#yZN%|1wxXx^mgj6Fn_v(bLfFO zuLhFVV!85bPBnx=UHxt!Vu2fG5&oflmg1E2=b{)5N~1KW*YrA|{u4tuN%_fD!2z0j zq#iOkDZ~E#+B26dS02W+5H?^cSd<(8#3jt@c4jg*>nj+nib45#tSLS@C9U?e&~_AU zavgt{oORz7>>;Q8E5NtB8b{u%*9A_rkIj0*Zm$mme-cc{o9H%SrTJSKq(b=RIy?ML z|6!XNev9(M6Bivx@jY}|CaX&_1TMdJnC7<{eEPT=r-g7!`;J&ZGE!~>=Y5VhDkLfYBzuX`8bnfdrS9pkO z@f_2gvdVpTRGng)jbJBn>@Uh9yVWobik=4BsriNe)XdD+at)&Jd!OTIs=n<1O6(wE zYD0nrFQP7V^9`ln7gI@jW?;$iz9BPu;nWbBkFeR+3$fea753rptX8&d592A3C_^p~ z>Rbx)mAXK|BG_wrxgMrm7i)V}GbikG-gFe!Ir7;L#~fQ^8^Kbu$evxZN(@Z>Na|4> zM-s!j7lO>9($3)&=)U>h7f;(6mLPo`v2T_t3^@#V%ZVTbC-C8-l~4IMOa-loI@YBh z_Y6JF&W5RD8?KFP=Q8XHLdBeFves6C9t<9f=~GUJf~Em>hM+hV_d% zp?o*7;qZZDZ%}Xz$zpz-&0IhN)$fAI=uT6@CB3$oQ&2Ou|C0n(NzUH(&Eu4~X)?qT z6a%S|Ho80!4#bq#?22O8ANZ&{n8Gn=!GZab8k=p;7~D!$rwMATLf`Kt-ly?i1_xU8 zm#Ek4_#Q%0kI)l`9o9-nGBie{ATx_i+m&EwAOj_O3j_ierC%l|;K2W_R7`4Qt9U6?zK3`!- z_G_*47!_#SYl$&j1Q|SAL?pFzO zAdQ4K^%2mv-?bYya~+A38(TG9X{nr3bVy_@L!do`zy1|xtq3A;qHVe>7k8hz{HXH> zzj6f_&RrVL!ikGsUm^pDGa(0jq$hm@pV0jFMO+MhQINY{4DL;PZI~ ze~JsbU(r8W*Hx}E)8o)jR`&+sD9H1wBB{}gb@{QtpBYoFNNaH}y4nu)L~?exuS5=P zcY_x@LF<}073qc17t11x_TptHR%BayQ^Y1)`Kn20V`-st*$_2y!?p_Aa4AG)ZjTBh>i)J8( zCA@?=iLphY`|=d~>_qc(k)LZeumr$gX{ZEfkt*r!!U5s}+9W+H;CB4GQ9G)aa|YpT z>RXq#(lvUw3@lCVy6LAj;jny1aTu4Kb!6CR$Z-=^x3*ils@?x504zSbK`Oa84k~^#8tt$>cHQ4^49&GmfS~5K zsOC0-e!ev>OH6U*73!K3MTbyB`2N&gF|URtp8fRJP=m@s382H^(CldD^r!YZ>>yMN zn=$zcvJpx^CU3dJ(MLI3sCZHUiOJ-P!TD@E8qj_0f!@{YgqrNt#I6bNa~TpJbg0if z!4tU=dq8|H55gl;y7aS9UVR`RZ(er7JJ;{OBgJIm~n}{nLW`=o;cSH|yHUy8U z)L&Pth8!zKRj{do3T$EJ(w zvYnI*O}>MJ!;L_&`=kc#AghGY@b@2jiNq95O08-1${w=Ft+3RNBZj>blvQFe2=I{> zt0g0S+~6QZ;pzG`XQWCi*(Yptym^B*h^-R3i9(`$zbMktMn_wP3IEgF5EZ`Dn2N|- zuhW8u{w8VpRjb`9(P+#w~=k{2&Rw0+8eY2So;XkLq zhb8w=#SNl-+t!Sh)LtcpZDa2Eg}YV%*^;_WWf?Qp=vIgm{Ys=>vlZ!wJpg_M2cSNV z0TRPFmT}hT0JfDoH*a~*e&UL#1Mf`eF`#|NAUwoDzT5$(ca~Do0n2Z>=ayeX0y2+r_3q`#kp&w+ z=b)?2Js)A_j(bB(I?*nUEhlR9GlR`z@9b5)l#)kFV-|o)qu@n|jQ1TP7;p!ygwTZH z5QYkC@X$#md210|9WtM*bcXjf`Cg4zAk%NR=^6krgUeYF?H_^|Q859>elPNzVgq0I zBmB6$RNQXF9fJ$15&~W8C?Rhs=Ji(^8EarGV)fC+t6US5J9q5?IV9#fTgFgn0EWN@ z0NaNIxk=^xn*+?f48i*-^PNAu-1gU}cB4>W$zyR?na20@48juDGsAs&HuFD9Y zHL&QJkyxEgVo^X@cs-v`4*l@-0%J>TMda9|PQ8doV6BhWK5o zS;*}!d`T42>5ZXVi6qFp;1_zTr`owq%pdgd#FB?S*+{wSY{s-o`LSH&YfmZ$1q-}l z5>g}A_->uigb+U&35Nk|lDi;+i2xqG?sfSj@+}G9syC*!?*PA`eEjB5W0fvy+t2XE zxW#emH~xr$+U|M|RAgmz>F8QI9Sn2fja8$=Y}|hBuc+JH0 zev_ejY5_+E8?wO^_-SN4-m)C%Q=omezD$FXp{@1vT*=4lxkuacnD1w0(B0+fWY^vh zCb2-zIoG%(v6%^7^IjUS(@;Q@fXm!=`+EB$<(tQQ-*U!f05#=JQ05c?C_ZW*MA2s|SotWH_r z%?8@UH1ms<8joiFgo{d?#uN}x1@?T$rt2}+a1cA2ic2Zc5-0i`2r_66L<1UL3~%pv z>vz0E&@vdHNo}U`?!X}J+~ZY3Ruana)!bkRFBxDn3#Jd|Zt&FH)i}zKYgEXSEf`7g zUg_~JmaA1uq%bDnb-$WUhf(S3xvAH`w)$V#$zq!rS`Bk_(WcWSn0vRJjdS?jh0lj9 zBql7wYuH!KefLEpYQ+G~wC!O=CH1{8izQwq>zH0I#Y(dI$a|}cM*Brjye=2tXC46J z0MmaCz4IWi3Jsb|8|53DRL^n1YQS|OZe99XzrFtK#s!Y>$_$l&@n_2xzYQ)AkJCpU z5xY_u9^Hd}GeV730@P9E&qeupYFNyYd z8`q8+U|McIULCG$f7F*03|Eqyg_snm^-;gH&}WOt3c!OUq!vL9TKM(Wl-2IE zWXHtIXLol$FyB#b57g z9B#Q}T@_A1C71L!(R(S&;DV9 z`-*dtFwOf>HCg%eIOG@F(5ID<5o?N~6(LRsRa3$M7AXLWyelhh0{b2F@@y1YBEb&_ zCKJkT3?BXQC`0n2T?lyf))=CUVH|`zdwaEArLf%J`BYuvAHPt>)8aEjpI};M0{5@p zZw#9u%x1zWu!(ETwVa!AV&k?VM6D#sjchRaxU0|%LP3`w{qf^O-;vyW7bsjD4S&S| zBt`4r$Vp)fy=W^MbbH)fiBIRUyqe0(2_BS0sB%dy77Rs){w8DFTL-u_a#I5pY=dpR zpSZ2Rhg-llqaiWAYt@QU@y(4aqAXNDR2?_N@B`UyWGk9suuAPq^ zv^9CrdD%7e3yv=7^||l$;v#2-y}q5@c{2Mp=a%WLijDDGLX%#^atbxcTPbIm%OaX3 z&P{UsTn$@b*1ma$cp8-XU{#+vwG98uV3Vi)b6J$)xLCTK>cL{+f{o=Zal8*#+P!n7 zi=N>F-vmALtu2$mz*=Hgm%xty3IOMd_~*2wt=LLIQIA}7AU>ELWPS;VnONPg_~m%2w@;n99OnP;V%O=QLqq$=`yqS37{iyfGkN6bZ|al$fFQavp(1ZiHx6pvM&j5Z$g4zq5Ya z-(9ug)%j21yvBS#V6d_m6McTX-7Gn=(>TQX_ z09X*G$bt3yJkD19(!JOj8@tNVU!#7pbPc8lLp}WSzGVpg6f!`AON|K z@r+i~L-BD@8IRi7Nu@DhVE)VnY;PMAxbil_a&)3Fmv+%xxt54-kV1Y(#%gXS=tgwT z26kqB>xGu9RQE4VuzctbfqdsV0%$332=aiJAcKYk7wMSD8>&LYE+JQa4gYxc{>|Mq*5pyil~>Ht_;s4PuwsNUiGzi ze-dE}dw0x#mIM-(8GquSGYB=6$Hv_GYXs_Y^D7f~Q&Id+nmUADhFX4XA~?uIL7{}a zY4Yx>IFkBNh--AeeUPklAZa2S!HhEr7K2`>bqVghBA$R2P?z3T2}AS~TFO8-h`LR~ zHS30TV@dtv+Lcyr=SK*YA;x)LTuj=58L)-%c);%IV4PinzltTnbYr!YS9fLou3RBH zzbaL&QB{AP#23vN4pcwuaqBB) zl4K;$`)6`q<6=NZ)Lv&3nosEk|GWi7E&ErKUUT&Pkt0eeo-znuHT+wQeZ{Y1)=U>e ziOy&fa63ZB9AS*$-4Q?xx4QOgC*}#GO-LYV9;R46*H`Fjk!Cx@yBlQVZ-_Z$NEABA zekpNQtU~c~SIfBvXXSncU-^0La}Sc7JuQ~~2@D|)Sbe7D;45e~_`tkEc=M1BwB;6= zaQ-z4&I%0wH52)dDzF($DyiQ+$JtCPb9y#1Uz%e_&zfU8I1GkB+Dg0mq6x!YH_w`h zpWOJNoiBk|&uH&-1rOHM@ot2mB#-#pcS&vzWu#?0C5WYUy&$0&`~E-0tnyxbp}t|@ z(6<;2mh9T*u_{@$KZZ|S!{b?4o&}Jc1u>5n`=-D%vu^#?DWy4Wk=E*A@K!iiLYuJ@ z2*-cb27ovDdla;<|4o<;{Eh2aq?TMo(QoPXuv^6OmvQ=bPps(+*%*Eb;Z|rf5e>Wi zBlyQE(9jZ_6C9o==7pWjCz*5+oj}m5j7}UN-RL;Jek&uot?p z!Jj`eEWoLAFx3Zz?8T1b{xIqlHed|w3_)LCN)c4(XvB1H8OA#fL;pg4L5$c7Fcy(O zI+M<&mI_BwuA`?$m%dx)5%zwl94cfErhYsZbFkM7`73T3;W7_+7V3&0ii>VEL>_{dB z6s^w!IcMS!+3yOPH{Nd0THekG$ny+=?O4Oxc>Lv;1M*YMwlxrL z7tn;)51J$~P2dv^cox9<8YrW0+~A0l?gt$=l1e5Bi?8IJSN6u*?#IoAm* zgQF1yGHL6yodP@N|m3Xx8t$`j8QC@pTd+6GH9r=AV>xcRVAQW8A7oJ9C}>X zBZQX{gnHW`jw4ZA$}nV@fz`_?7B33m&$m=Uzy{MC?7mki4nH=ANSG)E(o-2XE{n08=M7;OJ9=@aPN0_c8TtG4itjJ47l^6)A=1K}mi<-Bh+s(k_MYjDEhw zRkSk#{*-lQ;d=|o0{?#j1$-~aMI6aiiYMl824&&SvspvDm!^ZL-+t0uiKX?GNC^%{ zjrHzgr`7kQqmUFLJ+JS8)X_Ckgni3;BjR%px)Xuo0we@fNab~C!%=gCBp#oj%rX-F zln1r~3Mim}?+N+J)>gYH(bjr)2zMuc4NrW2S_t30E2~FtPafvdo8(+Q?R)8aU$3jL z7nnQC!XA6;C@ZH}-_f45Q9PugiXvFPB8|WQxe+TC6~neQaG8NdGz5b{AS%$MyXpCG zXsz{SBffg}ia>~FB$FY0_)ZL*J1W_TsWW`IR|75^6-Id%P3fL&qxZzq4L$lQko^u~ z@|!v7p6hDK0m)?WgJ>{Ge{-7TkB#-}D0%H>Gz1y=H!q>7&nZ-q6(` zS|Uies;=lD^NP+9pc1C~(mw5I0oyH<{>ar{h76`okrIE#5yQ z^5V_c8@DzTr{3;(8$;h_UTeKF+Y>2e#(xT0bimO7O)9T$l@YiVZhuKcR-aCrjM?Zv zyO&rwb`%0{c-?nD zX}d?f)rz36x2WV8JHn8c*9PTvPB#|h+P2sa)%>i|oUdJMb4^3KIu`}iq|&u&)=`}C zOUwOI3QIU>;v%DOcn5R%H_!?q;$+}l8>vN%Y) z%pNL1H4*6DkKpiQ6Ii~q8D@Z~^Qja;ZABbke-Xk{4>#h5+dF~i98$7PMw4Yv?KU{b z13{Z56Xu~M-RuwSwBDK#=iM8i_)U^Yf^Nr9VKVOaw$_9LcYp0?Vas#0>p`B z``|fXU<=ULBAvt$2Ww5 z&Q{^!+};qdbDz?5;27L90Gf`PSZV6D>qW?x2ilT!151r%f?)pc)r>Vt5w!!7j=L6u z=)lQd%=!hVt%P(!r2lT;VA-thKhr3|puK~Nr=7GLwDU(!Wssx5@^38s_pubXC`0FN zDfI1ULkpypsn5v}9CUaD0YmfMut^?gpZDi}2QZv{zJ>al8ZfsJ5U8t?_;!(wZ@wViohRLzZE1co^6#hpjq~W|@wN29OP7Y80vl{Qe8OOXYnowJjLRpXCW_kfCdnkHz*AXjf!h)}K z9CCQX_e=Tl%~`o*=EldxB?3(VDq88?IciXr*XsQuVGkY%ge;o?ggYG1F8COaulf`-n^)q+6<^@$ zzC%@F5yqIJR{$eeBU*66q(=|}g9QQZ2n;XjgPVt(gc#M<;|bZKK9E47DXGGbB=gB@ z;SrlFPD4Ur7&^?L0mg`*_0Uln3U`d{l>!PVU`NCEVn8aauXa2T&*#5lg^g%jqDxo! zPtQi4AmULn)vRkqCS#(gID;dOErG7HJUvgUO*7hI5)a>nK}Z#emi06wQXmkZIPzEn zlP5GHnKV#eYXJzls|9~!k>+YxuU6$KKoATP+PQlQtCofO?LAa8t6ne8H0x`KZgZMR zr8p@HIfSwzozZJZ(!NbnLb)Se3#8@k=>YqxJY^!CPPM&?pRn_grQRmy2LMVJ0kvDx z_XYf5ASx-kjgVxu6~*-2?(6JFXbgW8gpT4V>sJQTe{Y%5$8-nwykS8STAC)wkRu>N z(8pd2`?Rey0&cMZF#O6WfO5bRSXZQzu>(1cwGf^RDyQYW&8C_=CDTrn)Ur*7-eqy} z5GAbcvkS_x+sie`MD?u_xPWbs@;Wa%nbl@QnXY|n3O{r+EtIxFC?!%lSLuRXFGb)t zjfOadrSTvo4X8mH!u3ZM+cWA5cy3!6L(uCv7hQ)rmdJ5Pa zHS5;l!hyq46pShPC?Q)svU$qjP&g1mK+{b`qak$d)(Hn6 zvJZ|s{$OY-F$wpLfXJ~T7EnL|1^m$1(2{YR+3WJwnuSf)9Sf9l71FzIje*i~AP~+P z*g7)YdFQ0EhB1#aMChQ5LMVqi-^}4FMVp+F-QO^@ZW$7()+C$@Xc_^xiBCJ?u0b-O zF!Bp`sLZG(0|PpDH6f`ioi(ee=QCH1S~~EnqbPNSj8aB`3#7y!1Z1~(2A@SO+`N1z zIZ@b3X4b@P?&=k7!rlGpy(Wx-!V8v|pNVWyR~PUj!o8P_OQbYGAPLndo8s~@*` zXJr`fv6*~0)TCWbfLjsMUlT~luoGe#J1s$}2>s`3B+E2l#F+1N{Vg&8YHy7O(j2yC zfa`)m3ipBJOkgL;`Av?YUB+R6k<8Bxz|kZ_M)KlyX{;?60Jl5L+|3)n zY6itIt+NB^cm~0lyO;nnQaTP`VzI5$%V=ZzDH<^E7LeDXIC2TVZ>wTM9tD+_Q{0!( z#u{Q+TuMsidrzC4B(Xb{cL{pK=w^7^A;-l)6(0jvo_=0ZNH|Nx%qEwX^f1(K!;R{ihHJ9vXHoF04Na zTngxZi>2>4(w1ehHDhplWI6go-tRYACht0EcP+OJC0^*AkOF;*O3+IWsvpyzn#Hdj zHs)W`-Z8a+0t(oCWD{uZQQ8fE9{O`EY^a28>L6Sq17JekLR9j^dSggSCap%V&Y>83 znQ>)E;{;&qjes4K#``p?ihw;rDLRGYkhKEoOj@P;$VJvrkBBfr0yu8xeNiMdWC#JX z9UR(q7wjC2`ntw<9RR#?$%H_n$=i`hS&fP=tep5>c39xxU;_?)XcQ8rrw~Rg$an^LeUwMlb>Kfn zA)$kWZSUL%*LufKxNj6HBOo>diqb&Hh8%yrckX8ok3y3PVmd(7g8cBBdx7zzfMf5) zevgboS_5&f;g*?jomb|then|x;Q7lGWguf6$PlrCaGne!?jMD<;f`fE=;2XlZZE@o zqkv;JUk3O5xb`wwK-g}T;rRPUVO<2op{+8!+kp%*3v!|>!-P@5(VLdR)r;e4$f6AJ z+RyJr(Gw%lsYS@H4N|O)Q-;+-+7vIvBz>!%$jdmvoecqujm=KJCxKhj8fJ;K1Cs=` z2m|juHzBK@yAH$>sJ`&8n|Qd}VzUf@?2VhiD1opnqzsLT`~Jk~O{2My;xQ)VmFtS- zZ4$yHU~dD?UCb%@%+^}mr%t0fc+FY+@!kJDo2=TS1cFHPc;#}eKI5Nib$Y_zZpPxU zMbhresdI=(om_d!)9SkWK6(QhOb|{eU?#Bq++~zMyGT zXOb|25~QRBOOJird*J`?R#b5dEmj6b3|M~lL%Dt#_}8m2zh)KqQ(prWi?^ubwV`gU2{mJbz6&s8igvhv6I1HqQVkvYBfMSI?cA=Y^l~kz@^en>*#_(GE*x zG>78^@agns9N$Azo)(^58?Q4fdwFMxZ|0atCIBP!<1af1q2N-YlODt|oqGP*;%pj1 z?l`Np4)p7+K~&KlZmIs=>NeTdZE#2FuL26#45-P75JZTE8+Sjn%{lk282{dRXrm7s%%K^|A~59Dwe6giDmM5bodi4D@Qp zC8R=#=+>Lxh&%amiPG{gF2C{um;o=#A%u=ZB8inNDlq4%b7wwvRNTKk4JBV zuH$l?b1!j{U$635bX6;w?-MR_!P%ZCzi$-wJ>Fe;8O>&LZaX--?NA}%iWAYl7gG9d zY5#lamyxYy{A@6aPbycY%@w?`Y)MquIq^5QD9P_v;(Lb(Z>^GRtIDIM?!(6VwSWS4 zRFDn)T|fajsNtW&;Wko9flyTBj!SUggRjG2+uZ-%VVE)M&ouyH-nq@yK|89~ogUJu zBn(!LasQXWqHn5j!DVGA>#W0(Lh;edJQ>qLiaTX`3Y(w z1a60L{u<_b(-rSa+;cjp+%GQT@1((A{Q@U0CWvJqLmX%_fr)6<8Ehsq4n^kb>0I7& zg`T_oaSH$ufs7_SFT`dOc-=pD;sPL&hBP!F$-MAk7yGXPObaq<0hXT_bbPkV5tpNfV$bbMW z;?*}kuT4SW$*0`f`?>=Gu(fQ!f0ChmowCX__FcIGwNV@Md(3fCv0*44`w6w2WOY6k3*L?$O>kFMgm9|HQ*b+%sXR7 zC^NIqiGu zfE5HST?i6E5>?v>5P11>;LLrne6Yc^B5WQmi`eepRk)@+BmlEM>_QK~$6I zEz{1IC;v^=7oK-_2YPTr2DmqhghIhUfTk}NwjGU(8~sesmNa?Nywx5YEl>hs^u}EQw5jge8M!M0^6H z&D4k_VM^I@9EL;-hY=vqHP55LQ!;3~_|cAX%C4WC!3;2DoEA8=1W!NhVAdCFaODl% zQCh~7V%32@u^xgUf<&`~ZEcv0_J@R$z*pM1-`YGr`}Itvo0|K+YZ+{!ob(U4O6GdK z_7ike5m`fZcIQvD#x=`!_hIuP^J>rY#gsdc(X@a9HUru4dLe{*$86#)RvO`l{)$$6 zdF%M&y?I0m(7YG2b$Dq_jaE`V-N@!nDpUvoO9lhU`$g6sr+%4v9_bG5FCUGHSbZXwK=bh zSbiHg=bj>!CzF?P*)xFU6#~tDv`6<5}LgKz$yZd+Q7dfv0*d6lrXWk7OMyIOD1wlP$oS zBQk*jk8>>i%^2J)Z){uT!)}*&89b>wVM-xx`CC_9cK;eY>i^`Xe|8{>@56M9a+gtz9fedeMLWaL}P~Vu1 z%W%t<%J9Y(%1{H~FW#S{Tm8y#wL3DtpX7@q7y8c6q_GmR8Ag zPCI#TzNFk8shn2A@w`!i2+?5#lBRaXoeu9Hl|qti3PQgw$CrVM+&tPw24+jC%#1## zLH=TtaeWXrS67F~7hKz8n3u{WK+Wpvdk2ub|6qqu(1r;lb@Z~It2maWITmBG3 zFs$X2l#U?%%Pwp?!DR5%yK|o!? zJ5D!i^pSbSc$piw)THPa*Flw`M=ky($?=qLt2dYU6LlhyLNI75uQIO_04^-_?Yk2O z?6wPLOq-9|+FB%95{MVY@&3eVShlnh-MV$gamOBvH^zO8X;WsZ5UTSpIt6>|*#{06 z(3puetJdM&cR$6qOIIRoB@vAT(6?_-9DD3x=-jzj8LKF5^MX0K&1=A?HbS}dYhwq2I_ZgtP?g;g~ z998ybTVmn|pJDEo3sF;73yV7_EiJ*Iy#`?TQ3oPmvNrQCV-yP)ti%Tsr(^B9DkKvY zO5$D6y?ZH+I(i6t^eXcVZ=}^5gfWICOIG8fNuOc)@?}UR9K<4J7_i$;IOgbKC@u@- z%DDgPYML;9{1l`z2^@Urekd*~#nb0&AK;D%p&dnlj8;6@x|zKATdFPp4Jls$07t7WcCDv&|lQy-;GN1E*hDiX%>v z825Y~CcSH5;ny0*KHr23FOLC&E53NyZfzlb??EAfSDne^vtvF1DY1S=xz44OR=hRb1n~!*1H~yUoDK}d5>Lqp z+fuI%VQ{Axs_JLN^;@!Lr~UqkLt0F{?!I@i3{s_O3>ptqyXW@$7oKV|acbBC;u*aB z3Gi$8_)&jF=T$MxO>59glPWa!_{qR@_uM{@;p+Mz?l2t{7CV0ia1f?@Zu7f8iO&)` z`W2^DNbyZmfydl32RwqqYeN|8_Csk3sp2#SzUSvD+3R0;y2-@J5lhMU!Y9Dx?(riZ zLf6$%e3{XpxiUm896Jg4)IGQNW4N{&q~5D)5Ao;h#9N zHi%bz84@Ka3>@!Sf?OG%Z8C9!FT;O70j_Y5AMp^nuZiJHi-G$6aP%Z#rZ2F-v*S&+wM=|zl{ML zAIYc?XCT5%bL`|3%jsCg3)Kw8o8sc$heFOF#J;PZ{ZNTNdusyk`N)W`(xL~zU`zv3rC#9)uKhFh(nEVDr(&F`#nJ`>)G-U*{2~u zr`t?|ks!k9y>_Q%VN?ZrvGxuEgy0TLmEqY}t!zBsuRRAu6ZKw_irnQGHhxevL$_%rf0q}}jCX@*e za3s5j0G6QKX)&9x0#N6kGaTbrDUd_lm9)bg_oY(C`r@XNNDJ5Q0;J9as4nH-0MD^8 zlnES^5cD)iX%k=_fCmv;rerW3?J_`gqLLC#DsNNuqJ}fU9u}wP0>V*Q!ijna6G{eK zKaxo7N+bl8Sj>s%Mf-L6Vv-<{WTQl%FyxuLPW}C~MYyX1$_B~$NnoIq?y^-jb*mvC zR&*ag2x($gc-5}R6unM_XmA)Vlp6gts(T;v@Ci?R(C>{SILnm!^hz7ImSsbGBXNOL zrV(h2AycJ6V+Iy2vN3;QC2qX4C&Cfr8SC=>-8zC$?&R(-4Ffps%s57zEb-DawV3c$ z2#dcC@wF03w!!N|a{sdbsiVr1i?lNjZo9WNw+-M|Nb{1N z4b7mMuIOX9;20a4q}q4yeb=C*H0tp?f#A_c zUdBr=ypP)Ygfb#Bs>4dpfoTXl|9>xF^liVv;YSWtS)K{?!j7MRegYnU^fffp)v9)D zECk^gFincrUVa;+ZoU-z?ze~U3CU1hRfmTkcm;`63u>w}ShQ$4KAW*n@iKnXX$>yB z{8ZG|G~kxe_u-3K-y)SF0MpdkhdU0!VH4xVeu#VSxf11_i#qTyETDiL6mlE$_2qE@ z7O(>$x1poK3>ZkK^D_%>Xr`6Q16@L3|3gY~Mr8xijmz#>D){v+7!JF zhfpe8lK{Krsl?a*TEc>alULtr0ECj$XiZ9;~u`$RIYO}Ty`z_;yWSWJMFf37aCzUDj`Quz+Z1Xzhy z8P?>Lv5CaIJPQ0&o($D__o5>iviB`thSq){Nao8xftBt#fA6}&T?a``*)41;$vi66 zs;?y(AVH#$JtUze)HC2Mc7N}#y);gIf6&dxCJlf@5UC|?l42x9Z2kz<5ReQ0rv#9%C%h$68z;&$oK!NymRBDipvr-uV(aq~uf{_HY4|ks~ zY4fZBu$p354&19e9XLlKnI4-p05$+u3E(96iA+i7-3T$10dQKR)*wkUMkPIlI^q~D z&5r}1`rXOs=TKYTrAa^&vaYE}lxBA73)piVz22N4rHo52DaG%oPQu{&y=#ltT%z@# zg9P_^oHjAwQlIDNgtNM!q$~m3aulD@vc)OP`yz&Ub2#39qYCHzqAP^hTpk@iFqU|1 zsg95^4d9oTmZ7e$4%6O?;IkhNuUEAO2$&SeZcb?!P7izy@tk3nf<;XVxA}<8kJEnqu-G!OYZ@_(iTDk?{ zMeVVkq{Q1e3x-UO`XUeR++I;xi^YqUz%Wh3i;58rdrOySs2H8gyP~G531b($hmw*g zdh{$seO(|IQZ;$vT-<%5p_eg&CiGh0mrg!N31C4)M5#^DjDG$@cVE7$r^YO{lIe6+{V-XBSG4z1JIP&QI zQ527&Voep^edj|gTDTl{-}xj44Csfh-O5zIzWvrzJb2%Kp=&w@?$r;6A29@F$HWOUQP)s~o%?h_G!lizm|7P+ z@%ZzYF>MJtbt=IrXY7Z8gZeA-`1$je;@!8VtM)#4-?RABn5%rRmLH!=q<{i`2#{^L zUdnR;-_h2W1$Z9aW$|u{eV+ode3vk907c3*oj07}mHi4*0N# zn1AdCPps}f=SX53yEH}FFkWI7`p#u#8z{v9fYcesr$-u#mzjO#Wt>+qo_hFJ(eUZ9@Qu zCw0KRR(^)Rh@UqZ>u2O{GBLA0h=T?3WHX&M=WeSD0Wy)CJ%2YHU0}J_aG9B0uHXOKd9)%qN$DQK}m%-l&W*X*sLTVL`eC>V?lew zsLXqjWZ=EVXyR-t$nBEJ-I_x7&4xp{1Nw$z@NX$_sS0bLfS>{xcRg^*7zk=S4k{ma z?+j6`05NeT)OEs^94>(1JEL0H-FqBzrD@q< zjp^#_xo}P3CJ^~IN+bzdymG_|hhmQV{I1@$4m3Sna9ayo#EiAbnb|TWRq^K`?1c^- z8L??&i*#Vr&VZHHSZN|~rmcx!eE|DQMlKYb4+0Y{v8GOM_FHklNtpq`unMA;8+Oac z^82`rDvDE#I5paOQcC>g_G-+S62sTCTfoopIgQ%P1sfX4vyZrU>on?Rh@zBW*v|s^ zY$8XZg`l=7g-9&Y!K9G&`tZKBG%=V#0>k`Y^?Unn*yud_6I{KKd?R3<{M%-6OWH1U zwsUXO0t(pH5PmeRBm~XAuN_m{IYg0dgLFVCEB&}miM)glTmDAo{BYHEyhIrJR36li zWz!^qO(`5A)Yh)6ew6t0&DIzI^F16X(cm}+=FR;Ufq?GiIgvmrC9r(iYP>df0-73a zWHK2H{n>udH1GPt)i{Vdw>6+wuQJ?x+vV7EU@v4cT+MySWCl+>F&36h(4|{3?j3VI zdhOIHcikh7I1r<*yC2n6EqMIVm+|1kqhT5vKKN)hW_+>$h8e+e#~+N*xBW6#=H!!( zz>U}b4e!1^9k0AN9;cmo7&>?9go*FZ#+1qPV46{!an4cr?eEUWl{xdwqj1md&*H5& zCgGJAKg5U=hNHMNif2GyEjC8Dq_pAssU2BdwPyjE3Kw2*kJNL=~)MSr~MrZaA$HGOUd z$Vj2H;XGx%D5<<7MD>M-EKT8yuBL$yu@9V?l&fF83A9lXCP0}`Mj~%f6!W%8slZ=K zlSo%ba92}MwajBGkt*wKwu~~rWM%T}B1|d+V2*U${T&rx*)mk-0v%L+OtzDO=ju~s z{QBCK@$+Q)^|gAwNnZxhPKLbtI+Vfh2ljo+knIQGK)N9rHrNlM<1&(Ta4&orGy$YF ziH`;|=xNC@$A-Sh@Lg+_7Y z%r!7yE~T!75_qv^HGiw7h+QcuexEk+IxPDw5Zq?bgdj8N!=BAQl4Ly0{-&)4{}u0)hALVC|4 znU2SDveuIt)DL?MnD!FxCp9=2XW#(;+@c#k#ISuP0xxE;@Gp5>0;9$O@i`J!U(gui z&ZreYmT$BB905ix*a4U^>ie$l24DdYaD_5Jgha4hZ}kt2G;yM#*^v}XC$NL9Lya(b zvklVhPFl9!dfe7+0TNj4w%k=LL|#~kO=W|mq=8c}mRBKtxMH0YOfC|>Eh@i^&}5SU zrn_&FM%@u2?;OPf5G;_sqV>#kUOt>L=e}>9|LlNIZl3%^>Y0N_LKCe5uoz!LZF zxN_7*3>IE%bE{b6Ys=Fgvli`Y5PF#*NU7vHP0R{X}a2H6+tt4)o^)i+=SAl4n3iWj7xFvQS zplQkjPv*UcL}uTi4TsNfaR{19=S%ZS`?Cq7SYtQi+z%gxDF~7Vfpi?~U)&q_4>}n! zP2cL*)}8N|Zlb!X8CP6>mzuZ*Ol2@~9IlLhjA;$X?z{KK@S}#|h5vqt1@l&6 z(KpL6aNsVO`tfXVPEpn=jtee1QC%MQ*45x2e|r%}A2$p; z_3jEo*D&&?^YNR@PgmCthCF4gTeogVr!$DfBly=NZ{VzRkH)~gc1AcHz_6iv)y8bPFv)6Y0o8UL)btu_`_7}?NWaLD0%;O#f2Va}|jSh;E)cIwr| z&*BRt6G;r%tv5#B{2SG7x3{wf+E^?A$8xZ)q8X3;{Z$-w^nU2oyPGngUN`DoTzp9y zj<697>HhsHY$g`)zkz(On5};5Wotc!H)zL#kWzVDck6Cr!Iw6YEgYe!wsD#!>3PfR zRD;8%iXd~&zq=;>tw#PqQ zwbq8PL$x(0ecMENph;?cR7oTLRUe_ZNrGQO;%qL&eP4Ap?@wv6YUgTT+>v1xU%igc zf8}_&)1}jSSz1Rtovqh<@@vBM*G!;(=Q?@&{Qj+k|7A(YkPXg>;>1HN&+DvH3q;TX zf_7VPw0H*Sk^s+WKv4<^=XuMOCm{_EuDkPGR)&gPeLP}#*9f=zaVZ&?1Q$ec_Nn@F z^(8zRLY60A7VdSsY8#Lt;mg4D+Or86N?jR5s|+!#tqcLj`+XZ3+N^^sk8%c3W+>L6tRBpRneC{GDODal!ziDnTgG@llUx`Sz} z#hY;|Bvt)D36YNO$?@=*A;m$Hta@bl_5T;P*%b@QEu!p?uU!XN4t2-mj0S3%L~tq{ z37CwFU(uxTN5V*40yAJaXWGTuM{w34GWBeB{B2LJt6$5zNGWAjz)elojCccOCc~jI zf^X&t%$gQa_ohoP4yKVU;LdUU(bvxw=qiyy>5o{lP=IrWV344wJmCGj!z!gxs2onQ za&-gBI|X5wMLr>VA$;e55i*Z_`unlX6)b)Q{BJ@n{t3bAmKwaY_){-lf-;{)-krR| z$_gWV17OZhuB?0699#JbqG*SCx3&bNU5Xy}f3{^~cKs56t5`{ng*6ozJ>V$BwD@=T z?8*)*Dx}F#n{-Bqf^(7SJE3>?%Ig9Z=4u!HtRNofoY_qJ)5E?Er-1G>pD zXz;E|c1_pRnt}_jEra&n1rx@9iiSEHtCrU&1EANx1er`42M!&ijDMD6sm#EZ;LsVx zFD^J1^X4r^b#)z{c=UC=^3nwKAFvY!4(f+NgZnE^MS_6xB$QhbE?z(ZKQ+j_k$9UX z1Swz#gEt9w?H0z&j~&$167-4+Z!>NK1E%yhXI6Y`;qkvVBiR^I2}F`f2Q5vVA-Ihr zj*F-SOe+7<7Fvy^A0DP@1kH^C8QL$QQjB3mO zbB*5A?>~pqHFO=NvTiW}Js(m&E*SnGg&3KpxHy`@@43ViAh6c$*@tv1nl6(DS0s9$ z|2mth*Xs<%38xZb{-yT;lSbvPJ^8A8H`t!(S9E;tt&tlpGxf?2&K>0)oB7+0%YAgs zhU=U0yN=Jjy`?e?-?$8uuHJGP-nn55WjJ!nWhj4cBpgby(`hTn%Jcik^08|;7d*-= zTq_8<&3)WBFxC+AP9n)%-}Q;5dz1(VoHsBBa>N6u+MuKx$7Xo|x=5Y%ATaJaMAHl> z%x>oI>=jZZY*ffq+@O~;?mWps@0K7}Hblt<5V*q%YX2la`obA0JaK9a#u^)?8HtMX z#d$mLR!KSh(cIrTCW;nZ5C8yx07*naRK>ww_y|A7%SWAT$H3^Er1B}N zHF|B95HEY~elAA~K+f;pyx=zmMmv0%a zKKK4ynHm7yp1DSiBD$tlqhUarYkqm36Mp$xjlOZM(zz;(3=q+UnUPLrPXhY2JR=b` z@74v`RS@|t6XLSeX{i^CR|5do=z$+!L#N+;xO1WQ;{5t`_MpZ_DT|6W?9HTv6`r@^ zO%eDEd*VGO$rkSHrR zH)vG!#(jykRRRrlCiWd_c(0)(h3L%xQSiL1qxr^x!sAdt0b2)UG!qhoO1k5(hn|mx z^_9>WgJ2ZgcJN;1JSdjbor<`fL3!tL-17VD5f14pxrNGfOc|oFC`wBr zeo25!o)`Mel&4-K5>uWO?!4zY&QmpKr85~wWMBq@2$+WFHR$)J^1dfT@!F~CBtatK zZTBY=O)4QqC}gToZ1^~4T=LkkL{PEAf!<%o+z_iI^vngOXorSh1k1YdpLfUoE1D2|1ZXpk^;2{T%cv(MFV z^hpu5@xs)UMcZ9;Baq(4S~4XuZ*B^2znI3d1#wW8z>y<>q9|4T@>)kmMPKJ#x0cJw zHJa3dP1EfF!+x)wMdSVEItvHCMkSRHTud!l5Ij7wt94GJ>D=8w4Xl8shmvji`k+l1z(IAYNd5=|BigP_Y!6)IEj?zmL!mDHn7{Ce*CX21Rne^ zL*~@}A8 z0{%aH*8wL*k-fjF?w&jw*#t|@IVTB%5kw5Ac!o2fcly6O<;;3|>UrLIisAHB4#jvV zW>iEGP$Xw0!;+R|S=hkF+1;7x?yCQ9s(aGzf*=dB+MmC1*qQFG>h9^Pdf$8RdjPy{ z5B}BvGNhff7+@lz4(xUXUAwkx(74cf$neocMcc5nMWmBlGNaV%@8@vHAk;`BuJvoe zcw#YP`k}uot13-BMZjh=mWnn95s`O+U_i{n@dBs}K~06fHVDo&IGr{qiVTU$LL;el zn=IV>z%7s!W91tr+CqnLIvvn;4R%NLB-?e!!rk{>4Sztx-aUt~eOnGzuGowvi+;e) z9r<|R-e>UN=}(|-`xc@XUXK%3Up*REUOgIz3W~9P>n?2ExD$&Ou0~051zvc5h6p6R z_WJR#+w4YBu%Lz-pDM0%WNpHYXpgGO3TQe5m1MZw&e$6hA(;sYOU;LMjE(EjrcD-Z zpL`|!{!rfwk(09<%U5i~;_p{u>y~|Z=)q?&b=p7Bu|u}Fj+O?Xgx_QyAvrR;cQ`!)Tc=VY+ zG>_K!2%h^#2{!&9L((a@R*SPQ)-ZN_k{H7!kz5#mY~hmRMpoPQtv(tEIEgn&T{Qd4 z1pM~}ovU&HKDV7A7zB~2ohB0cJNkXfDsWnb>H(TUY|QfpI5 zy@Ui1+!Q0OX*~{21e}0WI~5cMgBkbT+4@V(A@!r2=gTU;F=2eCw@SXw;j$!A3XYU2 zt`{fTpy&*~iO)1j`^QHBn#h~~KpDS~NvabRL1vMFrx}uJjR8Z^P?_$-IE5jv5I~v_dX)pjSwT=` zW8Ha;et)f#zwwnUN#IOEN_q)?cbx;?L_6Ad$b{mi(6|bT*pB1uxIyeug1x(H@x%ju zRF=Cyq#$grT--c49UZ#4#DbqnN9vmK`POwlc$0!?)7E|*?OXVzg7BCiqy~Q3aoS`S zELb>p5F^z`L`ZRi_zNvostFL_h7zxcT8rJq{J$(J^TvwE9;ex9Zc6J|mT}_sBs8xu z)CQ};6c_wpaOj41FQwGj_a}u0$Haq0LJpZA>Zsjj%u^)Bm?p$1h!wX(jCZ1LyXN1W zI<85=fif~p1JayihpZMN7iJ3A9wH-(wboA)a0W~!$=6$^kfq;g-1G}PQhvw+oH5FaEj>4rA&ceI@n}=yHe~ju%6-&Nf zk9O@_VBwWdyg^nOx@y&idrmMxKcz6 z4jR%6?b~IeYqu8Sb@3%5@!q>%;lD3`1YdaoOBSv(2tdX_B}+=g!Y>?+U9Fc!4r?(U zzc2`27UxJwr|8;K#-`Owq}_TG$&Xg=)pRDJ-(`C_CS0EeNfI6>XgWl727-7^-yegJ z@}Lq*3VqnQmB49FL{drt&buHHV1xOUj%NL});hl`Mq zoW$1s^IT)IFYZPvap{L`)ldSEd|v8DkUW!Cf%ht%>~V$B&`J0ejl!j}=+C{vc>DM@ zpv(nouF9`R?DkHKTIj5Osi#-JPXD;IaM;$8VLYXAL!H9GXVdPznXx)MezlU%fqMS>9 zw@#c)(Syv1iiuC7{OU&le+J{=qEgj0Z6E)NS9jtgO7cS@+x zr3($hlu}5dB5%WkH8n{;8kN?KafTnGnMk6fK4ta3GTSi@B`U)a3Zc$N!u=!hi>l+Q zI#O3eJW2+J&YDsJLaEU7C8732+a!9eKx4M&#w#>W@u*B;==vG@ejU)VRTdazeDTHi z*s^U89Eu$>rGorhv1}veeZ3Hb1Vr`VjvZTyh6F(dl?~>WB4iey2_ypsAs?j7^Bi#~;&zgLPyEdoLjzf`5wJ)tUXIF2CL}8~jfp5cBHW14vN61l z?HFx5_78JTl^ARe27iqo6R%FexXTlS9mo*YC1f!>n+IS7lxQe8Sj%#@Nul}WtAd>< z@unt8L7p3JW&9KzzVL6bs}zoauG>odgz6&Dupp2^CzV}Bt_vM4v4+RjL2(;z&yPal z^dS(15~{C1k*jXuRDu)2l>?OP6g8DsIL>DZy|-2&6#$X&^>U(3cupruA`V~tByiDl zjo$uGiU6z&cJZ@t%)ApB0Rmqar@pF6C&qe{6xJl+<{HjHHK625zuw}1#}mC};Z$H( znsHA`y>@KK2J9wrVfV|Rx>(-oHl5zZ7#wOdj6< zF-6$34k^I}&FPRoC^{hb$=U2juYO*k^(P&LdgkWhTGyx;yg8RdEp9@iX~M@IS`l3x zP+~-90Ecniq-Mx$V;A~C%2B-UVIlX;YpYRsK*JT+r3evNO*bPkEc_}F{#B7W!A0TE zM0B9_`>35e#)&YX#m5C@K7Mn3(Tvr`luLI*Q9F6JIKEdi0 zyYa|F(?yXHNs@4|;2^$TumXx4T5ko>uYZ@wz7{O}TF}^4fI*tRAD)>`t1zz>mPa^} zG(c!P3XR7kV$=L|gpUQ`z@Vp%96`7rJ^FKGWix#Lm5MXYG()YA+C^@p3Uf&~vlVT%HB5RB2p0WQR<~<2V9m)W6ih__fJ2480i1glATa?%7N$7WO!=i7BNkxT> zlxFsK3M&D7=p0m~Ue5!UJ`LNulVJ;T&JI`VT56II?d5i`7H@N5S4go;rDQa^x;2&( zF3Ja-p|@*DjZEPRB1EKF91K*5$U}$CZX73UorT^RH+6p` zBMsN+iBZYwM-7ytfkfu8D&Jugm+nV(l`G^$ z4<37b5}tqh6YS4DgsK0T2}N;;b8tFs$ZnN|dmfmC?!DSaDj+I0f`=cu5s&lfSh;K? zzWi(vzWnTaa83o?+N_xuzx~}sxai_Djd|PL=aI*5#@`-!9m|()5&O*fe3>BPLif($ zw&T(X=i|0NOcd8Nbd_(qrUoDL_>7~Tcf-*^caSE1{`xN)Np4C;9K zg-?;Yw+R1v;XTN*4XPT1%T3X~QwAP*@c+=heI|msA2!JWO;=G?eh8(dl?eKcGH%4w z59H=r;l`WC;6TA%e79gNKK*zuKKW?AFi1oD+7*hVL@y@YdPjh`5RNmt`?P}5UqRxD==O$3cHJcT;^i)Q1JVya{c6S^>-2%)Q~?r`WSo703-3>_ z!j8>;bnWdF1vjKRT7Kbw*=%+ZMIH<|5cCs~Hf+x9_1ieob&DYh!+dCU4M(s>#Jxba zF_9Ujvm?*Q;Z)_ya#deEmr_NsuclJfg4D!BQmfVKvXs=|epOc+1x{POdI$DTe1^4n z^>%cgDxu)I=g?~UpRs4s^Y!{!Z9}4D8o}I(v1&5dfBh6FvmU_eKR%5P)9xnCih}x~ z8=u9Y+4le!K7G>LZ!`ihj0#hnkiT&+^h=)M?O(r#%Q~TI`Dp))tGk#E?$t5$2q0?rgkQE(19}rq&J6z#GwyySG?4*C> z2{0UOc(_OhXLb;(LAG%bQFyd~?5I+%v&q7lK zGpm6{Zat5#Zn^m#ZE9;$3l>fnm`!mob^o1h7_dO z?<>U&!TrM}Vvs>Vd;l0tdTN|;R;lwivnWQ#L!m}^jiG|;-#t-Ya zqqwL@T<`1_Eirg#FAN;qHGJa*hw60M@Xx1i$C4#yV8ezTC@rak&sPhV%ZZjPTVU{z zp6K4AEi_?36H(c5!e!^8U%y^hFn=YAiVvWyybKamkd~H#-o3lx%uxf3=zSi9B-^0s z9G6~tHv0DMj)e=>Vqab!d_EtN5|ae!dGLX9C^}Sz!zJay`I45FjOp2(V;^-^bwK7$4UZ5NmDUJfui7G5x)CwIf6kABSsEH`}VC& zt=XS*Y%=F&b3Hxsd=KV+R*6sEJB$Z znN^GOA}8@C*`KDNS%>IS=mg%KlXBE6u>Y2Si}wS!yb$>;Z&Jf)po$JR;RuR9v}3-= z7ES}Gml&CoyMH$&I*6SD@((A201RW#jZca9-8V%~WalJ{{5u`tw;4m1&q27V2w#cN znbc&0R;4=f0JJJ&EVQW4zwzH_@y28vxbgWPJ+9_b!K#D2w8)O`oolBYE_7V;$&5`<%lr&aGE-&Y1FBoM|Ckb(T#a)8Rl zfXC@;WoIaBFLz^Me{=YBfQX$c@I6Rm70oNRtac6 z^!2vEq;^A0LNDe)CKM$#mAHE0%dk-@qQF={#qehB@Wh}ikw)vM_Z%yTNt%s5XK>^* zcAWO@@R5Uokw@Q#Qkx*qvYIF3n%|CTa2s8VEl^M`(i?&OVcDPEAh0WtWeQZXgUrDg4jKcA?j>VSUA`!M^BG|>E0(Q8f;SmYKl=a%4DN0e6FR17Vng}PC~iTqdv_k5e)=`2K^^HCX+mQ) zG)^A5DB>xES#v2fQ^L5333%&2Wmx)MEk>N>fGV6nQ36ot%X6S>cLxR!Ka8c{5Ug3` z#t+M@L2UsutvY&W5xpnO$FPcP*rGK4G6+y30#MUz&;qFl*4ALm6*gqGu$kAJH2CWa zlYa;I2lRPg1SDC)vXrE+KPfC4OF?oOB^ix%g+$Xj#pQ&V)SuKO9tI1i0ZtkLXrbvK z(GL;HmsA7Y8^Cr&M_w5KZve=VP|?|)zkuv3TTH60if?dJBE((Vd;E3)+|oKvhQoaBpHNJF}6j0F5%?_0S!%N zX6_}U44i1uq%o?2Fjv`b0VcaoBg=c@s`~cRhsKm9my3ssK zK~U$AB@z=mf)ftGx6m{ZR|U9%bysA0YW*}USU6^cxwd`23e3yjY*+x2xq4R_sLgI@ z!0~Wo9}y&bGit}QVb|i%%V%Q#q3y^G6dEiNU?{0G{YO_Py15-Uf4ZV;IunIh#67X*1q;6> z@Z+qB^rNWbacvSHV2lXcV#b?a;e~&HfS^X;@jB7ALklP}paz2|K2(aLLq5Fwzi+T~ z=_3_qv&WX%*KZfwwIrd@?R(;1X;#Vc#%zMyuj26gt67yVfo5d;AKjnpobcgWzRa$@94t9m zqB_$cQ8_N;Td`5xGPxNNQ>w6Lg$7@ZAF9TTpDU+==nH$>Mfkt?gGM4b808{FJR;&s z{IEG|k&!83!fz5VV31u5(ll19DKd(4GxgVol0!kf^J*13bO**<>V?iVbF!)X*!;AK zFTRo$vBbce5J-&nm^Is4u+Vr2lPYJVWaG`@H(_V#A!v39HULI_ZUCG)!6xTedQNaV8cT!q>goZ1E*+)0S768lqeyrf_JHKaIJX}PHW z6gT^gBSezKq)0_{qhd$DqYKCz_kqSa?ZNISL32G zByuE)4&&yFKo@Zhn4uMAjY`c^->dj zhK#5&2|3&IF>TsMu-TmG+A|%0ykjD|cWVz>k)f&@a(C~?Ytui*nl-zyKerUGPx}-P z{dJNkir6eY5m#L^4j;X@5PkY}$2sQ@6UQbJg|2I+1I-x6IU_R^{ zPjrCk1a*t+#uf-M96Zb@hQTQ`of+gAks?kXrW;IJ)CtrGH_0U7oK6YX{@#hd-Q~kO zFZprjV+lfcmh-4H#b}nm?Pi!XIT>RvQ*m&Am2d(9(F*3WZ2A zMXR=6WTq)G+VeazN5mBpVMdV1r=Jw*n^xImw}(AdU8Aqwk$1gR;j7l2_C^%xGjC11 z!$u!=hOR-8rA}|**kBQW7SL&>F=*FxT>)Q5nR|8`!^g`5G~nYCJ9b5R5!`Ms53m~Q zba+d=3Bf-W<$FJV`{fG#jt5c+fkqgY;ze$`q5F5;tqHj38Wq)*01I$1B7xOW`w-=( zIiQ_Uhi>v4B4YZblH0&?HRajjY&8Toz7mkXRBh=H^>upalz!0}p`fv-^N&M;|d2 z!1@Ebk<0QRN__CB1Vd;J(ZJyX#TD7zaRs_Z4Zo9Kn#6EW8UkIydrg#kVq6S zhgpS?YeH z#F(H4yII#rJx(F~I?lr*T?rcdt7?ir3kw#00#5o`D8>QXRMuT#r~k%HI}i*q*lY~9 z-gYGh^zRDJgF;R~ktKBN(E(5Veb#c1?--Eol#;!T#LuA5*`700Ji zEX8)>gB^*QaUz(%M?VLyx`pA*7i*B+t`t{Zp8{1gRM4X3(Y5xD z2U?^EF|F6f5Ogy`Y|ZjYZT5S*?DE)_B((kZq0*gaN}Q;yvYOz-Z!;UMKVdL}>rCCM znGJA~7A!OfivYBMwr^AH1>kyPJvNoC1{oU!$ZJ@brJnGy-})*8w@s+Q&}J@u-9c5d z!S;#Ut30>rd;aGSS{&A{ydhQAbTjQL>=-vaKB0*!$*UmAo7fnqDW*J)>5zrPh{c-; z#EAXYnFd*s@z(3bSTtY9|K06D*Pet5|4%c z3yaLglJzmd^WlY0>?6#~SEQZ~iOB(iQ5A~HGSpf>z;sYmLqPL`Np|6QBIc|@i8Xeg z8k{C<+G)}a3l>gy#8Uq!Tz8f?#S~xzRaI5cbPYB*5bzszma0=w=!OO=EfvMSA+9Jajx^`F2%alIq-O0xM9*oaC@9kRUI?l`3eUP6ry*ZP8dCU5N5nH z7aKQj7g2=?2_6g^(G%k@9wjQ4i?BZt@k|UsYZi?t3)vu&B%{2n2JgN1Dg3p5)cXCH zIB`5WbZjMd*}g3gU(Z>Nef##IysR7v9xu`|GB9$)0E`^n%Z#K8OK1G_`xCj##E@VZ zH_?gQodLW%wFXH^vs|^=x2UTd40!z_<%=} zZ4G^_=@8`K&Mh_it5X6pSDWR_&fvAr{QClR(zqI`bH+PnXJGEipL{9m!wirw^x{Q{ zqYS`ImShot7LE-T0cZiH?Zii|IxBH#^ZORr-^VNfkGP#Ksj?A@b=3x|@XqdA(JJ9N zW@jl<`uHjL7S5U6^|N<_V-ga|w6PPCWj!kAXS@+}fgn6Nh8B{c&k&9Uh!110Bk>+{ zFdmK5BY;eK0}BRXY7^;#{QG2P89shj!-bc*F>ZnzjA%x3YziDsJ6g8Rz$FvT#h}66<8&n~oPH4Zr?GDiiE5I7upbXC zv0&jB4oYZb0t(Fo9zv0pk_uEwSXs0i>#B;;&zn}ym<<1|H&A2>W}!S9m&kJrW+bJ^ z)dKjYXa|U-Ala3OWZ7Y)AQRdI%}@&#ELb>o;744m{{+ycM*xN;5;}Kj4@EKbR9|`N z1Gqgh`VZ(9kpZF<2x>LB=%V4c_@YtbGeetI7ZG`DR&U1}ugyX0wwd_NHDd$;sOt=K zX0ODCb-OTla0x#9cqJArScK%HRPmiG%UCdPr62?!{_AZ*(#UL06f_ie7BOrsMUhcj z>cd|ioFa;isA>?m{oy)fw=|;H-ktFkrcM12<)t-3>(t|NBH#~-%6DIWwj7sFJO_Wi zccNI!HkLMESPM{=34V8HB5G??y!KoL6vA=VMahPqEaO7gHnI;TM~(`QK|PmU-5)1u zDAF`&RNP>aXj_nfo2&JIKYmyW)}&Q12qly z-GYT9z#;%G2=7M-Duo1sUH~?T4;n6!d)#<)b_T8=e-MmIoRJ{gdCxs)c3$n3uqS;p zb6Uk)#AVy?G{W+xJQH~v98xpPUWRB)Bi9`${B{wvf&zj-K8&j(> z`g{eq+~E~s^xS+*Sj1$Eu04+B0x`3M|IVCTJaqq5?8_}fZO{iwLX&dnl1m8s1K6^4 zFBU9Vi}4qqiF@w55wcZZgt-zIwr(t;WE=bnxA}vP+hhi39&_fHH1~_z;80k&Mc0|m7VJ$#z z&W9pxif}MO#CH!&tufky1q&7;XlO)?E!}s*I%QGi zo+guxK`9l%1_K9n$EdRh;mgmLA`sAV*B#HHYxg!dV^}W?9?}aPI<^8LUID!33d^@2lH}x+;(JUreR<10mu#wkNoX-XxXYc^7kLYtdGA(Nl`Vv z{Co+{Id>?|IHQlTV>sHLa;V&hsxK<4#KRB%8|&9^hBv{ByY9XP7hhr!k-51=c8Yr!tiqR{t;EtrKj59WKSHm***JI1FhM>_CsqP!HBUY9I>2pZE&4MM5j{>Rne5&B@j_`xw21eg<&Ot5~5Pn$L+ zAlFo79d;xJE~5nNYJ89=wdp!n@%k6(Cv**t2FkG$9F;L$vtZ#zVG)29BH__0F$(E& zKxynY-ufa7H(Xi<9;Dnes)|yDS4ymQJZA}?zWZv$uqt1PHu1)0G9?_k4p8Z619lzp zonzXD)cMRfQJw2TY+X}1-kn;FPu>gQtg%k~{>~)W>=e2%?huJN4Bf$4y+UrJ z#l?#ChQ-yRHmE6+&Ur+0q<%`V5gN~R<9i}S_m7JH5;Gkk+8#A4CB~kg@rv)y(NdUW zj~DGfIie9QgowkU9>>_m$ws(ZSWJ*^>L+(_mlrbn&ESpGdUwuo(XQykD_`vv1qaAmvU zxS`bmLCzCer;J6Tjs+GhSg`O50BPhEO_N<78AT-?{_F42rgd}7owEk{2M%HB;*D6o zbQ6+OW}{=rHW)slA4ZPujkawwjZ({P7uV-;xV&q6ZH?cq_(SFa)&$ z6;8W?J$v$?sX$GQiu2DOfeXfsh_unSUr#*x&~qp%D#C$+gNE#zAoTrG7H)@V%MQ-jac}jey35Hfl(2wz1FG$V&T|e5r7tu?gI({ zWcpBD=)-+7!^TnoDK%h~jo8kcpC;p`3sq2fn7>-5&64HfGNpX1qc(NMteO7td-m;T zlm6HY8EH4>c!Wp**NG$gEExfy$RmP1w)OuYJR+;a29aoY_W(H#SZ_Qc(HJ_A{{ z;fv3{#bp!DhsWcLL=c9_#3N};ai)uq_i(2SQt(Jqp8368_j?{ktcBV<644gcDr~5Y zum+d$T{M!7^CJ^?OnB}j!^kG_ZAeEU2Y;^U8^WXVU=14m$Gt#}O;(^5i(tWm2@>-! z${7CM_d@i@XoHXUtU*EfVbtmxYN(DpS`8HpEtm_VJiKCd_a z@TY6w@z{(iyOAuW1`bUnQIMlcw=A4>&H!k-3eJhJ8T$0?gcdE+v19vQl$O-Q>}14c zIvoz|$|=G-?|hD3JNKhin{-UR`!^Upx<3M%23axepjItgh)Tc?yMm8Cnu~y1g^?rs zAuB5bNy#oe|I%Ne1~qYxobec&r14;ofPl**;kNs|$jAaddOLueZRPmwZ5|97;S|iH zX0Fj?($IWd_X5Ux^0~Pps3SGSU|dgAI&n`#?x9oDc7APM7&8T+dazZ7d zQ;}o+>KCVgW*KSLW(i@`$#2fWF9Q|lu0?Fz?V^7fVB@~aZHQRjLjiBiI#0ZckVgjoHFg*5PaIQ2I)a0>zo z#sIFVI==t18Z)N{Q5^t&f0qm6FH1HFLNf@Y4iVGfRCWyWTTyWZG}Q&097LZ!9mHqV zem@kMi^){i33~Qui*e(JV&=P_!&hm?zPkmM9)@{e?l^fu6+A(I_NTjEwAgHRaDGDkpYq4h4R^;s~ z0EdRu)MRw**cx5Cw-v1z9zY`0xCF09B;@4eV$+7*C@ZamVw2FWLre7Q+ePS@Fs6bM z#f;8mkSIk}RgKU*Ie4%PwZ0%yQWKG#or-~jI*Y|cm>50go6!gelLf4<9-t{8EUHtR z;$`%>RWwOFqiCfUTCflS5ouH$S2gc}iOqYUlrb@GCx#n==IkH6^&A{{@)bX#}dcF1F*0!9^uZ&)&Xmu*Ag!3umZV*w5qm*I(j-QJ+{JmNMM^AU%ps>sMlh9VO%E*Ulq z0c>_V+-@hB&JhemFBp{}Qoj_%g?HZg3>*Qm@9>em1^E}$0+3}zw4~`A9Xe*=hMUJ= z+Dl&}XX`;cz4d>1>-EpkzC$*8_wIsWBl@9N?+z#J@+ypN=c6CL_KBgO%AfZu;&%6TR8-se1_rea54aXyi?t8dU)8~Jtk$H0h zQjIS$kdR3J<>-0!-vV1_=sFKbvSiama)DfP(yl`swhHshz)1@>wD3!VMF3hrevP>AqqdJs2E8i$K7 z839UUq@|`t2sBCw_U_Kd(@(yM4eNKK(&rc6Kq^V_COFZ%PkRya-?nWgR84~-%lPiw zHTcKh-h{&?Vd|?7p--PKW-6^w;cMxVRrt$&FT&xL@$$6CFra@gam}82>TN8Tw;p50 zj>L$O1M%XEuOsLu2n79To}GoW&mJtuzeV4##f$%*i354%@cC**+MPrRQj%R5b#@=z zao2V5Cb*3XorFSD30`>NZOr-NTa*;nK+~v5v2%OuXy2|S{_js$VZ^9jU|a*?k|6)) ze7OW~zxf$<kxEFQ1a3$|0GCl? z)K4IWi(g_uh9o0d3X_N&8aJb-Bb`C~@vSUi3l=O`Xb_~%`%gwpG*?r~2G&#^rKP3t z`vXW!OhiJWTR83sk&u||#E@Y<#jD`JVLblV>DaL~4~rMB!J>t$arU`G&7)Ant4||R z=uo(&W7ebL&@du`Ai{-m>#UVUaD)a;4Tw3;CMRI_XNxgla2JdoH2^_f6PZkeLXdzr z-gGJY^y!Xo<}SmQ&AV_QzXU6mZNu^P0s!!6~ z`uc1c*NxYq$|ZbLUK^qSQc&e{vXX5}DkaCgTh|5txNW09v0I-a3_DlR`wdEfH$kqK z7s+@uwUn5HHVKdACP`>2=<&)jjjdl@BYiuk3Y*t!XqIWmq&rl7o9q_gw5=v+i62W%cSV} znd!f|dk^BzxBndl`IT_G5-|F#UTD!GOO*WIylFdDty+W0x98zMFFlUzmd(VzvTTRj zl?1zkK&H_&=J07qk{up*5^PQiNirrYQGHo*p;>A+^7AV3(hDD;wi-xI@uH$q6%jI_ zZI&!qhrc{91-cemv?Srov-+Z0vs95@uzky3%=&ndNJV(;?~}oyi^a|JPtU~6ne*Uq zDndNCbLTdwsr6(1nqAnLQ-X)?`!Ak*=??T8&_Q%${o0*)?2%VMVMj)0I?fo;2MGzG zYnX@itG8p_+Fkhjqp#q(m+pbvZEJMBk)z&MG%P&LPsAh5V_e!?ve*O*7LJ0@!dy4y z2E-y=RC6Mr5)mek@aJ;@58EOHAR#=eooyaJoO#0I7N!j^9(jxix5#!P5;V0MZ9Aso`Wvpq z3(tOpk|GUHJ@GcOTQo<HGYRV7f1(37dN7gM1l07>W$?tQ(F3StjGNk9Weow#bI4rcaDlKSJ z#AKGhxTIp*tW!Y##dNvciM}#NndrFd)HH3O>BAxbEkI@lFC=l2ATi97ff)dhCocMX)Z=L$ebC^_7N-K4Shni>Erj=Lm|J$7K|aP99CG3^&=FwFC?C(ch(04Ee%cg@5I@226Ok&6BO$?szdtz{|N6(P*t%seHg2fEh97e9 z=JeU<*1aPJ4D5!X!+N4i*H&UbP4_{f4xv97+W$W;GX-h!I<--!nKRND{zw6*}_+M6SbDnFcYDpffudqh_kJd9FMFLW8KRDuKf( z<4=FS3H|!FMU}4_&6}sArY3;@yzoAPeg&y%GM;$ykLcUCi`X|9)bZkTZ{x%F=3~wm zYcXc*W(*tI1K)qQ0iVp850}e!V47d#^pH9wcf>ZF zg9idoxnh=oihhTN-9r{ESg>H>C}`9wz_GF`Cwi$YL(()DmV-MI3qv2eK+bncRk&RtsJf^lcy zzI&d*%4K`8b#nps=NF=NtIU(81sH!0fX)qDt#w-`es`x6<0jT(&0>O;i)%3J{aU>L zrV~kNHOOe`LsqsOSHl7ODDhW8J~0%@Ak1A4&AP+a*zl3NuB4 zb*ACoyRXBTaf9Q&Ub=V__U|nf1m)!u#|rW-GmzRsfhgB3DIs}d$ z_+s`VvB1b^nU1^ez7dK&dc1b+GI8_oC*Z!jUc~n8yRl{KE};o%cuQ3%b~~0WTaT8l zQ<0S97W>?I^F`>>uZyUH*1C1NxK@!|kW;W43@u)DT4y~);$J)*dioPx-4=gn!NSo& zOeYKH9Eq|6H?|mrWd+H&qGfOK08vh&ikjwj>53b-wH}Vug}X2|v$L_CIVy&u`$nX< z!-UMfXyI*!PTnNc4KF5jD}UEv1e=0EYQcg93ylS8mdoSBTpQH|iP21MjP;GlAQIk0 z7E7V?=L{4Q&Ko-#OP6dHm1G}(>^0nV&u`JAXFJ&JhA|cls@T3g2hTk_6T0R^$Rz98 zyKCf7+^9wo@41RLQ@`OK>lm1Do-49lL?4bC-5=N8G!Cy%`xtrq4&w30Ucr-7?to1p zI9O1M|GqE>ZjS?L%`!3mlEINSY<39_Ck5kv$V#%%B|oMvoFHQ5I6^bIZm!8&WINH~ zDq#GT3i5Lqwr^3fW2=VTeFW=PG5D)h1Zz1|RRbdolE(q2a4_&%ok&$Ro|0nLrEE_y ztu*IwQhveiTPMBr+i^UATlZ16p*3-)@6}aqcjM}2yVWx8mQHI*S7EKyAEDSi4-Iyg`~vk z!ds<>@K%qZMJ<9=S^az!&JiUAv`c=zzon`NGbyD`aPDH9b4pnarNJ6X?1hwj@+imF z#1!9>wDg1Po_MLlAzU^8U}eHUFJl}k(;&-ITO%db+)iUsVobC*^349? zRa#nw4eNJe)yl0{yJjzFG&r--q41a|Hp&c^yV7auzVpxBh1^uR&-+Y}+mq=Z+aH`Yk9y zrbCfr?8w<8_K;;81`qBpyqbhiNK$Cx)sT|n!M|U6%sets<5!WJyC0H5L1hVVzx9>y zU{Xp%3OAtyB_(C>cs;18trAgv{rYyn*l}lI_UH3ZbSQu~r_IK9^H-yDkJjkbw41LyI>lY%Q*Cy~IZ_)6LM8F<7A#meIS5NVJltn2v_4}Y{0q7vpCBA1k?eW`1;F3? zU4YQE7xqJt$Q)q2cXW91=6SaYqtb?aF7JdZ?tEbt`^-ho)Sr7tY z)=x5=q0AhM*{>}JSVXBwLYCtE^UuKYr5o_&=gYxV8}6C>BHFiaiNqwQ*ru$k47+pl zpaq@qRRu8S!a*23q#IO~K?$v+Aq!a_iPAV9luW76>Wk`O$H%URjvfEKId_TzgH%{5 zq3;5!ikoh_7`c0mblz3VcVOzvAK>;ouEy{YeT4IU+tyu}`pUc5uyG5rv$LQEIX14} zg)Lk5AsEomzGE7)vYH7(@l@z@*VP6z2v;NBIdtBw+J#;U&Kn>4NqSIOMo?Wv5U33z zz=FtE{p`WTAJby0uP2i8%^FvkR^=_NsdXOIKy_lPa=LrJ_*e=d!J-36pRzRXM5Z22 zhwLfG8(;q{)e;U_YSjkpa4`|>B>iv7%ZZxiRG2Q= zbspeOM{UsVsti8x@WmD5M{p()KU{F4gi}m?ZnpcjfJ~JDgkeYtKPC1V=LC`vl{fy@ zbY09)DJfozKD(cIRg_m_;lfpzKJ61!RxrHs(!1!{vnBcu>Mj-oOy`glf`kOS5lU~8 zfG`Qm^z>x0FsP{o$}1|&mWeSNaNY12)&XOrPEtk`s;VI+)nlX*n@Motf+XeTWzaMY zE~gtFuiYT>7&oJA!qt`yJrhZmP+3`pn%Y`895$2`9maFdz9F;(sj0uk2osf>-L51Y zE-ONQ{(jMi)@?KK>MZVh8h|Ic*KWuuvCa0#IlMQ9zaI1g4#gD7_}^T%i=W|ACv4=uO1jua?7CRf#pLOGKqw zp=l>`*c=o|DK?B7KOBF&eIjH4!u`Zm7uJ=5Y^RHaqWd> zxRA-<^Od5!ycoWk%E)(`7DRbP3Dh8grW(;8A_fi&ci(dh_U`&8wr$PBw5c;i!M;h8 zF2i3Rn}okT`U-Yz-;J3wz7#D;)DF&MI2?BL?cWJ^-E&RE$$zTQknBfjq0WR26#kS7 zLNX~C;$@W0wZRF>i?(h-zCBRIob(VhITf7SrCO&$)Jf)c>2Oj|fBQ?jo_W=S}ha!Xf}IpxYtf8vxBK7^|Q-KpC(UfXja>hwdoe z{@Mr|zv{diyfY_(-}T!(t*AU3qCg|niNh%ohZFqqdwS~d>7|tFM!G21p-3E5A^771 z=}1cAk{0Boq(u(WD^m4V9>36W4_Bn3;=f~4bD}3e!RV@ve?Ijl_U|h}k6zjM!=J7a zUMZb(VW@_t+r%U{F1}>MprXs`V{1?$HrCi_z1to zd|)g$o&^iX2a)Cr!@+apjZDW$e9galo1lSc@FVJmAE_M+7A#o!6+kfs!~GOcW0hsd zNm|T=*R-_hWLZ!%3O!_qxZO6~ecv^>eBxQyvS}ys_a8!ed6gjR5)wRU-L5&h_2`J! ztuw^FA=^bVwE-`mcn$^+?GA_C2Cv5moRAcPJMOs-m6g@V$Y^Ft1Vv{oi2`@udksnt zUx3W!Y2q`+fUaHJ;hAUe7B#d&?S*Uun4-=8*Dv6>U zgmVo#=bSNy;GPBG@^^Ao#@!_M#wmQ_B_D}{M6)*Q6%{@yP;3(R?k>Q}CArwK zEf-f^eKAr~J*IA+K_vxMO{Dbp>D?K2y954!hH786*uQ0V3n+?$Kv2WZoc$Owv1-w^F`f{ zhL>k(`oVZ3m;W!x}RMcAo)}rTiXwNLaW7jMMYS~f!#1TXxvV_GP$B)9cNjqjNJ-kjK8&k zbP&uk*4clfJ3!{~PYk?sJG};Kv5^Tuu>y}lSN~r|;qU0671*C2kV-3Ahv>)x;${mG zdNI`(&95Ky*-r;>uU?iU$k!UKU^q*JpZ~s>g5z61#def08 z<`MT{x~S=irGA;JVMPnytq`HVkKjfNWfA)Rs@vqs#2yiF8112=Qszs1kFxXzy$@=9 z(UYBE?A00!jxZ00%KN{q3ZG50j1)xJyR2U#v%BjU7ybAl{>g$^P$#y@p4a5&z;K2@N;ZZXvhE85 zGdnH)*KpHv+<;iFH_6jWjLW--`c$eR6qb56d+*XtynPn1^C#YC1%;4`#Es1iM4}J%!|NyJYR8u3 z_#@yO@DnJvVDog_`3nWXqWxZ$Tqe+Ka-@)7QvVJ!p@?a(e8Nnrd{jy-5BGfhLDT7D zV`*(O=O-Q;9krJK>`d)Hxz8oSA!fzUc&SjOhyQe1@Y{@`ID0~C;4fu@MG3bI0X%0v z-bfvi0$Ew9?BBW*vcmkkx=(PwMo+CI=`DU*y$$V&(tX`LNgZ-RHt}%vnXuzQ&F;4i z^u>}&1mApP$u~n8`LBdBSRhJUp&&!mh8@$UHaQ3zY%x_;8BepgZcim_Ks_ZswN>^D_mOS&ciz&CN`@OibUM2nBO5Z9iPYf`*zpd?W*Br^|^-= zf%$xgZODutox7PNjL(NHzYuZx0HmiIwUER&HAHG|b;+ZAb8~hAMVHt<0RHEFqvnYV zz`qhAM{XDvr|ms>=GE6aA% zdb|J^S)EF!kiMbpJ93Q8KO3RQ2YMEn+_=v?GXL%mCGcWo} z?0NUC-!DZM7Pb4hS*Z2C&2WcM#jmNr%QlbD-@D}cKJV(>ZgC14Mhu~3%}B*y=`?a!@{ry0UdSfYBzY&rz3$+M zwn-@P5At<@o&n)CFsM^nZVSI|lYf2z%62#h`+7tuFv>b7Cx#E7Sz^B#XYM%mOALN| zIEURWBC+=lFw1?+w2j|;ENmvO?fQrPRG*Qb$^e&24>*Nssi2A_1PNQtZ0?r}|9<7P z$$=@Ch8gBa92t8=^tf0M^2w>W_n+uC8>ON}p1q?Fe*5F^E=Z7FVY4}#rDpI;OA%S4 zBx-xLR1ZV7rcLSHhx}~fU^nuM1!#G2`3Yj+pN7-xqe3?Gt&N9T4}B`dd2y)$jkBf) z3;&CLNM}sBvV+PpH-RMNXZ`jPg1RstHea|?18VV<;uvf)1_Ke4`@YJZhc!$Ikk;Ag zkiZf|7AyFl`ZYN3P|RQTyE&21W=T?hl^(Fks}}vzb)WGihE))k3S_4#`R(n`+l|Q_ zKHaC}5eE%>tc{5b$ppAV{|rY_huvn)&#nXRI%>bZEn~n*KpsH;3xZKnn(q2HSjfH> z#M&$5`OvMBQ~@vXX==A(c_0$t#RX?xn>|hozV{C>O~$>U&tX{+P|IL_kAhxHP+<1& z#F`W5tA2Piwt$^|X3pjH2Ev%$jGY*kydFk=e1w0+#ah(-WZ-^ndy?`IG-}25QI{P@ zntBfN&m@`n^0>S$QpiBUih9?#pN)+6X)&^tj)c3(rbY&MhPuVdV*56CAE_Zc4Ax=Z zyZC|D^7===EudVn+h8xlUi9!~VvRz*9vgpfsdBeKL0O|_X^Gd8IJSfE=MIRat1?T_ znlXNKrH;yZzolA(<7G`x-A;dT@p_|Mn$1 zdQqbRk>&f-5tlI{2@*^7crt$f-N%=`??ysF*_0 zEg0#a3=l|{A}q$5`Uq0w=r%-A;25xk>#zufwwXSV^JHOkuuhSHoxZnCHo~mi1&qyh zZ5r8-*zidI5ND-Zo=xxiA?77=@_!#rnZv+^!1g_@KtCHzb5;kaR(*=2%U9HyGaL1Ys*dP+Az8me@>+Fj^Dw#m%y&4=xO3k6TZ7be<8eCWD6U<+u*Rv zD30IcdRu)lpZRyq?sBf&HxpGU4<*HOIdi@0>U2)HdnfJTKe53^1E0WR9jr1BBbfFz zo$xH0iD%<}_V%>LOt8CG1Mh3K;a!ckZ9iO6QE-(0V>u3!)_vP?CBf^GA8vKBwCQ+= zJ)b`?Rlvv{Mi!l_t%-&n`4tg~%>C3oqlv7nEje?{db7dniMG7vdT~mvZVV#_Aag=t zA$ElL(hw|DXO?nkp<5!GNfl6CgWA$?VqS`w&zxayT_ibDWEA$>&h+2Xf($wM4^Mux z9R_HX#|8y$)prpo?O%ur1sMlSJtK2imv}w!M3jWeV5FkN zvO~X^?Wj9-D~0fgDeI~j_zUUl#bN0Aptw$WojzfsiOx|Rq#$i&EQ<6wAwfNJH4T4* zsd?ZVXumf6#~!sFiO={y%0tP=63+g9lkCSy^+oKcb*Ok84t?>Kj=NmXqoBy zArjs|U6e2K#_~u>{{Zwt55>yG>fq0SAMQ%ZQ{i&LmO}ahEl+-$@$?OvJ%)-o{mbU; zb#PVx*Y4x*fv_g5PGK)T%UD;~;X+5Z*3kDqjwcD>H*ZRWuFIBq5;MXYqCRUqogQn7N5L`xfbJP4H?31zWwUBL09M@Sou{RyI-hTewIx=t*ntgybQyAh&R>Pub&e=EHTI z4PR59XQV0NDHN%fXVXA>_y>U0$41-Y;G=W;^)A1rOLed!8b-nPm7`PF=&pg*ML@1a zs|wVIrrw`)!ABqqDC)e9LikA-cj%)zLb)TUvpT)msm&J2i?a^6T}s_(zX z+dRyIc=Sm}c1iN1jbg$pxkcSQyngEY*~DRB#Ob|YU#IsR#TueW>3s$~WU2#=#csYp&T!Pf)r8X94Pny#%?BwxgFo=JF>MsQ5wLzV{y7E#p=VI87 z#vm)m8UjxLbUPd`|10DRord6+dUr__gf}y!I)bbb4Xx&^0yIL!O})`nUY6B}YNJ!Kfl#i9PbuLW7V7SWS5Ik*J z;jmaf9=9ldek@ohxAm{LL8`2*FR+7Q+rbO`ltfBQLX<F>gJGNRAY&0BBVmma_D48#rSA{^Rc?vt(`AzaW8tezKNr_0rA@|J5D{2xa-g^VJ`u* zZ&W|Ch$J4Ht+$zFXl6zQiMrGEhne#z)*yR#9@5*(C7wI|9Z)BIEYFB28^Qq&c|7tX zs*tg>+{0@@jcGG83$Un@6K#DD_O%HqkhjBGME>vhiXCydvnIKGx-BBWMnKPOSqUgq zWC4<;!y@EQSG{c~nif$*iU8 zB^L%y{>!nZ1*8fN_L8EdZ~ePo2rlmfo`6) zCO31c@_A|$WqrEvm&tWa!<;!#@9Pl8OZ!-v+w&z-XLbzj)oHlBm(#0l5YuK%oVZlQ zrFy$$TNnpwLLgmm;~6b`O-c$+Jz6~@%LH*Vh1WkQq%S6Q4oV=a0-Or2CX76EGAvGE zm@R$~m_t-2a;<&_=@Rzue3)~qi{NYfn_vx$$GCuMv3Vcj4;VxL9FZuYFNj3!g}R-4 zG(Qo)r>sz2Al~Sc=s%mv*0BqtSR1z%3bcHtm`2Dz%buRXn;EN8> zEBlqyy@#xpct5`kTGuNhPFoh6#PaO?SZxSP^Yl#cs@bw9lI8Gah#PJrsb(Ey0Q*rn z?1(;XK2HEpV+Lxi=%D2Gw)6*`aSE%1z3P%fp4v14sQ1=d-GFfpTHHC49%+1T3+@tD zzj6sLp7ic*TWe6K=Y}RK#luSkcd7YWG+=z0v|SH<>#~Il)%i5Phr3jCsHQL5@8}h8 zLc2H_uzO4z4*D}4L&B}2+@r0G`s7fb1p+=S(t-RvAaB7=ey~*(4N0d4`Sw-2w(LUH zO(KgkDfSMZP<@tPzJVI!Jv6~xY~XVW>`Wg(CYJ-d$G9YWs~XDCfnLAwc(PjXPXf4& zDtzz8>I%X^TIQ!ZRb6e%OR`=+fcR{K_xNCS92ZjdZ-WE4BuCL5WXhe+E)GH}7VDEs zE%RaV7)nNXh&9()+v%*SwDt~Cl+)U=ECdfXG;J)fJ)nb_F}fn2I-y?9FmDN4pwSHx z6iML6?V4(rW_U}mCsRo~7(#a?wR52oj_`Me0miEka$2V5Rd)m-#~M-H6o{%RQ{A*VyJI;1tP!@} zW9@FO?byk5ME9+-(b zj@)+sMc6eV#*b2pyqC9;q`SK;@gUlc(m%H{_Q8VXqQeAYjzq)L=nwa#6R zd)rVsKnwV3V19gfn_Hkwt@tgTlbyXW6UI)e?_AMPx%-qNyF!K=3+yb^RWmMH z9#3VzyO-jN=~ObAAAMeNnry(l)8?_+z1KGi>-J#2I0YYW*AeVInmvb!N$<}=5wORq zhsP>LvKAfw_TD3$%>XKuFbk63Yf@8<)W=MP+@lxvi5=-+jnW&O$t-~T(#~sh1oz|m zz!Z0Fr-zZqZ3n>JfBUWXg~fu8d&R4UrM?#2oVY(Sw+!N(Teb4yPKW1?VzrUSsX%~k z9Q^W*S1JCk`cZ1m0X@tp%*oJ+r`JmdRujFR2qcY!N(c@4cCSOyQ$X(&chR%D6vXNY zf&K<~aG=&lfz@7x{x0kF04_dL84sY{?JTN@*$GnlD!`uVnMhN%8Tpj#by$KxHKR@ekvC~D#EbCM+yLhSDT_YfIzVyq z_hv+W2>}MfwI)ewaMqU6I&&eA!d(?rJ6KD#$X7KB>->bZQctr)Qa*FTX{7*ch9tlWdk!D>AbpDGoDmZZdN>GiDW zKL7 z7BggY)s?lx2ajW=4k^aj$7!y)Ve4Dolt3pb?IdcG)F*v2?1Ok)IwJ(RRQVj%in$9fSIKm1bMI?kSlN~J(83gJ!!_ifaC1#UX>L@>0WjDg~zD=8lkYRO4@*X=@zp|B0Z zg`trA-EpbA5j*)|k%+-zGe`d2$DEG4e?qjhMLRiw<9>4JBMO7nC{l8q-#6yH(Y<;n zHdit#V}~VN-kf@KJ0;+a=V6*$bqeBmM7HrQ-29dj_63IjD3yDQ(8j^CmutbBR6hcMg1dZ;4U2q31RW4&?)P1RPT$6n z*zjQ35+Xy5C62vEcczsq=9$uhWBj#uXU$(#L~%Dnr{-2Qqy`9BG0nxHut3OhHvyz| zmAydnUvV!tB+y@~#&cN~QaIl{Acq3fMD1Es`o~Y?sIU3Zl>>?7mLvF!UKSTjYMeT> zVWzhAGA%35CR(gciG4~y!rpJp1Iwe{`xl9YP+1;KW8g#R^dM)rS@@!w#qkYSOI~V# zzl@-O-^Ii!8e#!GL}k>z=&QN~zTYg+Z;`OV`nt*QDAaC|3C)a+h4JO>ZXDwYc*IGw z?b7#{)(GiXZ+xjj-;83=x36VnwO}-<_h4p%9}!fTUy8F)I)WxFO9gTS!>1>~c2LiW zJx;$6ix?~$TSBNWg+qO<%H&Q2%!z}13;KGdT*G8IW(6F1!*F2c*x}artU>w{v#65R zc=d|)OBk?b@0E3Bg?es-@c1Nu`@ zf9`!Zw6*vsVPm1%JO@-X(<%DaultHx%AI$h+GmtS!%Ul-`O&8 zcS498(^lfAiwYv}L|~T{iQ-wyto#f&<&bJP#%|gfm zP>qv5NGacl)`(V64`KVS9qt*PVtWK^U<=axjfHI(fOzN;>$j%@hhGUq1mxU@Dzhp# z>h<_zyTKTN(cQ&Tt z+sWMIwj=4zd)NX)&IF~G^T3kiX9rJ<;XtsGkLOG>Lg_TB0Cf(HcU)G-_1dV7)GeNe znW_-wf2185>o{7dwA2q)NBc!?VGTDnvwJqYr+$TAlGXq&*kjFv&&Sz((>x9pDGmux zX9=@~t*Ro{+3GkwXF*Qqi46adbi@~)31?xGKKdWfNX`gntmp&+V{Q)RUboDxtIS9$ zwqR(Y42xG#Fv5R?kt9EF4qbvO%CA_lB7|%=!aC@Q-^xCO?Dw07$w?y%C;w>HLJ}i{ zDn|qBB35%7^1ovk%p1;znShju&B6-9=HVkGDlGk^*P$p)b>jHj0#$#49XACiuup_e z6lXr|wkD_4<%VZzTpOQdo)PU`dxmTAt8A^#A<%FigJB-c&G-)$3nhzldqVq>5n zgo>_~gji-jN3a3X&W@4|Qu<0Z?6DG5u>-ixBX1}L^J^MhI0Ob$xu%0+@NBXyxbds; z`3s13`u5leV73X7Q5@!`fNTWusNliJ)i6_bD+^1MVb1+QI*HGu&M0p^%Sm(lLF=z# zi7&k&_@qx?;%=w%NJ1o;ao+e&wH&w@)=(~ant$f89Og^!w~1Qrn+SuYOH$7=3KyQp z|Dd%~=LB=Op=QzX3WvHA%~>G%3~Y?L*c)zH^Gk9IdLe~!FqB(i3Z;jKXrrtfQH|%k z&1T)X39}9>sA*V4>oLnV0-a8dcs^N1Zbt%;+{at|?0TpMv`aC+?s9GDtc6*=ov3|f#X;~Z)XImuUVqf`~S{j`Z1@0*B)p-4GSh*5iq#LaTE5*Jm> z!o>d~OC5?=dE><7NgfbW@>QML3X+RK^crJxn+`|LQn+Y-v49$ZVTbNy3^PgbA|Dk0 z=Qi7iIGBy>T?~84)ys-66Qaaqm#sC)O&>6}&0U@{iXfAb)4W zh5TEnzUD;#XWLyFs2;3yTU4pZ%=8=_tbnN7VyvqiSp3t`4WRW9KxAP*eypF%!R1ocRIz8>6olFx@-slgw0OP5utAbEb znr%F}U6--AhR(9R+^KeXz@)mvKR^RrgiY#hM&!R2>L2dJm(z37jtxat-mTMzmMpQ( z_F0G;+v1qydbS!*zNEUJ;_fbbYb=NIfqS(rfKm!@rkXq-xW(?2c47m+pdNj+3bne! z*o^ErOb&;G9aloAmgWwDOH=-%+#XHSjtr4Cf{dG-IVg2l_ubo2EzA1|nc48M#ND@R_F_m2;5<03WTOLymgBc$+D?%*tGBq@oNt!i}|O zYt#p8nfIAF5FSnYlMR=>uRrwLK&d2SYEg^+C6~*F2ZO07Hn=FoDbD}Lb_44naZs|& zR84gxLCA=mOK^F-NUOXvdt*^oiZmC!A3-bKq9%(O=r*mYXw@pL4 z<(2}=XLhThP-b?|&w1aitr{BJ-m4-FJ{t)6SoLlhYazq+LSHDF%uonH&hEb(?E3{o zrND=m$7e1pa+RE@w)1z^?_@vzrTp&6$Lmi*QsyNPq2Io^&eW-sjFX=kQTR=1=S(yQ z`ApQEB_%9^{0oJUl3(N(F9s$DobzVC$q#c?z{`3fuJ|ZJBG*vLTAGVaIw_bq(-1f> z=3u4gnLpI`K;9upP&})tlY?2iyzcjST52~Yj{NfuysIT&OLmj&((iRIu-NMOXF^;L znE*ILIIU!Qn4SE{JsV?mTY%qnB*W%=x06DodKHgOh`?_?(+V!aNGBvI+YA~^sfV7G zA7-~|fvfbD@9XWQ2wss|PO)~@0Or1ehcExQdhU+-f<>_9ynS~=-ZkoV3iEdj0L zWOI=IJok7Md~bRm3@t*=nK>1?#MxMD*!wX3lx_Tu#)}hSi9XE#%y}=*Ej(P!EX@mf zZ~B)VB4OfqIFAHrHq46!Y~AAKL4inJ+;1kt#~P=1n0PZaLObr#(6sHD zJ8A!!`hu)S1Gw+Q6pASLO2Vo_omK_%AO~Y4xKKH=w~tIk>=ydc>>BXBIh|;0g~pe# zEL)>n-64c}4JlD?KjtEr+u@b4oiCOdpy+00ATH#;WYQXJ@K7Ji#ckvghAl}OLsESM z^o*DVxe8-}5MmIX$@Za$gOnEIP&$Q-l5A`v=Xb6I+jKCttm2*|BKw8nOG>w{K=Rvv z#6<;;5Is191&bkDMLcoRY0SX3fv-*9*KBv5<@+dR9oY#J`oG+U0Qg{uDmIX^2BG@< zG|xWQoIC&A_&p}Y`S%0+UqcpWLBgnNiHAfu7{~I2;N!nlexxB%l<@MrMZYkxqAgA_Yb`cy_aCqgCOK1o#LX|H{wE4Yatk*q7dk}o1PwyN2q#6hmL<-^ za_}@lL~6<*v^4fgOhnoD)29e}oDx8u1(M*M$7Ykquh0LSqW>IN7Z&A6jxuZ4 zBfJd@wghEh9CI&ezr%SBM+8W0R$V6$*b9@kVKDcd)gb-{4S9t}?b5A;i%9H%-EfM$ zuGIfhu+IGZogZeXI56^LKmz$ z`)?jwQ%1n7_K7E<$NOMLN=C%`5R6h~q02hv$`kIbYGw1dF5;hPadb!mu(8fSo(M2< zXjWO2WA6sTZ_jnL^I6HWR?AlXURA{LNVyAUS+6aN?EfDddlMPyu#6HDR)#mdN}uoeBva|a{WKn z84PT-X|SvL-vEOVxDQa&Q5Sh@s?bvPx7G+3@%gJw9~Ec?MCNJ##R>&x&ZNGa+ZdT2 zqGN|vTjS>KJ!3wHDlWqdpxgdlC8L8SuZ!8|ebInouIgx^3HdLAkQ(omx<#aQ)1Ho^ zy=Gy}2L^*y5^c!ZDtqQ^ueCzzU{EQdE==0a$Hv*S8)m6YlJ z{j6D{n!i6KVLC0viP~BYsy+|xenw)w;yxkU$ND^75$Ok0lQ!nw3B&4zj^5yiMA{ui zB2EN7)K3gYuq?p@-gAyjHYiYFfHU-f5%1V-o?~rZTNvE54V6FM)rH_^%nsf;1;&95 z{@^!;o(q1-*29#eZ!|Nu0UM;T@_yo!jGpY0|BJlPU{Ung3jrYZ;mU{wq8}7vTpPvE zLbE73TaOu<2WRXac?#W^^v>eogSfobMm!(PqmmUlj73$tRIYU_dKPA8m zG+MuW>@`iSRe+S~LB)b%GDo9OfWOwXA2E4(hC#JthSb3Ie^splS1pnb!P0 z!9X){Qs=|Vi}E%G0rca3`(@YagqcM`06_PwxwEZRHZb;gEt zYbDsNM4x#bDtyIqm)>p1c9jEuTdY(^&{3V~POV?{l zWpO4#fjtlv1$~yuhe;#sqY2B~cY&^fmRTJ^&Gs!(Q)1|@Cx=rjwFkYAeKNH!nFuDd@ zCwpyC4LD-Jbr`YSo!J*u+N8S z;5vDMFniS2K5B6rmrRSw+_!cj%~k#5JsEfGi)XP9Jl3gYncIISdQ)Ivjnc3jwqPXO z|0Spd!rj#{*~z0QRuZm+nNZ^hqqY(=%T7@}BiE3mfV!^eLg7%${LMtR;ceh_yrV_l zW1N3BQLv9#DlhaP8&kSXb$}l&Yg|b57~4;X<2J84y|+}W1eCGMpv9*9 z>-V~qN>_>HTg71asMP;TQ3}EiP=be1HY3adI>1jHi3sG-7h9NiFuF=Rj+0!8s9))j zvVk1+wYyI8#PqXH&dY@wkEDGJ8^`pDP!n-OePu3~VQ>`CNzmeFs&*)c8%WvT8 zSzoyH;YnlaRWZqF4uoJO5VV7 z)%r5dMSQgC)Gyl$_(<1c2tFg;svejNZ5kv#H>BU$Xyb|`7&s7*ewB^|2XD#> z4Ah7PLcpGfPu#}CuHKG-Jhg?Z^93 zj~s)v#wq#v*VA z1Fe>NoBVQuV@59ZU5)=80c3Jnd%js53q%H-BgLXIhEK7S?zcIG|DSOT4{-Z&fzDIg z%IaJL5QZ`dK^uIntsZLk}Z)wu%)(J`gGCah$V$E*j*Lj!uiJRJ> z#;xyLnTMh_ACTfWn3Z_gwpKIzJ&1B$S`Kh~%L#6Q@| zK_m%4=lH;^n}hEQDZ`FAR?>zp=y*KD)OhB4$%`OJ8!d-7&s)Sufq@)I(WX`+qBy9X z)4<})Qi{mL^mqUHsz_L@H55+wjDydU1Y!ZT=Xzc1ymPvBhe@_^zeo_ZWhMmxI+kOx zhX>Bgd#~tyFm1b)48772ku=667H_1iV={c`yWKR!&xJUm2k=j|cCf?99t(Nh50;RJ zwIx#rS0d8wH&MKK3o#q`tu2qzo8@?U1%jCB*y3n?F3EvI4hM?Z{~ zsWaiHT9ye~#)c8=MN>!^0d+4;%L)t}HptWy0Tctte_@G@DFYl=^9qvc^<>cM(%90KmYfX*#DOF&aQf#zNGd%Vg zjWDpNA&(rTrE$ysI#)ShI4*n0J^XId!|SjCUd;VGSge$33Ic!=2%?{NdD80+agGEd zX;|bBp2P)H_6Xhtg?BvRrP94172ThQvIJ^OpGnnScKTk=PP4G%+*V=lw{5pH|&h!lmbS%tK2$LK~`w(%ygyU9m*;E`~q0 zcuKh^OYLpqiXXBjDwa3|Dz9iCntphz^u#%$qoHn1ZnaI=ul3&lDBSG8abb9aXQd=U zoD|BT>6(%<3`^DnEgv$yq%w5pM}oi#$2{D-`RY^0{llr9ZOk`;&L9kT1dOWh@~}-h zvLS#nyRZT?Fu}-_Mv6>{6KnF^$k4h|kmT}*flKpD@?z*Ie3bAFkL{UYpDte3!J+dr zG6HtavDQaxkaq!3#HIg>CuAWSOdWyqdQEp^82YM;lJWUjpR7BWVc&SZnYU8Eht%)9 zBhv0v67eW&|I$n3i_qKO*774wrlsAUn#|uIAc7vBL~^7k5w;H(Gyh~gd3%C0*>G>D~RA2;{5RP{1ymLFy0=O+C7J3;#QJ$KX&24HDkrJIX zLgv*j^KlAN`f$O;j4l16cV-n{&rbPZ3-)xlK+lbK|6_}({l$39z{qC0|C>aZ$P&&4 z#8}>se&M-jdogD8%(wiAIL@Ke z{c?|G5bzuQYSQU1G0SAt)Yq83goLaO+94rir-101Tbqud@t*}PfIt!wuz>EkOK+D>Uc%8R)33a5frtCb7sSTj ze~&MV>rou(5i_L)wKsH~TLcXSt(!eRmm47#Oy?IXoSisZr#F@X9vsX1${i)mE$^<2wLru0IBhNvr*$ zKV(kRO6^a6-wD#h(6+i6PHM<}3|~$aO1tS&ymUsaQ!^0@oLStHciS93ARHx}PwW@^#L=#WZa; zE5OCc7%P>=e>^)uNeLQX(g)M``CRbsWcqtOs_|Lp9E&tE62<1Wc$30qtB&JyOIOd- z`fs5Hw-F7e$?V}%!tJbPCNF;3cTU$kHfHPuu{X-*&-T*XqK1nR&g-GC>xRB!{ibM8 zP{B|iq$M<-Y*!j>E9`_Css+UkBlc`>@2%gBGHBh@tIw?teT>RI2hKe*s-&ybubEJq zsgM+O+P25~!Yw(p#h&0WaA9r^af?5R)Sa4fG=@(}N7Af-Knfl3#I-N|#NYezGd11IA!^7&ftbr7B3<$@H)7X(8ot@kEd%SuyFhQ8kQ zTO<<{-arV#c}xtONEsCZxp`?#!tS1evN-R0yz!q1&66@@wLnmeM4< zw3%iibFh5n8c#nz7=O4?dEPXo?gLDj2B6MnH0~W>$vnq>_^31>xim^i0F+u535&n7 zT=fWKP(aM>1B_arrl*QCV}*8Rwq2tgX+s`nB$K4;$`#x<@_fQ z2AZTVxM9k*fsY9`x$!E3EvMSZrwL{FcB8w9O~{QiMV21to(An}wi!Th++Z;x5v$Do zV}Nl>4;%CW7nXu;n`O*H?Ho&CKGmn_sv#_KK?ia?kJ!d+E&}UK(^?^1!`9&jkPRMP6^1t#e;zchL>UOzb|h)#`N5(ss_M zqRDEA?T^rI^$rlH&6cIc@;uR(B*bo8XVnOZJbzxV6LHtT$aAq?$^B`ycu9s9;ntID z8Evpm9pyUX#fQ1I&v^L>50ai{8%onSkNjN>t4(QOf|4X(3NMHo#riKt2o~B>aO<5r zlPKj1#YP!v<1^Dh5QZ;O8}8V%H;_EWf_% zDCsbP{oQ(W?cLd+i4X>A)~-G7&nvCC4VM9gRg<*+rl>FHVx(z7ZyLag81rL^J%j&>N5rpnB5V=1$K0ScK))|U#?2Wye4EAOYHBVSaKQ#94e6Bj|yu~B+vk+}| zRHD?v?%R5a?@Q6n3CTF|wx-JSix++W3p3&KTj{e-4{HC6s=8(jELuj?4J$uslZU1c zz$TQSNd6BL^%w#Z7Fl3oAt+FG;FDTAokY6i8n$5=By}O%2ulPVYbWRGM&!5%zhwub zni&ph7ILwa{`Didh=O&ND0z39$m@Qs>}PJ~+B@Yw*{8s<&4SiHp4bG~QF~b~FT1Ih zMC?vCYDGoxa01GionplZd+bHxiPwP-t*LY3!lWaub>_2cj|_P4ez<|J6$mcZG<5m- z`MeJz6wC{YGR`*%J!@ruD{k;i?z?W-9XD_W%{AHMm`Ps_=WXm(gCR{@L}|vJtXizw zv8xXgeYKdM2#UdJ#(~(Aalm7Dye1D0kIMGp>zP-!J+Gc_1P2FQ%awUpilSIlovUFp z;iti#tIf**RJvUE@O_43<@JD*>t*LtmLNF=g^xbCvi+Y32s>c);+|?I2lSm-2H66i zHnZ?|Gnz7;r@N*L%iBQ}%Z`HuG78Dp^rdpi)@$odU2zGi)xF=XbAxj?{_20;T#B{1 zV=q!CWcKwFFv@%o4Rkqe;@=5o7|p;7ZI6#|V(pW9m>#(3P#VN-5RF94;_f92Yz3fi zyfexs#F@XAICviq@%3V8s7FySck%=Y;7zZBqm&DT9AO+Mbo^$d=s z5Pjw09AlbwOuISgjG?)7=zT^dsy*5szgpe@)_QwWl6;2JbzH9?W)R|c5eSn+8z-mF zG@`CDt!d==vl*^fk+cvP>}DKDR@425HeS?|%!Aljge!#((c0Vcg0l1K%#Z)i(Rr); z(lO)I?{%kuaDzG6Iot}tD1Ki{PG%wtdOZvi<{X*{K2!b~9_C#}h zJ4RgzNVvu*hFb=Y5o4Iyd=s;xj517OIwWsnfD?9PQzZMZK@MUPin05V@T3?kb~AG# zv+_|Pxdx+BjOCw&JE|c$6j+-fzgw7kTvk!{N0bh!w)W{WxJzjm5trwW&Nk5?aas?! z(Xua@)(&%67aLtI4AsNvmlD;*Y2b2<8$J-ud6MUF4}2OJcdvzwz9EFbCi*UjChD$2 z?D4B_2L@&@UnrSrbrw2ac0aek_F`=n3Qn?5_B z`Q4d5cC8(xF;wV%vC6l2y9ALG5)<0tc(UsFhr6fu;q#VlR9Zqb5q~xV=f0n%WS?SK z^fO$xUscSG5yr3Q$zDm4hE;k==SRu~Uu}-e8 zLIwu_I`5V)Ic)#XL)uA=Wt1G?P_}iy(2@GUv&1p{-EDdAb$WLe38gsvDa)-F_PjP* zT2}34eu%{LDnNi-Q0>Ss8NxW>=kxUswa{_VK;q}=owAWb^Xq~Xofb!;i|5m%$K6S7 z1}S~cl%eS$SA?a&dfYl49Q9*=J)#LJf4HXF0d^@0oJjHQj@I&FESNYyzX%$JlcjsD5M0~z-C*zm3cGDBS z`Vma{2W_z6UZfOvcPmbTqQ%|a-95CpyO!ea?(VL|-5oyOd+#@Y zvU0MLb#nIX*<;Tk@iukb-I>eVpO6T792IIQryur3EA$?#oz>B)=I5cXU= z*;{zrRvVLScf4IAFS>S=EH>)%p0(_MVY5cwtnwX~^sGju&bY!$ckM!?t>3t2Y>jxl z1{*FtAKOoCWOj1eG(s1~1I_Ty8n3e+1_CE2z!lx$)WrVE@@XZ7jEq{%`0s8Plx3P9 zv~uqk6CY!4Iw0zNjXPKd0S8dtnQ)C2^$IBFMnyt2etOgQ8pfAJajX9l8=%ygCoRIH z|LgnaXx`aO4C_tg0*!NxXmyKrza<4evJmbmr>qGuD>8MU$2!MEo9Kq693)$;Kl7va zZie<;Li5pZAQi&7{XXeFBkn#A)3>S2`mS!4kQH2ob3!h2>$=t>xmZu$0d zKdUk{@&URx9O(6aMh68@SOU7323$Mt#naukb1b6GrEx<(=)Z0ifRTpN|9(9@T5kx+ z$+4+D#r72LeARtH{83@mOYw;xa{hKfd;6Hmeq6PX$*O0>ZPR$ka#H6i>GCFOau&>} z?Zb*eV$Y|J=cFN zTB%%#XMX5bE!vIgTd6SCu&`(rB&&#x`NonWYcP*9(Ua8S;A8WX4!>`Va}Dk%vi?)l z+GSjpZm8no<;%+`Gs4!KyBCb!G=`6hN#@0&`r zc+7F=U=WD-P&;0Gq94}Yy$Kf9?eJm*_Vi{K=JeN^|GNF#!ntT8@C{@fe_{Z)H>p#8 zLKo>|svagaz=wD0CzV59a%y2&H>y5^Q!!4Mo0?yaah%{)!h1*g6{2sFeT+il+lv2H z%}Q}d1Wj;TNX$^5y`Z6uJ}^=2t3_!Z*F)^9J|avcj(3z;Bxcb?P+Ux>?)fY6nfn9p z2bv8ryNy56dPOVNNPJXVXlO+)U$Iv-E*#K6a0^HO2yNOL%2t{{V{(Ag2E}L zmgl3wrEVx0Tet!Ys zrxCU>uNh^A&b1wkD$khG5PhDGxHxW2mk;cD)`~Sf zKJr%WOw9L{VUvsvTL@kpIz&>nW$E3IKjD(maG~LO<-BS@WsV^&4b#lpd3o}k%F-_p zJ6hyi{cl?R@amZVTlZQ+_0N;}@fW!@55^b4uHY!Os`e~0pU$!3B0H*7)yk76hZAj8v3eDPnvb62`+u)58O3W;Z zXRYF*!iY&9XH%#qXFMow17901H2E>%dS>k(d8%}_evQi*xVDZ;iKt>ghH-45J2;Zm z5wCsQ1zG!|6=>x37XYKcJ?(tOO3dlXa;&TgnHacm2!w_Z zYi6wkU!o_a)=#f21?z;`6T+<%(R^mKTK5?g4wv1{3V(WzWM?D%iHUuUn{WypXjdTD z2N`+oyxT~uFuZ;N6tI-d&kf9ulp}mhvpO~WEu`u!<-Vd3Qm{?7Y_;C{vkumC?E+m@ z{!YhaAt5MUV|Jl$YLW@%c){ju&`SFz3A>!CycTFqih-uz0h7MbIYc#@ZVzxo^(r8q zUTjVoyqt8VXZ~M^fc9J*!}Doa$?KtaiW=32jpj*WK*Zy1}|x`$dgu$dY-J0 z?xQ41F9zN{fv#(h#XQ6be8Is0JPAjcxTjM=ja$U;B`yip$)xb~?YJ1C-OBz4YRMII zDjv^M^sr4q*I^^L|KIP#`v4Lh`W5`~v^w`+Y+;il3%DdhIe zIGGVI%r$GDoyzpbkCZ}a%-+pDo3}))NxXp(0>SR?{<_#HKZmcn)!y_v%hiN65~rvP z!5eTR@AIz9Ds-$d)>>Lq)A2!IJu_Toh*0NH#4y*uzoERj<6N1TzMdN0ZX#a~V2MjO z@3|Wa0&#OF?fKRsKztn3&pRfLISE796y zL%P#GyfCHdrCJE%&R>+`k|{+@{y@EQvN*?oRFa4tmiUf_1Shc>+FZ`01<&4l{oj8} zLkhV=#P_bRjqWCuL%KkU5WEyy>BOM3n<94``#x(IqO<(fALR5wqQb^BHNG%1VdLJF zY3kw*^X@x3)a9+Jt~X9jC{}z5i7oVXFcU&Y%9L#$$CC#T);`<_H?C%8aLO(|llPZT z$;w&32{uy;hP8f|Y{&%?7{)C*m+VFrX;2gt3as!DG*EUm79L4=2bG(W8UL9ku9znM z6|j?CtVAf3yKtj%Y~S)z0;)Bfc#`>b;qbo6C?hH$A;_pv4lwTLKeHtCKb0h4lLCqe z6^M-tCeiT&*~#VJMH<>HnTsOTv10bt*kx*#DB2qGjJ0lZ|DRyF?#vQ5j12sHNo|S@}>mvMv)P%t2T-14JXN4=P9-> znb83&=~z-Yw7tjn?fW$biofJ)o9c}Q=%*A%a5F>E_lR&ES^Ya}$?9~RXboRpreSFP zhv9V=Ff++iEsy8*k+ELTeU=<`zm#MD*)&-Vz51io5N~)i^@j>!2$WKEk^R>i4(4J>J*DT?;Y_%)O%IZ z0EeMe3P9X+0Mp_{45Bn7AhwPGt0XL?YvN=(NYi?F%D-zP4OO5S7^cS3_IT3n5Ag&%(AIY6jauzlI~3ac6&x zUCfIhflKcpg{Gw5F&3@{x6k zJi}5$ZSGH4SihbaS60pr^xs4ancqg(J%x=4)z19k$$Q5LhDeeHi&Q3C&qeCJTtqy8^A5_27#H07$4J! zeymDiWc@@VY+8QQ(&fpH2tx|6B83g%wa;ayqx`#ddezhH(ln(LW75oaMSSxQ{v_9} zHI{ho2BU>;)Ri@vvnPg>7PAPpl9148p#J=%|MJhixTHj4-r@TWf(VqEFltI!_!t2}f`8wYf$={BxeNJg zo&;uTY1wox_~)}W&SaqeTdTO6^I3i};&gZZ8Y8(2ky%|vNwTM3X`il4H9uXE=>ht%;-NcUEx-Wr##700FuJ)g0X-! zq!D3+SM6@u#bL#MZqE^$kK1sAWJmWAgaMqu2yF+h_xU>TQoT>tU=+3a>X=0*$zB0E)zSsqZ!_oZ=#mOy>icBG`mzskX zUI|3I8W>j;6$)bTaa_o%ayjLo(UlNCw>|qH&v@UfxLA8{A|jYS1CS|wQ5HGD=rXXY zbR>^lL)W$ui+DK^k2%l#+i)~6IL~v^&<5}L(jndhoH1gy&=d8ezsO2Cdfokk^`3&( zd%Jg2jpr%xAi2K7cmW_ZHf-%X3)QbxQ&&gfl~oQFyex?S2ZI2kf)7t9o6A|qbJ;*& zF=T*Vq>u`E-A28_KhLNJxt9A9-j{hoC7t{7?WxSkC~!ANIz)b9WTg69s^jwL{_ujm z0cWb(xHI3exz*9`7mi8wQM~yj&uX>Wh{)@55)UVObcES>uwAfRC2wz;rQz9SxVBja z-gv#M07ZLp-M1ES?at;o6*F%(=8LVfsYY$@Wbxrtbi85LHn?o1={{1%Xge_7rE=h< z)mwELLS7Mm5`81AwG;GuLw={6DE(XB-|kqr6RpOfKQ~zpD+MwnrP#XsRi z;qy&PX*Wr&#zt%>PQ@Fe*I_A%;mEwn%Z;yZD6h|>q(*CQT^-CohTG@n=H<$lId)QS zfl6dzSC;*38R=CuVJhm@YsHs0*LT>Fe*0HW-#}l!8@kj~{EJy-MuRmi0%r4VC$_x)s?vPpnmwUD;?GT4x)_Hu)dvQ2MRlw zYnNac${j)m=ZmykMm^PgmtIVEF3!9;UQVXOuXqD6jIDX=?Vyd5k-YR|D^QYfnF{mYTsIt5e)6F@G%LxeqpKf`MQs zZBqnE+qQ5!7H#u#>i841dsDA#SD}6kv#T5>8i*B@`y2U)T)2g-`#^KIY?)4rzYR$G z=_0^UZrOEQ(Hg$-en$54_WlY}E(!`)_S%jk5m8fv7ks^}U^ch-eBQOWJAH7l3Qf-E z)?~%Z2o$HC39rAiK9J|YTSe*i=U(P@E&fiNtN52A+U_)qM&#}|Zg{&!eyw@ZzOZB@?U z9glkv+wrC0R^AO0U@ZmPpQ&W0byQlC9FL+{*z zPLPz!?VJcrO$M{ZNqZ%`5M4jr&1AaER+BayEOLvjp%Pgc)MWQ+Ekt<}Q>9ZEdv&+i zAv$jzkX-TfRYl&?MCA~9Qtby3GBT#@{a0ZR_%#jnlb8#qcPFtN;x9R{t~OqX_k!=Z zHUhif@yfqAW+_eGC)31N4Hlry8#UVqK0>PZ)y;VE+aDlW@7crMLc1~*o4_YIZ zjgCDvT(;xr$(VnIV_1PA##>MpbpJfA%WrQAUElW`h3kI!Q%VvA^@WZNe61+!O#8~I zH&CC!f}s6Tx(W)%WO7}XrBf%(GtWo=2L3jgQ$AH3`C(Ns(%t8Ir{irB-i6BcE&90~NH;W=of@$XUD(M47_dEMuuh3Jvt70$MKIF$i zy4#=SCE4)%;@kTF_^EH>ZVij!JG~!Iv)cXp9aw3#GI;z~u&TvzsWVMAGK7_me}{(9r}xe6E=8@< zd`#$92D+SQ6e**Du==5Ys+A46bzLf{R)Qll->`p}X!)V^ z4BUAm-~)w!&@5chDtZ}oux1Bc!Qo*=G5d{smSrUJX7X}BKA&> zva=6F>5euh`>uEYU?rC=4sTgdfQJZZn%J_()@-T#QXY3+CHHicsRq@iR1qU++rJ-` z9uNE^bbRT$G8QQu-XSOPIm$Yp!GN@ISs!8mWFlhj8#|*8mH&Dxl;AQ2|gZ6Zc6g%0xdL0 zuh@?;`h+_D-fA-?`9P)7{Z=&#w7aUamk> zFAL7nO3pk;(76xfh^Jrc)|kpARct>iN`A&rYCW|^j@Bi;MuK1Tyu*5=ePM9gaNM+y z`4-o|Ra-D{(;H1)JG7Ll^Qgj;zd`9?GGEx>0Bz)kT__o9-y4Hg(n#Us=G6mlH-c(s>TrqL}<IrJM8&=f8;m?VY0P7_VFzLRZ5q$nACDFGms&d?9ZobkBI-$zTCLV4;_Qulxbiw-7-sYA|6yqCqO@yH0`cE3U+aB z+NNuZ9p%s-vWyEX#wdubbj(vv(gzJ@g*DI5yYH1jh}6(A_GPfF~C$ z4~wktpYCmZpyaLpvQf1joOTYqzc81Ee?_&PgeNtZjm>GzHQNaSWsi&)XqC!&Z#6gC z?P09iEk5V_|H7om4k-=*>T;i;)H;Twaq{_dHgoL!Ha!!*uKQ;i%PYi@1e-7D&*b?#<8GL^q>Lq0ra z)W(}_TZ84=p|D;;tL0j}jMJZ2t=Eiv*a-BC3VYB_9yv`0no%7;^UA8t9Z`)eiM1N< zr&)s?_*i`zjX?~O6>LY`yA^6FFT?3+wk;qJTYcsuz2Y=>Xw4fO;RD;ZYqOirbFd25 zYUgQQb!K?M_xjA_mGDByPDf_H%-1xmHlQ2VI<)}(wnO^+Qka~uOmXW;X>^NFGr5AK zh3J#&IL}!niV_`OS=AXM2CjaBclMo>uwr&gO2OlXd-k8Sn(LwH4jOA8dKG!Eb`?Pb zeJd7)JFAb9TRW_1p#)f9#OUe(#m}U6ujuDJ+a->61D25OrE)nl`G#EXFMx5QMk7dd zp-G7A`~AE;-Ji7Z(N)T6w2jr(Vk7xPEAi>>Yy)k|#n1eTQ(-EH>6B?CMBrfry`%E2=5EyL%hH`_J~ z3&8=NMZu7Q@pD$bw%-KgeMzS{pJ;g(@fGMoFnc&&5%~bt*q}(L&WoajnJA^DU;pG4 zf_8X-`A}X6qb9Z0#VGRtGk<#mKQMq718r&EuV4KGys);W>M0>>$kbKf(UP{DGi4B) zZ6cK)+-C-W;-bXe@3mjx6$sE^d`0AJf8q>V#x0aQx@do-HXw%#u6jGT@@26mB+C^m z{v?}JR1JJRqTac<^vqk&5_4og_0Er1mG@MfLyVCrB8hS=r0h5y3*EjX8AS5dGon3>s%lKED^X2w{N}idbg7D$!V(Gz*EFXrM-( zfGy#5oBtsgBy{Dn zd_H<-&=&KB9J=!lrp&cW-ZFRvc3cCiNA~Vt3GnDS5~oCp z#1YkVG(K`I(Au8<$luMAgM<4rgRzhPzFGn5j!K$0m>(FFdeL#)yDxgaY_UAY~bn>_#2`bHhdqlP>4U~6Vq+!u0q zj)gWyq{<)3{6-{GnIfkEmVnyMRI1zd94K{ebg5#MT=g}!Tq9Jxt9I3{ApQ90OK=Ox zm4L^*@Pt8puGyAKsY6BZfAZmVlA0D*Dl^=_sRXFgQ@CDF6;8gb4$FxZ|E?u4ZwJNm~cg z#j5VV1O=`O6T2jNz;IG=>PfLVlw`4fP{3?s`pYO( z6PAUeKIDLeM-`aNP&_PC_T3uSU);CT`Rb4uTUs)L6#0K8at-JrZ&bw_KwA9tHR^Wb z;N-7T2!3derq6{$Dt_b?Gy$}gfBYF-26|Z8SI0Et*9mUZAVdNKu4PS3TtJgP6m`AR zZ*jrSKvc8bNdG7{;g9|J|D`P;S@pnJ-gdg^5fZfS3Nu)MK*0qO`n$5VxEuC=w#3-a z8gMkD$BtJP&SGT)qnH8JEScI!CKS7vP(ueo#D>%+e5QvF%cbpHeNxs^{~w9cw>u3s zGGQSbt<0>#0`D>7~t~=SSCr0a#!WPxCYSug#Tbzr7DLN@Y<7JbwAhkx`%A+;cP5AG3fch#UNSi^VjpxLmA}`*@R=4Ng9G82BZh7clf#SX@3C*N_hS0P)tm%h)Vf$>7Z^;)?vK;lxIy5j}41J9bkTx0tj8?;b|z)QDPbN za7zO9z#Kd1wB>gzZ&Oh>>`>KiaxXu({64lU4>G(hy> zWapy~GcR}>7#k^%-8}+i_St#cZe-0AVz{OSvA5RK(ZPoitNI0I2$P8(2uD*_{^ha$ zzdyB4X4gki>W5e0CwkovCU-3Rm9qgvWsvdQGpMhmLG}GxuA`k61II+lrn&8BQE=G<_Mn!^ zrfmv2owbn;3yB~{AUR03p*E&{W$)U0YmYEH+#0JzntBxJ0f^v^upAyHsVB?X?Cb5K zJLtob{U=cjuwPtU+{Mb*)a#02MR>!6{1nL-mfvI+V&@UiXH}KNUfnrw9hOq>qSm7Nt+9cA0P$@};j) zVI}-^h`R3BqQqhJvIz&ElZ!h%5j&;V5Qm+ovn6E_?1J7_)EYy@tDZ^bG8o`DaRH%F z$y5~A{uuxrxC;dU!G}&Z&V0jnnXO$Yz#|6#bpv%S98)Q{@X)GdlXdmUa1)+Eno#Vf zGIG;tlRwyeP8D1=6A-N zNqpb?Pg=ITuSQIrw#o(l190H&+UUPnL32j>2)Sfo9MMKaU%cT0F;NB|1K}xt#tg)w zYp9TwYY`HBqWC}aGZmQaB*Mu8AmeU(!+ZM{nE}?g5z=ozskns2$wq@p&@DM%(d2fq z8p}22?U|=XoBo1RqK!t+Ee|g9Wc=P)Mq?p=UV46Hmf7gDU*FH^l5g4I;z=BC0&9ws zF}_Bx&_*NAcMHE~44{Tuz1$phkwA084EV>yMzkn%M7kF6Zyy_=!=S{=`k;;aF;w$f z9cGgofB;A)!6yJl{B|X?l01aJ!1Bu@GrW(mV%;6bUT!*~1pEP{1G0y>7Dk`Wq0AQU zYnEC=s|;WXN8mdNpa^q2JPA8_17Kvg2VGGT4>{ieDuwVa-S-R|GE4N(u2;wbazXO)z zCuz$0cJdhgrkfT4I}G>d!Z9!1?MfbL?rl|uRUnjo*mC$kl=R)nsikNaMtCxVQ`3&P z4)pMT;^&(Jw;SrAp<)cNLYg6WJYZ z0{|$rCbMXIae67HzrV5W{r}YcB=Mq%-(1M*z=kaQ0IORDs;n|^Jk%UIFUcfmh3zXC zewGC^8?1@LqIWy|Igx)Me0OA9y@n@nJU=v@`XjjoiWU>r_7z|}kToUd>-v~$?1;1A zj>+tQ5~l&;_c3kGu~c1Iw=GXrX}BiQNjyn^>Lzec`eUyH z?0|N}P08hpMFLF#AJV4GGP>0$F4RCS6q8U|J>Z$h4biU$iqA0UJQawcISOTm6kmfj zMeMi7{N}C(XzY6M39J`;-j{{01a!KsEuqczcN>wchxQ;9KD$xTxQMBA;Fj;Z;F1W4 zcNTneTY_C`LW`I9GUCpVdbC)kLM?E$oOjO6I@0QXxy*6W1_?~5*;(}D=#3wWpW_rq zm-CwhDk0+Wf6@sF&sbquY!Bx;o-aIB>qv25xsyw2uvXV{-pv_`6!VtE(fo8FwbK|j zD!*+{K9dO2!dT1|fzAA=#OO4RJT&L;L$?NSL*>A_T_B0EhT~|qEMHo!n6Px%Pur-{ z4R-&pKrfF(C`QQfQRvvLiGl1J?7MiKVB_^8nD&@N6DlY0U;FvQ4j2zFCESw2HTH%0b1JXx34~Wl{${O!ctWb;x%;@F zS-yf1LNU(;KzzMN>p_e>bZIzr(PBS+b54uqc)8aEL^uDf0T7lDb*UR~tp`UP`%*Kwd$^LUAC362c_8`ETi)KCv! z{5~Nl4E|ETP&oOX%F#qHWc~9YV&qaJvI2W;Go#0C&2b||%$m=i?ivrTsN>}t)~9P| ztgJclJnqP$e6=dtXE54n{)``Dc=^lTe(+bF)$k3MGZB6~-q$JL1gX2s6ktE>ud(?s zYl7;RA?lfNOUB;8qYmgS9}j+ck9)3k4+(nw)G)s9W)nilks^rC#52tKUf-!Ip)Olxuly33t%>?gB9-e5YOA%gRprd^FDRX-A@&odcLIRS`TF<`n1^)zI7BPj zpMV&aM4=dtgEja~6e2ly7C(Bj9eofQy;GVc6k$=vr)6ogiGE8!D`uz8(|IIa-NtUlK zy#C@s;x7dVT`B{PwIY*5aUmnl*$^TFvN0%p@5Y7OV)-a@@|svEI(-vF6+Gx?05<#-ED=MImL9vIMV2 z@wg+Vh@<-zVgi!cM=B1d6^ZXb5?oaBnEz(Y=eTb9>%1N#(jRtSd3Q!K?Z(f|EWK=1 zvD7%vlu9 zVXIfn)*GIB`o<@{YL4wLCg|_4&))!dGaIkk1n;@cui1Y)SL^)y+~~buWH%m+ zXgj4ZH!Y)_s61YB)ILjQr!BdwIdh85{|r~_m<^L7-W$v@1BBw--j3}#vK!3lyjGyR zY|+Jdc{u(;=gAQH$u}43c1QbBs46&s-a~YfKV+DnF>x>3T;xH90JLU#g?g%U)W+F% z#e~Lt%@g#ik0ct8;D{&^Ta7m&P3sQw`nO z^K_2vT*l2>I?A;6?M=ot)fZa55W<u|%CxEiorpCyNa!j9}il)Xp zeDk`!k}vaMr{@POG2Hjm;>wZYF89t5P>VCyj2WMOLzr>m_pWBpaeQt&&T{pseF`Tm z!1^+-Dlc#OQuAN9{?Sn|H|ZE#rr6(+aL94lw|`zhjJ+0cjj;I(=w|MjPTDFR^G)>0r5kfg3sLXK3k!HrfF= zJ~Wo|PK*2HcRWUh7uZ&_TyJ}|--1+r$YCSud>k`ddO?c*k@6%l+pX@?pO-PXswv1$ zfuOk_SMKQeUuqMqI^3`8n%$1_Ad!DxA9xA*jNeRJ^?W#Dj;CB6@N*2IcMKk|qrS;K z@#mcz%If`RRP8cCA%|`ktkK`6g)bpH&SwI-ZO@~hleG!%@8&wrVJ|{#8q%=^2EX9q zVicL5i$wKeZ0sJTmXqExP`|qfmnUQiht%dziJRtEPI#%=p#4zeEhpHCx;~LyUjFS{ zOtQg#>8Kl7T{CWJM}aQQSH4D>QCU!dLdzZlO85*`4C!=Z?@Ga$-I7^Q5OV##NuXB0 z8B87LkQ7!$8o$_`i>n=)iJ~fCS%1Sj_#JA?b=(GG;*c`JI`<2w!u_y)kVs4C8sqIm z9B9<-c<>#<^Z#=nU7hK=gKcj9DEFWOEKA34m%oa9=zo~HAmK-@jt4bhLo4|uxd>tK zcb~%}P@F>6aMrHToy5`5Aok`jTJ3ZnloE3hW0r6N&vO#mHzg9Y?2Q-605W22P6Uxv zGIr<2>13MN9lz|LbO(t*&s$fE#aWG^<@&qXQzh!R$0LhMn($q@m?SzPowV;kWM?dR z%{=LQe}u+jpfA2;)STD!A0z!dyk~9EcPc^1<~J(7x%qxZ2SC2vusu@TWoFJlu@S*X zoISi)DRP!?mw|er{TF@D{MruPnFXb7nNGq-OmNe(G4wkqHrLJV)pgO>rNsvlw7uo} zWx5&1NrIFQ$1Yf!W;kS#?*yWNSqN?U(at-F#F*Gg?Rs|dHWkHfZr{g_`H){y9%l%R zE}h??8*A80BI0RgNb|<|{tL_dC;X9`ba;9x6ItbEC2aXCRNtre#6H(@>#p4eBs%VT zeD2Hu8ADRawp99lS~o@hv;zaur{^v{_TS7MJchTms$k!jaNff|hktvc2hQ)* z@5~jy!;n)`i35*(?fg0Tys#@>!d&QXou!|i)lcjvZ3s5`P!~$NzcS|A@2F{^Ltqxx z7sK2U`hQ_IKLv@Ryl3QS4Pv}j+<uCPaPH%cld=6lK6 zbxoQi7DUIdgF+#6*p0VcWDQ?S<^Wx4;-Grz(4Pb`Qx)qV6a@-vDdciNog&u}{EayP zmm&@T@i+UM7=(`EJ49ct2Op4--2(L@qE!!>3@3drNU-$%X&iE3KxCd*lgwl|A>)rb z0P9sjYyZX|DRPecxf*mySglle2Qtu^D9eE96E9*PA@F+X;wY*w>5_3Dy5`Zm%xgk0 zUc_FI|9l&bW-Qz3tb$H5z0x>!982z@2^Ts3MIZ&kn0=Hb#(c)`p}T=<^XZS4-3(`3 zacQBtlltl&Y%L99id6R^5y?qj`Ryu!(D^eVAFUga0UI9npCXf;f%*Z*j;5>K=Z(94 z>x%F{ds(B945rY2BfS=pnBt$I7rR7lEmL^QV<4WP;5^Hl&^WhQOOkilgY;zLkC1j! zr>J4>HzkG(EL#u`F1tr1ounwsO)Z#**VceDn@pgW4fMKVHwB6pAuz`e$SD8L2Ft6b zk6&zKOUh>Ctji!SDj5zf;5{|@_k6^PF~>PikWGwz;#o%2n}jVMAK|Eu0YZhJ-GM_{ zzk?tux;%Ksug#`%D8K>2=aEbfIbva(weTiF!VjB|e48Y_;clCnEI`!`EFg0mKSg5; z8i@CUFLc-Wf;#8%!YD4v)NU%$A^w@jgGxSr%22K}ZsTbWv(b3h&?$7`v`IC*h3||X ziM+*ph&^hRCRi{1f~Xi0op7TG77gjX<4Mx*uy4K8MA~?DpgtlXe;9SHORlp;;ilT; z+8bs*v1N54eRO?K74aaP=j8b}Tp^n+_;iPMK4+S@Fh8F|Pwd>0WSki{6i_wzr$;36 z(?r+3RE|ZL9RZ*LT3wB5fn)))^*a30{s%qGBA4Mx6YXv1I1S*VMee&vjQrtZ1>9xT z-9tUV`yB!V%2E9$o8B>a%3sy`xuF8kz$!WBH(4bjz#3Y)*z_wbYsFTjEKb$;Ooket zMasn@xNUlXqt;}m1M>tFPd!xf6w*8&?5VDU1SjrDXAZY%1s;7_Q_UVxllD!0n@Rn} zNQF)ATjvmN=uTy>aLav8+A&3d;n+*=Yv{3}5nj&&BAUSKuEfj4e#?id{chazWH1?z z@pCM90LDCog!G*O0u<3YG2n#^&J@*vZ|ekp2mZXmDUv14e>Fmz|2Lzfo1>waZ|SGnr*y@tE`(i3j>2>rZCaGkEhH z1+kfVY^O zY@owJH8F@6@%-&8QE?^?R)Kuc*O*15T|pWVnVM7vi;M&$#0KZ)xXB{A?T>Wt`;-RJ9o3GAtAis# zGOIzFU>yN)btkVH4TYRP{4EKmk-o`qBqJs2Mq$y7kB=GoKF%RoWiT9mn3uii$4i9q zFC_OQRdQrT-QQI#8nZxgC4cxi;;GYwM7UoPWnDu7`_45IAae}lkpQzcnv0xC$c`vE zvc+U}3~N>$9KOeJw;(kcY%U0UM8pOcJ)m*Z2>H0TG-KtWJ7(@?Lu)D*z=Fi z=-KKh_n54BQgpX^{s=VFX(jfY{Y$qySD8dp0odsCD^&SsX3(cVD z#>SVC|285l_xL6kOciH1#tM;l#kJIwF4nBRUOK7LY3xWlQ`h6!9Ff)7@p|-a^tjfP z*n+nh8xP)k@7r@*#zUXCDDFuS_UnbS^n|hM)La3-cPOBXcAt}Hbpdrr1iEL*3cz^y zn!d#$aTmP#wAMTs`mN%<(G32A$IlbcT6V60_F$xkNFj+`231+PHD4O(>IKzl&O@>( z4Z&yW^MXUSq-uyC&w`4 zK)QWUCC+Y?wZ6tTE9cmX;L>=*U#i&4hpaDFy!h zv4vvcCs?Y1W*3j$b#&93;NS2Hm+I3(Sl%Bjgx=pr$dQ+*p*5JgciX^b2VWwgQ6o%p z-1l`EClJ)+_W0ikmBC^}`&n^w)y3mqChmp6vLo|?g#v$X2IGS_)C{0dg945{uiW=X z`p*COWQ|#@Ki;3r?3>~LW2Z^-ru<`^W2VT5_+&3J(tRHH8(uRuc%#FB(pVUYt5UQXbXb+hP!nw7Gy+b4pN!QHvZ)Tw=EkDqp!jA7e`;?;if6|(_S)0 zV(>&#s{rp&|Fzd-^`J%D;EdugI;?Z;a`S9BjR%ikUnwGMlm}I#T!!a2j@NkQ8ENuXa-+ zcDP$7L54}am~)Z!(2c|mlSJ@HZrCS%4Op*iX|Og#2~OZf;p6e@7@;5FI0@c8s=1A; zk|M7`JmZ3W1uwr>gDhJ8X`;^kKc;Op%h=9hAisN+Pz^ru=v-!PKjLD4~!S$_?U~Gj?+tH9{Z#0^;2bSD8 z+IN>nLZ4R%xGVeri2BCx%z~xc*tTukwrz8gOst7*TNAvoZM?BDaVDAAww;^voO7SA z{_eed_mA%CRb5qUsj)oYx3RyRDM>$yCx9+-ruyFg{&`zNza}~P=-jp#H#jNWHDl;H zj=PcCo@4yn^U(friTeSR!nM}4RWwObF=DB^-eCqqV@Zf`%Zl*dB1D;ORK@;zQ9}k# zI4(1l6vzoe2XSf93*nUIU+yP@|4wDY|HgdZurp2Q`$-=-bEb5^)v4z+6iL06Uo&;J zASoR`FC|5saE9288pR*rS?e9(T^jPG0KMxSa#^Aqaz%uc3nRw|nv`D$gySkPUHxMF z3!Ev*82%UAZAeW&=pWVj02hJS(ToCOMP!iCmjv#CVSw^d2<`+%;pH9WMhR#E#3cCg zgj!*9?#i z7^m*j1RYhnzC&8ow@<4wVMC%;V)^ug#hyIAOG?(z(WDwnJdetlhZYG0@=!t{Rtn07 zXj{)1r#{1TV$E@gFdn{3W^zQoz+(Zz5rU2q!4Q!$INbJaW_|OEMM7qv?KyTD1=aFN z|MLyWD?I067Jjq+Ko{UTP z@!gC~$j?qp;EH_?Q63XD)J;odDAdPV*31}qw$dII04gZzm zkqjkRs$zN^#+9Q|gZ(||YJTho14E3Cm#z$6v;}D~C}(~@>pfZ70~s^Cv?E=nHz>(S zxwCROgG_%FV>Py>m+tkzGm)y1akI5A_Pq6)LK;IRX^e@lBfCWbmteFdsdfKd3$w0g zI0IPsn9X3ja)<)e(Kq;tx9d^H;=;_l936nQ{@&?+;{ywB=%69Ua#(-#u%;3vr%54& z>cxbAxD1MRI7a|l@>qMQT_`OIZ1Tg6FviLBzg;o1!NXkPjbYp9V51Wv-o#iigm(d; zB^g`QGiRP@XQZt?wN^Ub{xS7CK|}ycLv0&JgidrKgYzfq*T{zzq69&2X>yf1?yeoejutY8tzTvS5}3DHuY`INU$#jP~s z#673|%#c!8RPS-Cs(&7}4)_`8*1(Gq$D2K4A1+$KH`jla{w?eDi<_}4vyr`Oq{QfV zo(oK@7O3VOs`;fR^8W};^*q5lD?~l=tjzo0kpF^YN}G%uL2fgHOhaA65;E?7JvxHS zq&A>HpR^ExexiiV=DSFK){l9#)NEyuPQ6Jv zh1wW9ESX|`frJlv*5?>TRylm}*IeFtcVrMh)a3H0 zLlX^BBhzJOm%{YCPzYy_n3c?E;82i~LoEsod|1^8`-`-ggg*HFG|~97V0QTK6-g9% zJ9jZCR$QnhB39w{&BU-J3M5HiKaDGJRF{Ls&Wd|y$3`FsYN%Ffm4Rj9ByCQa>RP<}90*#{zvNqziZxynC+5C0h^61$i3}EWUxZX@t5A|NMokaymz>_2#&+%!^yg3M!q#IA0y}CqtVRaK zVLYWRzot-v98|@aZ+&OC=7JfDvCVP2#~!y03j_n+#c;ciWoCnOWK0!o;v)y1O-{oW zt;`9yEs_?@Q)vpYPuRSZK&3s5m}UHZkNPvUIZAQNHGpMCPJ&WN-;d&;n(=@s-lWAw z`9qnF?6dHy4DiOS8#nXbb9fiLM9@I_qfrdPxSz+89)&}hXt7?T-QQ7)nlr|Tlexlp zxK~+(&`T_B88&PD<9kiw?Tp@q0`^!TzmXoZUdw-CCpx{~9-mDORk21Ea>rr2A@30| z{ZS@UkdhcEs$QyQwPA8Ik+5OkMf?_AN(Z{lF)MXQ}#8#J#fjfxgaSjOVaz`1NBh&)8zxy%DTC0D8 zABy%w4k6Y9MAv!u{BR%N_~)hTbPo*`&~v%(E}fD3BG$*L&uAjNUrezrHpxE|NkXr; zLau^m#1MpBoNz?N(xGfZV53{(F$aymtKH6{05;6sHX|Ow%U9jHf3qHmeC0-a-YyvC zy}-9-bM4UrkKCF1s;32?89|t1Etax?Tqr}dPN0}<@sC<`P-Te3d@Z$|`)iLdcfp|~fy8AfAJ0)TAMjla-`~|0PJ0U_1rf>kr|x%En#1{h z$~OE4Yv^U2xGHz)$JAHS)$|2%SmfV&d+0^-p2oW^+)-R+7dwFh2R)R-eQdJXOUe<~ zczLl*s`T1;mEn*Ib%IDcvaZyNG_b4HBjdEL-bf%yo-JVgWvZj zcVT19XiM%o%A33+?%+i+dCaUh_o(7nUaNA~@i@Q~<18TjI2R8z)YTM3TK5>1pr>}C zB&uH_39}LEmC0;bym=|Z?UN|H@Aw^4dmhbx^wg7W2z4pLzy}hPUo#23QH4=rkJqT8 z?!7eg7|1=@y0*e6uD~V0h{Z2%pxZ?)&qLCi6d~`Pt2A{19&H{z#iQA+_bl7P@;>-m zCaF~DG11=G8x>C-hVjra%v+TLUcGjar?!oi(sSl4j1<0T>&C@fmG(-^yPuv&eTp?9 zHO*d>s~LGSZ65x|y{dSt7t!5&H>QRB_qt~}hJWblxi=G;S-b<3b1OGK01O!w*hL`ZAn zF7#Bi#CA(Rn#DDrB!u8D_9b7;I#&R}@A2b$7F@U5S24e<34J}LuqS9|o04F(rwfmB z2}vLc{K1FOI?SIU3|F{7qH?LiYI*J`Zp-mlES+d^l-0ne zU@oFhIqb97VkVkKP2z=eco)$-Bb+c1sdLoyIew^(S~x;CH_`yEv0R7|Sc0}Zt!1b5 z^J?J)ukns6uYlksZZe-+8Udtc`X8L zNhc;HP-&Z~Mfxji)$WfC1c!9;p`n^g2-BaA(dJ`RauwjXjqkH;x7dm*`W8s9=}@SS zBzoqh`qhz9B*_D5O8>MIjq7V@3`j~=7Hmk~!wNpoJ1~ExcoCPPLP^|_N)V%Z6{#z~ zemi~F0pR3<6`omW!zbE{7);on{Iz5v*`}zE7sf)k3O?19hC(?Fhstk{LWrv=*U)cJ zcSs90!av*_yd#0JWy0#`wRuCfnU+yQlU~CsOQq(1+bB}rZS+vs=8t`ily)o`azwQQ1#bzg6#kPcHrf*U&4U%(2y zR>0%_s>ihFw@p+;=TYK}EFXZn+-}K8@jTzb@;#5?T(K(c^=EKAyF^qY#6_tg+i!jv z{fFFhp*ZC}{fZNpTt~Jb5-}^Q#I>(Vpa=$@Z>|`Km1g>#&bKAw1)OT+K`MRutHNiDQ4Z4hf>h zkFJ3%mXUNqtsDjb=w^rYWf|nWrW8l@^`5Otr>}>S(SJrZ_jL`Ybf^t-t7LAyfH$0} z2t3GOJG_yG$W!OH_7FxeMVSIVcvksbJRklsYQ~GYjeSfa*FU^P-*~sA_w(qt zRmpalNS$VlQd}>q9-m>2(x-pMED%6u(nXSsU&3JaH;;2^^8s^C4b1J8jksbo?3=DtaPq1x|A<1bT?W#1G zX=s)HvFEcwj<`oZi2W0zTY~3f*+kT0zCG*~ji;rl!@k8;YcRt2Ii+2Qy~A4K(S0o5 zD$ZM)It$-Npg%eGm-q^by%a%?`PW<%*6)B+44@-C3KX}QGjiV9PJ=@P!yx)sc*-AG zruCglg`W-3gI7KVPVX;04iX#RWtxL&gh|1t)QM95g5hR<=syuhJ(aM$3|a(2hy%Ld zvubI}EA9JHM^UGKV?tXY&}Vq;J{(mw*eG&3yr1$J-2LfhaaM!iJ(L|q-#z21uqz}+ zu=;F4=C2H&;?I|?_KXXtZbjmjWV^|oQcRIghjX3`5j^?{F3J{-_;Ms;Ci5F%kRX-L zG+2G7A&m%IiuUb~Nj!cE&M)xobz)Mh45Q9Skfxx#B)~G@8HQ0Ob)sB6!b2g5Nx_RxCde z(?&3?(d6N;ylQUo-Xk`&U0;_LcMugy>KoMu!^O15YkcjTQDCc`E(l148Ua)b@7)ZpNA zCrEN3c3e6uEZ9N$w320TOhqPNEtC=1+` zEb-JgW`&5{;H#i*lpGoI)k8#t8NsC>^i9Kr?ozEFcx5aISmL_$+jt{wHm6IPrF;k` z&7D?{tp@P%M`s-F3QX((x}d>$jv7l?fp-&Ze-C}AwdJL~pskCc0AnFc)ziZw@pfok z7PR%NPV_aX2_TrsXuu_kjAcrTt`VT&tpW7>lAXq;U?U|jcz1Dp{JoK};24VcaFaoO{ zj_eD5{pp=qqMEDzwLidQ(D(qq;P6Hvz#Uw36yTU)=sJcNc)6jSKN3YoNNO#EULCbh z04)(IkE6`+frdBV_xCBls+^hs8|~fX&W;EKQ0Sui>XMy%*fU?a?9DY7Ot}Zw3@O`K zcB&o45hY3M-dkEGxaloSP~c{gcClx=yp=T^3=ofb5Pxp@eB3Uq_}(CU+b;mlrL5D+ z)5UeOg3(@Oe`6qZ z6MbKWqj#suLEUH5SVP`_4FknDxr;MB)LNT)Fckgi;XEuRxMuj(Tg7mI^weu8S8h56 z%Kv@?)eBq-l{YabwGOrz7-|(aNw4lK)FOOa3?%^*(p)OtDvyC8%*TnxVdqug^D*;4 zaolbi&v{y12z=a-1^CrLk%7T)5Ex`L(azwXt!YjZ_TZ}olM=#6h zUa&O3+9(qARFZOLm2?)~TmhD;4;F`<;sAA?vU~;YY3#Q~%}O)}vfhY;Fv>AW^ zXOIlTSf07wytoBUKHum^@UVL(8!vgMe$f%rTBI=usu9y--DjGidzR|`cDXeKbJwuZ z8e)^TE(mtVF0ivl_nSY)#msK#jZAlc3b*_ItRM1wTFh=Z#uX3<) zkK!UN`MhR>KtAIQ)=S8OWyKIF><8S(hU`)FLF{-%K_+SKUoWg?3CMvvPuhXbLDQ~s z`c+Lz@CB&T*fSde1Emj}vJr&2l<3qNs$T>VL*AbIXo4>)=(6E%-2KG<3vowzx3imB zsqQHRMhP+$@uz&qci6)RmvTd}T%=Le$NTMlpYTE0fFCOjEwIKNh4^4Z<=G?mHoQ5` z4wzU(c;L$gh<>8D0Zo}~e}>;7A!gpYz^(jff=>15=%M2KfdyDWW(l)GYIhx5l7sfz z1>LZ_`2hRTLuaO)c;E9nupCWjd!?>iO(jff3NrQJv-+w$DuFPQo546@85M-Y?1I?B zQC?A_r*~eyr>HMWT7XX}Jw^5#Z9MAu@}MmEFr@bF(X20Ad*e~0+S7j9-opTNC3eN{ zd^xXWeqwJVwCbUOl4PG`QahSiZP+WhThWWG`#RzK;jTnG#mxc*1Sw=8lmj`nZSk3N zA@ED&+DD<&GCTR(Y4}en${E~dqvz3w3=hHQ??`Fq6Qy{RhI(z=UrK^G_;VKjDqnB~ zssj5P%er_c36Ihugr&vP&W{-u7)2H$2D|v;bg~W~5psJ980pafoB0WNuW7Nzi zYX8OrUN`#U&~=ND8k*>|mP}X3Fy8{{o4HZCS-hb4UO$&?v@EoYyhXV$d*!vvq(b|O zNPJHo%-$V^$myxdlzV$1+_A6@L{eR$FirCM)W6OLaz1De&xw<4StBImWBtSaKrsc6yJm7whNa9z?i{24baN2VYi?)upwEF{Tnc6q%1P?o%+SAGO+R0^rf4PGE3rm? z>krW;gN(LIi}?LR%!4Ts_Et;jul-t&2WR4BA1622{cyIeQ`BGy_J(`mFCBzH8cTHC zVDqO?ziRAr>dVoy21+pGFX3tF2cIpogB`zC18Bo%~+3KmU6E9#FGlQk}3 zc9fBFxZsSk!y>otc#CH`3yuUkWu-|DTPV8{Ff=$A_yp9hV<`^0a#ghZYqqwc0zSIo zlsHJMZusJ<`E99M2;Z2YDl4n@6=HrmLXZkVEw#5j<@}UD(@#=Z3UP)G)egGD1I~#x zxGX98KK{ldaPe$mV&38ofbb9{j1Jk7YB=YG#{Ua`hcr8anyqW1AeN7!%8tghy@XB6 zeEO&Nm0%qcU&3YDLPjn03nPd+Fg{RL{i!&j=G0 zf`)CN(%dFle;W6rc!4&@?G(b(0C#j!oY5-{&?1e*hj7mP&lUPWs zcY4!BJsg4S&{7?m9mGR3raWCftsLQvUjglETLRP3KB8i+;aK`N_}}6 zC1+3e!P_cq|0t%Muv0h1N!PLzOEK2&5JY1RIOv;1@m%@CCZT6Yz2`;1N@s>_5H)qO zMR*I3JOQ%ud^S_i5}*Il+>!rON1>DFTIjGYpF&V5#!yPwa3)Y+lDby`9pE*$Wo8VN zvw+2CI=i*8s-BipBP3`H}?3S*qc>ZLItzZ2j=v%}$0&QDmieF&H35qYl8(?yL zoSRc>|B44oXZ*7ruix1)%;n58&C=pwy1lH}svtIwH09G3sMz)`n%VbxR!fa~hNtO1 z-XAz>PwcLJ%yTHVJ)II1!VKoX|BvURwG`4KP8~wEw`G@(G505x8%9rsyA7Vfhzv*U`QdAtz%eqB5$dLV%sz#7%TK1_{{fl4a3VBcg)JBs0 zV@r-*n5~1q?<3M$HuAU9Jxz8?mVQQPzPE}$MrbzYHvFy7eJYraY1tr#0$>}UW7#vd zZESz4F!ls*wQkK<1Z+tA8&y|M>G3fCoUE=V{CoYIjpTimZW_L}1;k+Emrlx^SS`=c z(P~d=wNe1i>F?Vl0QO)chbiJ!i+C$I;%e6G+pj@IYWD4$^6C&t zW5h#{GlQ21|E2!h%D~7kn}H!hh=Hyv3{?iOY)4%yF2vkW^5gO9v7sX$4}VzX4Qdm9 zTDu-EEFV8oSb8HcSo=p@e%d$a>FFbbGgJ<# z14D68M5aUK1WbH`k{&*IF^5MiKbyga`82Lg!@3j2AWce>hE~Sm2$^FpCaSvl=jj$e zDC8hL8&n$$(0}TJ`@$+{Yy%!RR=0wzh^!1OJ6^$H4XcfJ#n-?`9uEc4{TKSf;NV)DTTVeDGXiy4jXDj^G>0-lNOj@L0z52Vcx;^()5kN2mp`uDK4|Buj?J{ z<5$zSdr=*IceJo`yzWvs7r$yJIv1=&J_mJpMV$9V z;OdMB3;)fn8Qq+2CI&!G@SUK~UcimFxlo?sz%-%8$cnjSxm=WE6#%GakWnNQ*pB3)AEI62*2aD9|vuJIP~h~rpG_8kX|!6S`VuM!zl>U$9#PH?f_vU zCkBo)X$(CNeUEvN^{2H>lBXZZDAlN49~iOiC#lHp$ENfkdu;%)t^&}gWaJvb@x5I! zblRNOI20nny5g@NbMw6x2fZ>AThgOLpb$5WB&01Q1v2E>(HaY1aYG?A0Ztge_J{~{ zrcIiFc(3hQ%s@hH3)=UK*tJy;h&|8#RnuMk+%Dv|2r6GVR_ll`e^WDsRy#gpYb~Ai zbOPvk^3m8Ckow3DuIyV8=%}F1R1$|^Dq#e=8q#rY*3VF#ImN)rm=|;ic{Bzzl>N}G z;KA8NKG3yau~v0lumICWDa7fbaJ`d(^Nb?u;@OQnkyhsGhwXB4$HYjv`g=J*1+1mM z;x+c`0RwRu+5cIg7>c_3z#WV|qjPL_YUqIur|T#tDxM3@pFC${_HaDWRbm@6aHLG( z`P@^agf1+wW@%}$)aA(Ax3ZQQss50SkK{i1?* z|4XB)twU5|#gdUL!^BB7*_)N+_>aljDc@K!&HLhj2la&wE7_Y(RzP!&&XKuV zdMI-%;FT5~Y@+8O!q)9wvlArYWv^m3FDB20`tlOfe}d{!V5v`p{!VoIk=$RCtQ#@} zTaSsD+nm(lb7tfk-DvzT)|uLXS}i@Rp}v_ep?X8=@H2nUcntDOcNW%_|0Hc@hLZ)r zEX78e&#=?A_e`Nl;p6VRz&VV@rmyvG{qA6rlV}3mhnn>@o;ZexjBIC6?*635uNG-y zDogT$g653|%)>+K|((U(VWDTj^A@kAEcA ziAn_pORI(XfW0#j) z^g0epuHNT2no_${Mg4kLBE9(D*rYBHncV0<%paiM?9LOqcqb0S@cy1zv`znpw?t`o z@{6XSyo6c}3at;M*%#?N$EqurYzk9(7C&Az;J2m+kwvcM9gZe?8wV%<*duLwt&u(WYUD%2>56Hhw3y`voFL}^Eqt=@!0j}gZb2cgyzt+= zD{|3J)=|}TR(QhK=sILI(DK5UO!Rf59wrmr3>OfR467^8;`{I9x_`a`= zx@I$PS8ie1z9Y!NC=fX|l^dJ;9USGXAlMFIxPbb%i zk2zg?HC;RChI^8T`=Op*6(DpO3&}Ichq3MVD+5^ccb3$HYF;g^8Njmea36u5Sd6mb zSQp!EI1A8Ud3#{EMBOgw)gGc+$o+rkds^QZk)Lv&rPnZ@Yz|X=qX6=f$Z!W+bueYN zqRgVU?==^+lk>$x1893a#O5wt3CKNMzRTODHY`#tuQa>{LcPr<=F5%p`u$%qSnVl6 ztTxK;6s0+g49EECvC018{bIp>%>OvFDen%%pX-|u?9FsoUo^8bDbx}vFkqsi(yY8R z(>nho!&$FGl*cW$l}RaJ5!NOI#m(vhe3N3WflRTeGvP7K_iI!j^gr~k*{=S{%YWeea{0h z2V({a*u(qHklnE>!=KY0fdFcX{>qnqa~06(c@ahy?PbZdIYr;PfZti`@3E6)z9&QB zlQfBWGvCX`wIjPF zZ?hSW!}_j!?bq76QyoQvG+$blusSzja>JCY|2!QgCiw$DY@?Bc_w~(UBxG~MbvX?| zdKKw)bN;4_m}~hg@Q2AA<4Co5q8yBK;1A~+KQO~7jy1A6jIHIMAOw`PH5>ER&i%~S z8>vH-rogFGBVO7MN?xT2_q}(lN>3)35NTmkY3ccGe)4o*rk%E3dtBX4}&r6pLYOpj zrlXn5o+`ZL{1tlw)loA7xDb+d@MI~l@L(})xEMmZ611K{$dSu5%@25hGKiB}b4ic> zj!FanQLlLl33h@9^q!1AfvSMx+d&)kJ*~(=LBBXT`Mg z{LNY_-I2y->^oR;#7*`6e@%d2ae@b|O93u!xz%WX^nmqtR^BIZ%|6)tVKQc>r1pIo ziJs&-T`?91vh99L<18^AyzLMs=nZeN4tjbWh&A$dEL3z^N|xnY zC+qoxi%xW3CK0Q4rJJ?H%Y<~8mLcdSH-$Vlvt84Z9Qp5_ddk&w&fR%-+$bH;KjsldaUR7S+(!fVwa3huY$8B4TVy+Gy5y8Tgk z;*-HI+D|j`ERBcYGJ9g#c=3(Rt!->%Dx7V165+8hLG0Y;XaUWfLS?uU^VZ-_gsuSk}=YH(MtCA`O=^Qj!2UNb6PwDTBWhdeOI9Srl~r>x|)WLnpm+jPLKf3M)tDe z0d7J>Q(7Ihz4T~*N$bu2zt46w;`D*pDr_m{>Aw?PF@B48d_ipw*H2l<@71e7Dc2_z zpiI;Qit2S~2CG1b5LOe;z6Y_hv`8#KkRM>uj7Y{ z(;^uZ53FeN&52_S6SqSh6N%jdrAcekEkNi2u?x#Ei0n0GmR9_%DTkIDwaWEmWj2qI z8`vVoBBV}xB2*d!uSxEdLEqTFSgL2R&RvRWn8GQaSgr2*N(pYu`U!WNEYghz`@0Drdg z`d}~m(#*Ky>Rx`>b22F)6+Z1_GEwqc3l6Upm#6bfngOF#dH}KRdt}a(a*#c^WIB1a zkVT6NLp?eO1f)++*&5XQjoW*!V`3%q1)VUi*}Z>@8}|Zh*cYDgFQ||xz8i&Xy{j=- zih!SUieC$rWQvi7@ppa3Cf__$o3 z_G^9}u}!joLB6Fy>{jsQ^hpKByEgigV`RaHVqM$1isiRmuzbRxf9G|#*H5>}L#>ku zI`cx^K>sKjm>RT7>&fns`)hQ4V}YyU05Im%wz!FTP~Nz!rZ~0L;Ws0{LFCn6rz)RY z_H!zQF|T$GsnM@GFKr?df07(K9~=QauA3vnGX=%mSR%e@vg~x&e-zskcDI!8`&xIM z*h1BUW^em>4E#6Aa(?-Psh9DJsw|nV)M=3;!P(@^E99A(-==!owOS%A8{~+OHd0)^ zsT2D@-=$eSl&B1<)4kN&gHAR=jS)1Mnx^_9|j(h)X5Kq zEA~%PG{mN!6+07Xz<6BSy?k;h(@{bJgJ1s4Sd7*~egQv6|E~QUA_z7TRr`Ir4^OfQ z0ul%!D`!9&(_?$H5|8oYjLct46{NMq`tA|qN7p=#BI&_SYcyDW zO?=OC(h#;fkqPT%1r5kr%Hph1gnW}yhmrZildmM`N?svzYuUAY1*kN>cx|={@z`v7 z;aw*hj^XDm8xN^5!ld0C>I4eB#k2bN%jt#0ZpHN;6P#@8n%{q;E^iSx%dBcBuQF zPV(Yn-T);gF2vry@5u^E8lTGSG^>Pr#DTG(PpC*Yb?sUsSzgn@^|fNy8>aWe2gE3h z({RRt)wjee*~yzGn6X&BHwir__x@{}5o_Dyb{`prd}$B8$K=v0rFM_KPMDH=CsY8a z1^=KHpop>eEmb%87$q501!+ttsf+4F&)aOHI9_o>EpsJEf#YiQlVL)Y^Ld~Q3NvqX zgyXmxr_3%O#9QF5Jy@zwuC;iy!!T=GVHm$bm#0f>drWVvUlNsAZRI$(V7;-k)JL9# z200Cz)oX8Du3kT6Txf~LV<%<(ot0xj`e0F_0AQL1_h}*t!;lxnHC97l8)YM^OB_ETQ+;YF!NkAm#n{2trkqq6SQuiiUu`9)I%pGG z(@{O0l#qJ@PHCJtn+C35f&XG)t7`f9QEk*T`Z$+dXb0!wfxr8cFy-YrXa_h zn`D!vC8=3z_mQa1(7+79S*>sRy+}2WCjGyozM61=R)hnnHv3bG4>;~0c%c>GC`07G(&`<^^s5rH4Zp=fFI`>v zD#!8nF-48plHc8Ei&=503cVpKA_b25Qt@aZM6CJj9au?MaC>-l)^INfg|{qrxB4RU z+k{2n5ISEnz^--(*f>FswFHOeSbCfJ8g330u_B*Gs|Y6|+2P6uCyeXo(3Gy+nWmbB zPvd-5(KN_=Rqnza2gk*KzFrpvhc(&t8f@xd;B_WjpEWh4(2G)n(#ZmiHn0Xcz2K2E zENP-qs~xhlI($D_nKq*{=J^N zgJU(S;9TMyD}z$kg>fU_I+jyjc{msN3r;!sG%u6{W8<{pPYhrT0GqunPLgrKyho+K8JsHw zrdK#=b-x%IVt>Epvf_Ls?oM4Eq^fEWuuA?QLuWI=jxb68@*R zG+IpzZf%*#TFYm~VA*?9iYyzLU1JG6zZA{BpFFpzlEa)?!4F%n*r)oKj5;3I4o8xRpX*chkLN^4{;8iQUBtWNxwRXNc?U_nGA{{47O$?hauP=xLkC(eP@jxj`SQHEB{sglT>dC zD?K!c=lbP~cQMI#6ETU|nO-tGSIYV`7A9G(_Qxn{u!t6m;<|;p4O+;fyPR7KmZB6m zkr`LthpY%sZ=&@)kdc)xi}yWfqjm$#;0Yez%oB= zvc4UNU_|VopMxGBTV9P3qfS)vaAIptkMy22yxAnvTU}L3!H=vQ%yXrgz z${o#rHT2010P$M>3UQ1wrMrLCJ@}MQ=mGKE#!xXP-sVQhlpK)OA1@%LmUA;#y=5kS zUXipRQ1pMVc5CaYPDL>+;bR0S`3E}Z>iyZ83cAq$u8HDi0+0!51Gb!?F!1)+SiREA z=!DBk7z^U|Xv9-~xN>2OtD9iWue_ye!x=v*A(0hv{7MT&N5i3nLC2wiFh*2{9Oq?O0&SHmGyvI4UK%v;kwBNa=l}tH zu33Y?$3+9@cWMQ{_0;dGeHsSW{BYJbf>V%?(L#Y;*OW{c@_B_gx9(_u!=Dlq)h%_h z&xs({WzxFvd&M)^^#;dw})u#mKZI%*JbJ4>+pDj{<>uu>H!gHMKcskB&a+Iq?; zYog}g{c?EHD>5Z7-rC&knW#lTfnoPHI6i7BQ*LI|_Y@YT z?YM?HNxyNGZA2b~aG(P$M()X&@NcwzU4LUZBOVvZQ;)1r_mj_o*rqLUE-G2%6{Nk< zvNZ>dGFYFV&<>_p!=vDpc_0B(EUNXCT8r-4v2Le~(vRkG-4CRy_4 z;tw*UC?GW5svpZ3qa}u z+p*fBoAt3`X3=imxP8I`QA?UAsANHX=^^D%IpS#lU2R-?{%P*Ks**sRPx|Nb!s?SB z$G|}LLE3`in1F(Vy_SRMAZeWH?+_3lkkS_W{^6WId1qwg3<|eyOYt6iB}E;n+6p}p zo}28`dn}hjhBzB(KeE&lYsLH$Dp#}Ptr`0+f9U{C$o5gLO!G1xvv3oL5T$e7^rIclL#?( z`2s8Lf>uce??fUsBKjnGkj6w;e3P+B^KMPrkTrMgsQXbA^UL|04M~0JS%RYQb*=f_ ztG}>1n3RH?x|FYJ`9n1~5e3z=3wQRt2%?R8y3_3l>v~BB*J8%FiQ%s}?opLA0v8>-?I}&jtsWilG-h;UXj_)Nv6LKFk93`XV%C`uNyxkqd-;Q4gZ%T z&BCHlegC*c`uClL@tsJpc`kKfvRol!($Mn+4G3d6hX3%dgXq!d0m2$AZq^o?cPS!g zLnW!hvo~$UY60%UKUW#o@B>_}C`kqhx%^(b_V4o)F2}nfOas9>zNCFF|0Yk@iR=rM z#YU~SkY*d0?Uovw+gH?&Px;>ivyO?%)OC&@>yu6`O$N6l-GGFW@z*+8k7=m^DM)7N2K9(r0$a80?yn`)n)Ol?h6|AR>$dXTf8ex^Y`+bb{ttD~vzd zf0@02Rj5qKoZX}$YZ;6^L8W@MJ$@vZBd@gHJg`lRnPiz&eTK-s(n#!Fw$|yoce+i= zMhHt8eMJ#peN^m!E7&T-PFJ@{y1ta90he|PZ0|bkMtiB3_S|HrMy`cKq+;!Aii}-5 z2bX`o%(k_SxLVuadE9GO8C>u~NVC~UwO`GAON42QAvF9RpoSxcg7qsJ%CFeLc zJF1X{mBG3!`T$XO-Pr$};|CmG?PJ2lnV3Os8-p1)av5+&?FIN)!F3v+BwbHgV+_{r zM_IoWa;9G{om@5!;=o6aQ{)Tugo<1L8xum5Jjc$ex?YnHVa>7Rt{|`(Iyv*WGmIC{ zgh?WX#zMp5>5YuU#VYwY?kP8Vg(nQbVU9d|GD!AhWno}=hs!@I*Icl|iR3#uVVWEh zZBtj5);t{CJTkeJ%__$C*hDvCJNB8*Zat9Cxemp{yHQ$8Gd#kbyU(Uw@)=t*VtGH* zQHV{%7WDz!o$i(=Qi9D*^ z@YTApSm8?Q@10{Du@8wCLBU3G2^J4z^m*zrA8?wXPGi{=XR;TM8f+wos)(7dqM;q; z=j?6qT57of5!XX!@t9L1QVuDO0Qett^`BeS5P4LXJ+VumvG1r~6z)3@#ndDz{aD)2 z8Gc%UV76;l`4AYe(Q;Ue^l{%jI~hS~kEF+*rXTk@{`78UFT2zRDu0wu3R8{a7LIK$ zyc)ZH-VC)JVOiX--2Y-s0*6^P_u?Z*w`Au4otWS*24c0Y%kM>n#R_-o_nRY$T-Ce^ zOUxA%Dhkm>yj`BMr25HVlW$`Z%C1!gGZCf+mDR5RGf@=D(d-YU`Un zeDoeEd`6Ty$)2Z@CM@JNo}*;HktVvY`!TrFS!x)esgNJ(0x(WG;h>iRUS@gNH2GMC zF!m2@@`FJ0Z6KVGD{c+rWMN2-My*)z7<~=RgFNiFx(<5bZ*jBWa?w-Bby6pk+!tlt+J1_iqPcW} z$3kKWW1^SY%#twXrb_KpFC3xXW@z%i8z0*oeUhsEqqlmKJwD$cMXJ86-a6d=UEdJ z32DPb`H`a7)_bV~4$WTbav6X3T+8x5_~psn{bq9urkM@_ZyP?`ecdyN6J(_kb~Av? z!-UWpx)en~tl`rX-ksLCZ9i$_-@OrMb}_!Y@b-R77AIFWNsbgDw}&f+%WLm`_XZS= zTFP#wN7GwpO3?y8{3|G5uUDI)dR`z5dHr3*&)YXBniU6CB#u;XPMaGO$Lt%+UJvg# z8Y~NY*G-G)|37=}ES!sSjP1FvG~)S_vg_K3kKy%pnW*3=R^Rg?*1xBT_DI5r;E&84 zx(HGssw*zc7ZJv1N%B3(m$pqIBbY3fJ%~|z)xo=2T7qBuk}y(q&RaFrq*=15pmdut zTIN5D_bu>S38xmDGX$(ahL9mFgau)R&jLwnd%9uzxUgCejOR;jpI4FDa5!hQ0-iL9 zlg$MEujidSPH2|Cbu`fJd&vz;WIRXjytXfFuS9k5Uj2{Yp5N}%hh=)zukEdli@dy& zUcIzOXBb!73z2|qIn6$t=?%zpe~H$@XM1_D+z}|({ffcfi3VCADYPt1D`chSVx*Ok zX`Z@_yYT9C7UnL5>2B#!IHc$v#5qR=Zn3b4nmipp)kpE@KoFq~lpkVTuhJ)SOBHg`rNz3Eo~(U8`N<(#=0T;gZXE^nRTTj?j`7gA^~dFnbey{ zmH@)HWx}V?%Y&=O9^a|mX6r#|{S)w)Mis(*X z{%8hsKqPWuFJ@iY*#Y}($RSg~P|;%at&3-RH*31L(Cyw+vdSBc2;kAJV#Y;+1TR$V z(f4|a8y)?R`Z#1-U*{k3YgQ;T#`}45#+Wk-g;xjG( z&H=ww#*IfQC}l?YJYJw^-@%rZOQvD&Q71nE71EZ*iccPC?5j8%nv`X7j52~+2?x3) z4S6I*iGh16^$yppealASa-J@GV0gxt<3#OZ4^g`hrLw$ii9r3Fd5XzE0f6Cn-8rvw zO-V8;F;4+2!$yILv#H&CHdAywPbcRJ>&6lIHpQ|WbWlX0zCBCHK6~a;U%dal#%ZH_ zSW$bzNM&##@);imbvUaD@682ii1_Us!F)$l4ulh=sj9l;aK@$7H~DkHu|sBLA4ggV zX0Ltmcuymw;Mfmm?3!g>d_dCNdAd01ecgu{$qf9x(eg#J+b2GQnBJGMXTvV>W2WpF zpj-~6d97*y5jkVIVBpmzU;nep#XTr+ znAeB@=LpMwmiD1Sxw3|=7O!G6?q=T0EU>TJZVxo^GaRi^%v&`0j^?hgVbWZ={Vf%V z_YUGZLJnX3-7u#n^EO~7{EP)H`ZRw%*jC%N2od*{0@{gxi^*toW{~>Z7 ztC_3$`gD@pPK#WOKH>dtoP|Q{V8AOKr7+O#IHaGij3f;0dN}9z+k()6RNXcCG-g3b z71O|sB57;jPRNyjN$OsB92Q3>BBj9`x?Mh60$~GyPk*_h#1(*eMKR|y{&KdzV!h}+ z@pGH^`7IB5|ChymF{MIQ_KHz@HKzGh#IvT>0){S2>Y|S3f{Bq9WqUkhOwZYWX{r|S6On=#6~9OQm$>RE9_*A6C#|Z| zgnVR`X&%}8rQ5$*CGnS6vD{KJ5Yhx+Wsyjl%~ZRP$;Ln$vM*_Ep_Im1Qby;Au9!g& z8vD`YcMbh;V&w~_QR>@?gr8;<jMtoo%Q z8SlQAyd`M0{b8@G`c^WHP4ET-06cMB()X41iP>3x(RDvPz)$nuT-X~-B}&4Pey=d7 z7)K#7@gLKdi!cg54V;O;gt;8_9TTzR=Y-`#yUam`GtkglTMlHy^@PpnV~0gO$CGz& zTUpWAj`8)h?VT`8<{8&g!h>CX9ZO>rmtH34pBFSO?pF^tpXCPf8;%g(h4yg~&#%`K z+eU1fDC@smu9Ne9nIuqv_TrVZSG8p{{BZO!=(^k%7oS;m$%cDMQdKg{_)l zCDt>nsebGgPft&^lT$ubrF|*!hG_c5>8icPE%MAH2ZdI) zTgfq}Mpx8rr3Msv^H0>~qo?lkBz>tp!_F`}`Sk=kcBGE#%hCq)UT)w6?R|4qV%wuPXzr&DED?9CIUj(0|_y#VIm%WEv0!Ivakn z078DuF`u@y>xv$99By$zmP(e7Xf#cTq>h?4+rjb%J>7HS==rAY)6Nn zZ$odLo#@RxP=U}g<9lSL$B+AE;153~qH`XIYv3Jv1E8Fw=ukfh`|MJDD?DaRN1e^g&Fa&#Z2LZmD!SsqnWeiy? zwB5VkhK1gI_(;_FIry#Men@0o|JXb=SPWsSy)bQhlX2Ml1%WmVFWM7R%v)q zF9)sSe6aM`XM6n*`5xUs6vy?KG2YZx_vfW4@!=$}T%^Fx$0jgD;ILUxo=pC*umz#IWeHM>z?8jWoE~uh5AvPG*+rZag)(ATF6Mq%SQ_5l!=)5 zkCz4wDc5U$EXa-ayu%NH%~cZjATPov@JHMYR>LPVG5j@(bg%Rmj-7y#u+Z|z%BlsL zE>qu8u_m~m=M?3DVu%H|%+P?=YvJX8(}Ei%rG{cO;&T=w8K1K*1^C#L2r>9Q&2VrA zgRjL|Ox<{fvd<_@14FjQ6tl}8ww?%YM1UbePHLN;o>B9@ z<9-d0MTj9R;r?Y0n?7TLzE0|hF``qkGc?4DeN`w$6wfekbxtOu264hV(r&Yde#*?U3 zd=1i{Rk6H*aq1%!u#!*{#iz!!LIOS=q+s_bJ-AM@cjt{OmeMRveoXQcwcQgci2`1D?;Wd_~MgEl&hWZA*FvEUG3l znco^Q{1H9{!+KlqZOdLeWfWSJA=ldZyGE!qAj?h|8uQ) z6MyNGs##>&tR7QI%@QT$S0%Ss>^Y&;(vb=^P_8U7!;tA!2D!iG9>rGn7sx@Xqo*&3 z&+I+Ga@4#p4svwIOX20n?w7NJ=@0H|uTw@Ik4vm;OlduW+q4LR2|a7yKLl`+^|?ra zkfQ&9r1ckt+b+UwODb#k^E5Ip_AgG2%%YVgKl&T!Ev7vk&GY7>pU`O>y}e{YYq`q8 z-hKge0$-bWQ1K^uNmev>EibaFMdB_jK5KyQ$+nAf_sTNTV&Xs`}K(;iEMa2+~PH+!{d_E_v}i zm)S^(oct+5%jjnbc=$D1ld?%!xQYG0GVX?jc+lN42SnK=1}6xEJ}pa7II!57r3BA& zLEoMu!8Rll_N+F8=(9h?{EPHo}Vu7uXTYWwz-sN|oFvMuY(=eC|>ZoIh72uAU zZk5DI+rJITf-3)lLeq2Pq+b%7>iOlv?Ph(SK%3_;C9z`0hurE}^H1PEtKHojh8_xy zNpCf@jD?jmK`Q{GnCHFxH_@y&PjMOhv$+#?K9rpGZA}p7@K>PWArPHv#9>eY|4*mr zPGghj+&{cvGi9r7t_%p-Xb(J185e+thXMg3-8=dyI2AJ(f#-f*3k)Izq+L7dnZOhB zq%`F@e095zORI!Vb$&W5Cx5%^;ewhd@zgHVdK0^GqMIFQ9dUM~B%Ad(sPH^%Z0d4xYa2V1imlWw7Vu8p z*rBy-h674Cx6$dkYa{1+{T_1^h}`+>#hH$v+FA_yLI@RwNnyAmsI>+@cU!)~jc%eG zZkd>Zu$-dW7sR8raP;5z@Y_`yyF$?uhMJM}r=aLbC9j@YOa|`X&qD#_WXbG!9)*fq?tiX7&98g%D6c=)`p@$o3rqu`X~Yagg@a^jg{K>M(AzV8xTASX)C{YY z>D|2#&gH*upwZMTXI?+%-G-~Zk&A7yRXY?bR5kxnYEtA14y3^#p;)?*hDt4{kycs= z%Ys0|kJ=r~1}<+hJU%_K2Qom^wpXfP@-kd_?>5V#&$wT_6!V^y@v-~mmIagV+37Su z=~A!{H#VMe7vcRFPIq?Ne*F1`h;5#?9<;2_xU<#-Tshj7O~28g*U&ZIb}V!p50&UZ zJ|@vs;MX-M=k45&TxHUo)ciT~{9g|R^(<7{0;5(Blp2L1;(#)I_6LBQhzG>Sw7obD zc?7NDQ|YEJk@8R2XsuVV!{F@S=j|S_86HbqeLwy%!_LxI5$VO1uM8yP-FEA65{U(1 z{5-R6WoXDZaWhK*a@q!qBw$HQdX}FrtnBMj(*?Kl-R+4YRv|XP@O04-NV?tWm2_5Z zTP&_*fiWFs(TKt#OA2WSu|7A$L?y>TQeLl7-m?C)_QRUcI}Vg2qo(`!TD6g^I1S7#n-qZ6e(N;<4H-3No|AlhYWlZRyBp0m9V@ve#lsGXahn=q?J^4--8OSyyyR&2iu$ zQX1ZuBEX5U^x3`d=0xw0q1mOl%Xjb^KfHU|8T=Z3HOcAgMpJz!f0q9}f>lESSL{b- z01M+YEYAs0%iVOyzyC`x{EOP4eaoZDzsau=CM|C{7WS`hwyzXk*Si5!uDGB2t`LHy zBA|q1jksWS%co}jR}lX)k-3Q>QS`zeRMaYkiwfNK3)N&#JX+n~d6P#@Um4h+s6xT; zHCDJN0a%O1_e=$(4eIGajsG{ZtPWE&R1th2<0)Ghcqp)B;FcBLD@2T}w9O?pE^m0C zQsLTbgqnIz4mqoJzPTf6*-kmSGg^OAoG7y#f7v6}>BD?1`K6TFF}A*wXC59sVi&umb>`uRC`ShdMH)`A@P5*tp#=WRhv zJ=y2e1ouEqvk6IB%nsMjY?cGy8D-XUi&N%J47leGCV8UqXOXH=Xt4DM9p6EN-a(?z ztox1DRhWIxrs>7)3HjryKm|vp6{M18j94fi zf)n2Tcz_`H(;78ZjO+DH>fFl#9PMU16bOr#?d)#E`+fH!I+GuT7IJb;)crBs zC|8WVUoSvdjJOa93$-CsH4i)_b^mYxNo|H*+inXC$D>5?+6O~xz?cJ0xDP0;2umU) zBupa&mWpCFMTm{YLXuE#GaNDaHa&M_CkD`S+KQaH{jRqja2Rl6$t%Ooo@!b^_E7Q3 z#XYDDg@450TJJ10DX+Lv4d!qrUq_*LHN5G2oO>xkW!YHbn-YLYs9}@Feh$V(If3!a1-z1T68vrQZXfT%oqpzn!W(|5noY?owT;4 zJs*2Q;Zi@)^G?^B*+e5^Y0bbJWq1#Z{TBGO z;E&fJe;m_fqjXf!6&TG_FJ-6-amw>Q=p4@xH{{JGIA*eu4SclYcX;m3$X3{EP@N4w ze)zX~6Fri@vLt_Nr~imfo*^sZE8ZYL3yHZMPk>gi-UBrhz729P=%dXysFj9=m604S zi79VHjS=03iQvH$b0Uu|b!X5%%d96~4aAh^OI_wHT8zZ=;Em6qcA$nDVrC-*)dU+H z2>F72$dbYWHPXMuI5bZUzNyC^9Kzqk8#^%{b(p;JydM8x%=Kjs*pqK|SbD+w7KQ7}C?RjZYL6i1C)+TgVIy*-=w%!Foxh1C(P z2tQMxKio8F@$VPA?UI7`;r$k2RpN+Ayrf=~# z0@=WVP52p2IML!)$r(q~-Hw=xp^cDB5iC0{`&)mptL^b(EPKI8y|C+s%+E_!_j+Ar zXga3GgI?EW=Qypa+0asSR%<)IBVwE}8YEEE2m~ZRH*RjDL`*{h%4@Az_bEG-u*>-P z46@T$ioi)4%)skQnO+~2g>o>Pl8st(p5`T5vh`bKQfOBUO@T=Iu0W861eF%K~w zoOzF`J>*u@A-Lbir|bGo4%txTOW(N^iX~odAf@&y*LuNp>nBJqJPfSlq~C)6@n#Yk zG*c&drdNu0m7kG;w4o-n{f*($Tr-%>8bQ-QEKt1|sf?X*Fibr+%qa`&h3Mcd%P2i# zToMWD?_XFrIEW;aq~G5CK8G>@)Rq2s5Ej@tUiMii{_7iV_q`C0_lH{cTz8b`9nQD8 z6@r0AV?qKq()x31c&9`0KkKIhuEi15K^WJ($l6{D0uZOG2S!(q-7B3FXtuUe4P|4H z2bpLI<=ys=>z`k%&R^MYz@x+bbE5-~RxG8-zjk=boBDnxOd$T7T#OCuub}S+l_UF? zbcYn+_#n7kxm!jn+8nFFh^_rCAfNxR8krTl!rtCBwwfW*dSZx^##9W%JzZRS8cjLk zrOw7^?D=|k)2F(9^d7Jg9uF9C&Knf89`>(Uf0m}0nxe*7h#JHFzU3=j?}ncJJ6;Oe zh>PE0W8@HS*jDyOqgc(PI)RL(2D}6u7caOmWjN#N$?r&i#p_P2 zq#D_=iv%OeAS?)cu&SgU_vO7lLAOe12P^;Z;7BfuNcI8>{Wz`gf(X)d%sPo2bg?N% zMRN%hR5a9`eoE89kiuMl%w+#%U*X6>Hj|R5;M;h0JdqCe`3~OKzp(cQ^Eh+d&PULH zvZR6Ia>ntuI8#r+vrQk(jaJmQuR4Y8_E0T{2*Z@J21A_CjG5$$C5%RBc^=!w+qnD? z4*a`$@@zP1SVU4-x&A>4j?FhzeEU9G;|(kLS^h(#$x#BkPDXHd4uYT1G<>3kXbBb= zsWnqL**A@(7nTXraQ6pj`PyYfEuLv^&QFJ8q#%3e*b?R;N*>A;;mcu9H zlsL5@4}M?q%fS(~=X~toewn-%yiY6Z25ZsFiJ?R9*eP;)Id`0Jb~YkKjerl}N`Tdp zmpq|ln6!=?1LA9E0dOOwKTN5YY3Bf>^J1m9*)lKa1Qar z?KlDQWPc!_b}2`gC31;XpAK_oi+=KfR5jp^AIJd&=ka*5gp5VtW22@Ag{uJ54P2Sk zTvq2Wq_Kz+v@5^GMgP?Y3oj*JgOTNAjAPk(&W5<&5nl{UA0-ImSsnnHQ-#cFBYHYW z#*4#({AavryhkYk!=YERXe z&f*P=oVcF0%38g{4g(LaBYW6ko(;V*bd{PwB49lb3OFb|0pD>J%y!!mUv1A6%E>w3OMX9pS>Mr6w5VI%a=7N|*6My6s! z;1Yfl=0+v1Ysl|~gxArDuFn1yXq1i{Q|`c#1jIDd|7nC!l*26=FWqGUvp}pFI~+K| zLa&LC?W?|{Cx7z6$5Z0xyf$4q3)u7zE%*}Rm=Eq2BgJnK+l=I~t6{U>j+PW{J`0=e zh}d*LJ94~Wm{z${leo4{mGl;lEgm!nCuU>(Mi9eKlyb!W@5J?C%psQ4*%dsU4WiC3 z(xmgS_TS+RAGU4(*E(9sQ;%(y3@H+Yj%*I(vY&veS}QqarOALuLQNA=L@t4PMMc%uJBw zgI0-1m~wl*$=o1fBKgdFZb-RUC=P-p?>%_Rn&T!gznh2^t#v-8A9BZsBbupNNE4Nl z=2x3ooi}P^n4b#$YM}Or;va|@{m{m>`w>?g%(W3+ii^gvElBos5{TKX)kjaW_ zms`U05sB_{@U59MR)j8|7Ab-?HtcN35}wu$bf#8GL^U&{xQ(fhreM!OzS4*iosDQQ zq@`ATYX}TIdx&(njr_x|x`jna`yBGOV<$E^7euO4!PM+IEl+gg_)hzdVZ6a$-;<(^ z3Z|n%=I94)Czb`5P5kwr*ZwE;fCpOgzEqW*Ie(>0TEG3RY3iY!ej-`c1*{AKmcoAr z&3fP1ul5qP?H9<9h{V1=NBsxns`XY!2zT?sseAy| z08%DN!FsgOZtB5(7y@`iVhxL=Ok&9_mM$(Xl|N*obcfM(r0?q_&6Y?2RJDVEN{<1X7dE|smd^flUyY?Vj0yM(`!7Uwh7lO;Y#Yn6UWtDXi z1`az=iHN@^-pI#Fljviu3C!p=C`TuEBJ>D!(+P(y6#R8O8epE1HF943eR{<-HK%xF44DL%_T$ee2jVfK+SRiI1oX~C;byu7zl<` zX-2QHBN_9&mGMK3Xk$=5W3 z8!hoJaoTL=+z+LI4hJQ;!onT-ffbkV3{^eNOt8K*N`d|1}&jT(UkW=%>>a#Rbvd~(_EtO z*Gp_*F(2=;>Hh=b{4KxdhYRrIw-~_XU$_ZaJ z|EQFbiTwrki9i`p$n|xc!*TTWq_Ng1NZyzMUAR1t!>4EUW+bYmsgau+cn8@bU4p-z zF!0r$Q&FRH)F~B6-sSEfuj{-#zbva2+zcfYpj#1LwmW8WR4*4r!Tr>IcmQ23#U;kU5^x#&>Y_%*AH>wo>IN9|Xut45ujp&SE*hE49+W$UC^ z0}$8_BstDmWYx$**qw%DQnP~r-N*vEPS~ENP#ZikZ|FLXU)PjLLcK^z>^dy;)z*Xb zT~G{vJlPkP3TX7hMkN{WP{R;mDIn=3i?og=*mU4<$ys3$N|aYpj)!vJQFxNTSJDVX znM~uK8hu%XOb1fI}1)?)G z!Lb-MqYosQ{4Y=%wS;xn_|Mmt1Ihqvk#bDlL;`Ow*!3na^cEL*dq$K=BYi$6eU7tw zaeFywxp7T1o{;PZ#KS8cAQ8fg2~^h<;`~&j@0UA!fMdSBq=OMB9KflYA&wd8bIV+m z6!d~g49!Sn^`d#Ikr_k)XuXMi)nLN_GE06f&wJSXXvz_G=cm7v42~Jbx5vjn>H*3* zuGkk)a$|LKxlY=KHAONJ5>A8=8v5}9-twhS zDrha}?w=@RiX0Ihoi8r7jfF%8(M;~13!Mn2aBnyAq`ndQ#d|@zKsOc)reKD47Iv%c zzT5Dl{cVOy5#7GuqmB;lW+EZIZ60VXgHhVVCIkjN2#)mtY;K37A0s0ej;7VyK42Ph` z8Dg1Cl46==P@wpzgOc83SddC$rqXBcNOFaBfZ=^f7{`erfpu2G#*6eBsn<<{3ql(^ z_UL6Z(FWcm{kL#IcNx3Zh&V>v&=S(21GBh3QE-y426A9L7EC3GMw*2z=FoSr($D`g zOVLu$l<(KQ1EcFlV50;?-^H^$P5||@@@X9^X!UMBai4rJVq;)w@Q0=BaLfuVZex`E zEZ*>I_1&0E^uP&Zgc6}I&`yn62(~&2;Lp|fF9>)xj1bJ&Bu$&fJ-AI8X-w|8j|bmi z$I%BC@#er;QCSyr~<4ofK)hKmBQbNx%+(L%vRq)e1) zU}*j+z)k!hO$bM zqu1NO*D%D?CsV80n#Bo*a+Z7sv@i00FWL}xL6kYvaLZOzb^hSwDB#ZZG{%Ii_d0!cwB2sLBAu zhK#z`B$!q&?1crYVil{!|A^A->a4#_r>M6L=|0Mg@5)Y-AP4S6#u-GL=V`^c%V8*6 zCDC2$XnbJnDv%~kkfFuw?1bftc)h9b-wr)L_vywO$7(^%K1{iIzefCMKN*Ftw;kgz zZj=%&!ZMq!7_&BT;`rPlwD&h1aQ{hLQg3!)_I_U07bJm4^l|)FZ*Ncad31`;ydn_I zChy1H`}m;n3MB@*?l_jze$_kHP~}6E7~!j#8ge*_mF@dFdEGn7Bx>wbb3=>~^0^qq zvTLi`2EF`w%5iS97FMtW3WGbqUB?eg^uE2dXa4n|W3#pp;H2*YxA`?|5B{xu`J6l) z`k>?Z;o@dEd1!|~5o-7k&FQFR+og^V9lPh_!GQo5hhVHWd5U=o0%Z?&JddlIf+cK5 zR2x~%SW1d498uR0l{vg0GtX!g^t-f}lJb89N#~3f)gbs$~S+ulK=ynv?tij?CfI40KC^^Qc1U zG;KJLqBf6OMMo#~tVQKoQRR7)7Iz~G4`2T!S`XYQV4v}sjD#>(TwX&NiiP za&^hq{&ZPp^b<)t^Keid?d|b$V5!DBUixV-(XV?B2PCEBI?G@yHt%!0%|Q>onEdqg zjCpP=s+I7<{c?iA{j($Hc`!{N+JgRi>P%s0VvLgvt7@dq*Mlk7>ru=2{jZ0igi6}Y zRFd+x*VZe8uh&rCG)1&1VKe)(p@joZPS=y+M<$2O>RjyA=kecgQ3i_(A=;2Jz^Xr! z{tG$+GU8B@C40+4zl+NA!D8P~=)>BdHSXvd9ap2xb@jU>&svUL-#Gqzg5LL$tAa8r z@UaRp9~FWL6TGffPT*pab;D@}_H|3$2Rtc@NB?^m!jP=LG&Cy)TdC@8`jiZUqyN$0 zsLiPF+f^W3_K_h*rHJ21Cc8Kf78#!soEMd_V9aJiA*BBIHzHe1!hlcFq_(Q5wnl~y zNmv2PR4{Ib^?S;jI%nWjjw=mbNdL?`@oN&zJw?*71lq=lkqUzwahkXRe^7Ilh`#%{ zG8SXEvZes0@mr9ydn5#bW_^L-kZ?@nADgkcZP7WLh{DoZ&>!JIBl_5-OzrA$_8q#l z&q#X#FE-~Mf(LY$CzGytl+*GPl=m_RRTU)K)Oxzy23%~v}V=XZViohV`*b)Twc zJO6I5u)B2vJG|A}+c49?I%KjLb?bfOCRfqY{n+vw+%^QOb3d58S!&CPQKb#QlwkOg z_j2}8byIZp@n;`~_oZ3P{p!Ge;621CLAKaojRAQ(w<~wB=ZQDsFxAuhwJ9xQmbyey zLnka;FRl#W2WY?x;nZ{Bz{9gbiJ;!|cslYT8k+TUB*Qz^xn5n)iJaW};$UZAz!TXa zvU{hy>^h>U)^EXe+q&Um?0nWm*IaL~M=|(%`eHF!Z8}=sy+ziGWSij{z`@9K-}QLE zm+%uEwi9)7BFn}zSX4_LywuxxK3uL7;lUkgpgYTJ)e?%7Y6}t?=H6x8KfhUYTg%EZU;xOA)ET6ynRP!&B3OcDY`&*9 zB~0x*TrgdUbL{Pw7o}|@+H*PWceVq3-_`^&{EH4#wY_huh=vNqrc3(r90rw+FXXo~ z$~g8eyK;wh7`+kIYjuGL7TR9l+NP_k)V=R5$lK={J>JRh9o|UxJ=cq7HG54O`k|%b z0_>ib>t^qLb-JNHX6*-;VhAss+YjNMy85QC_Qc^6#l_*>RkG2i`WiTd`CCohb8M55QXM<>=+3{<)PO zh}*`8dG?DNo#VlgK*rN_cg0X?e3$J>gGdqjlJKf7U zHqn0HCp%@N+H=FoZFerN+zHkBdN3mj5Yz>_HgQ1s;)vyT;z)FVot#DAFr6JvTPwpE zYkE!U=o`0^Mu;MPe1MOeHd2yv>8rpOkbAzp$^h`QIP9QBk(>^vN54%dm4&_2Y_-Tp zD&THL0FzVKudTvc_gtsxAFq0yJ~DCG3p^f+xjek<)#alToK>JL4MEp0?rcenjU1_J zohHPi96z3KWGe*=SNyn~Q04muH`)fzVbyf-mpb+82ldeW9b|FGWHD-l*!&fY$zfUi z?Q%cLdICya%iRx2|bMVH{Iam1|aJjj0Z z(YEXQKFQkQiZ2d$w#31EJI6yF=I2lJe(dB>=sRK{6H3KlQu%7N)7NTmkKKriSffX8 z&^uQLMGp~-p*Uzzui5U-XmtiWzLYI!YGQJxwwxycxV;0n(xG5WOyb28WNU{%doeEG z{}#F5|0$dM_b=#k6QVoYFT|z`N}fGXmC}ADX|n71@{)y>gF^x}jxL^@tZykK>dNRb zn{t73bZ+sFM(5l|P|)$294VGleU%S3qtB7~2N?Ni{}DFk!~ySJ4|G^)gX5n17D#!8 z)uA^6n;xtN=wqoS)?vxCre}a<&LI5lC!qSbfy<72xQy`3`F@!;o>}Mr?NemrV6lwY7Fs7i`rtDO2?Tk=r@%Nj)q*Up=6$tLBW#}^vn0od8>Ff#(6}3{w%yui1w8W zjrJQsR=K`%h?niu>~ckCJwd=?%#1piJn9z%i2imzA#_ILM}#w>>}r}u)y`HwiBCU zL^oWNLZ!NvKv7PkJ z^kIOpyQ+xgegA!o$x-IT-{rLZot}kYIH9mg)g|Dd@L-wjyvC@>1@wYUvBSren#KnZ z^jl^9<7&s$;mryCsbqB4DU$jI2nvgzKwQK(Z?$Uq2*7$AW zmR7|iPrv6+^n^9*>krqKG+kw^ENqx{ulM${goV$S*2MMS>$zkzuCo$FfWO0PElFS3 ztgY#Y6F_ffKq&C-Tlt=?6N^o|yyjDwvpJ?d0#9#KW2QB#7IH^2Ew*q*sjCMPTA?3yKS7+1|3{uH859l*`E^nzR%13 zmS#LG8bWmijb|4HUcckoiNJ2|DH35X<9#BshnD1LR3-T;+J~W`cy2g*8UdNUfhevzSd$0FjCPr65-v1w`~EIz27mc0XiW zSmrc&U1F;Aw}-bak`v^o4S{uwk@SsgCggkQo$8Pr_Vg`8`1wqA0I>Pme5hkw=kl-O z{(<`I7oHM&MV}b5fO)iObG^vgZ3FrD$PpqxxH4C`gwjxS1@0wZ1v*v>yw z+}>u!&3H%0HC%e9-R)dcI!Mb~Yr`eG*V(P5mMG^x3w?UWCRv5A&so zr|r1cxDodb*Bd5>odvtWU*%ayH@_sI@CeSPw*`D{4fidO|x@@f;O<$vE-rRSYSS`NO1_~!%ciSLsG<_0@Gc$>rYb;)FA6ndP2*3lZuhZ#ZGfY!gvPh-gb(BGX*NqmQJ$uYg&fd=as z?7Wol5(jmEO@9^1DZ_HECCR@nov z)QlszH8ezNBzdHAG_{{ULuhDoW9>xxLyI87UQ~Y8)8diwXN^3r%rr#~^hV%S@R@V#UWX3#!^Q{GrMJK^SNAU?L zP1LhCuEm>`Djb8fgz_Kkps+%T(fzpbdT345z*b!Nnu#^;7GRcb&H>wguvh@@+btjH z2B;pu8AFkYG@VSe2P8+J$i7^{G`(CrdZcz_fMq}qmyE{1=`U-^Zq62VEnOY-Sr<{| z_5-#rV8U<~uWCf419n2!!r0muD|Pr6OGh_sRUOo1f)fWVzLP8|9pg5Qmb)xA6@vDk z(W6@&*V~nS%35Zw5CQP6m7LSQ<_`s1qVquo2b4D7;m?5m-g-MNuRs9rvu~r0^1d4l zHBXDuAGT0BgWGn^ovY7X-wogScWLv}uNlK2syOuvY#~2_7zn1lbA8M#Ak0Y{2p4ol z)gbCAG!u>mGNP!n%P;{#qv za(pcqzu@n{oE%WwtyaV`alEO9Q{c5?H-3V2p208pW07=qRJeD+{;Za|GNh4g7Ggek z`;j%yOdvq$TAu*02j}W~ITQ@gjyi~sp`19%_gnr3Q}+I|OWew;a!B*@-4Iwb10d$} zxZCwu{={8=KfTP`a6w8l@J3zY?}2=O?}`^$X{r$Dyar^4sn2A^WPUmle=S|CZC4w= zV#MUB(6w6J>AiiDtTNPt>3e{rN#yaaMx9?fB`PotMtkgk^Iqs0hFI7k;51*WZh(Y; zbtxae?L3axfOU#;ssAoYER+1lUxHE(5cs#wQa>!eC8~1b=Uq zPl+ZKtQzcDIgA6sNV=m9MghsMDApe0`Yrh5js@xl7vTgUEEJxLm|`*IgEh1sHEM|r z8)K3rUjz%-vi0qbi5qiDgQ7H=?|aY>^4zwYcddHYZ}uJcpswx|UyeB^@&>gx+A@V!I$?#*BIjiBL$I80*DCwPFV>an$k&`G*f0%-=oZQV@s4nI0`WWs_kwzmmcYo?O z8ivIO$xwMbg5?JDOd`1>Esf%8N>u;pSl@g@r>NHzn896)37Jm~mTy5JN_B+m{Kzut z8j=@-y;I}PAJt>43)xW(I!?I~v;&t%g(VTAj*JEtX^ZDI!~S@e2V{}vITr;Rl_il1 zK)U=9Lz3yo!iyCnh6+o|sO5+J>s3rng3ys)aS;2ybz+@ka_)^7_f+Q z?S8RlKF#iNsn>ZPa#)-`3QIjRvd+W{&w2eTv0KG6PF7gfg>cJf71Hy{oTZI`@i5oJ zh$PsR&OyWU$B#Xc%z)=EyOI78L4Vqh-90Yj%{9i8GklgI(NqTQ=zYU(g1U4&XVLoW zIAf%kI`4+s2LbEt@;q{sU)Z`Ck>tT%#E@;?Q9c$)28(>3p~`sRiEn$^^SuURYOr7U z&m=*KunuSroBHeI%Ec51co@?+j|UJlDrn64kcd1y$EUcyGu{ZNIX&S-<5>QKwCs)5 zb=Z8^9EdPl*aOHsh(ys$xY} zx&J2rp8AFr`Z%g0VM6DZ)A((;Rs#Mwwmmu2fi}dh92mrb#hh*0%tC~^(Qh74zD;9* zK2U!cHGY7Qt@=}ia$i+JXwBAGFOJ1VBWiV$r_nOD$9x~j_mhvSf6AxzYjri`b)1b* zbkm(xTX*y;R=@9!GQf}iqB=?8BXO;AV|gb%{2WdDP5&g z!^^Z9UXox?5X+ABTyJzdPiTX@%mCQ5E1N!>>end2ZKMkH1398dxRld`$`Q5C$(tlsB!I4c0O(g#f$ggRZPB(D1EuW7_IVb(ipyR;y4#C}Idln>2g|?6; zE;>_EzCZL34FwhWgF;S4kQNlK-z$`6^IQdH`^m6y1s0{xrRf?A1I00gjkGou>PQBf zan6Fl-hSBK0VWgbkqpaDyx}l}qKcp%nEf3ZlI3S6F&uqJ&zSO-9XVRMuLp+;suqNV zvP>vP(D533Q=Du!|3U5w4rUocq6~Tplj)OIkxLjtTG7vr{+i#FYDQ+1h@_)>pZ$iZ z3_0P|v83lK-Oyf-$hC~n^szc%%kq1noVrf;i(F)B6Tgd^h>km74tCm~3_iOF0xYXh zQxf~Idu+y-#(BX8)S&wPz0`1v%UPa%hIJNeRBk@RIHK}*XwMOg(r}OR{=q(`Z#sB2 z$x`ZotbAHRqpR$spN0|d9LO3{>0;!15QY2$6ay^kA;A4X9h#uroEHTSkd>qvqh9&n zTznDLYH05Ru&t{MiNIOPu!QMC`Hz;${U==`OI*_}ZY>q;?xyaE0aA{Pk^*#gd|x24zXm_-0b7+YDCBqs!CIQ>%-IC<_0 zbIIIObw&UU?$NMs6j~8W`Chn{+Tvh>=@>YX<=vS|EKyRKr+T!p{ehm2PiaL>pJjC? zhT+SbN8NSk)YAdxw&+i zvCJ2wr@l}_cY+kL=y`;a=SZ}M3b@+|Ri^iq(rLUleMMd^C5AI30c<4849byzO}BYp zIqCTb7EfX_79qrH1XTGWqtKy!5PlQeAWaUU8k3W7k%pDo2aV>~GQp2T8kG@-*8Uc* zgtdYQ6T_sV^~5lz{JpW$hLDm%V{gyW9*ucnc-KWQc|wxOMHD6q?+MLXs~$8LUQ-)b zch^(CgFD#z5S>PfI_-5I`82246Ct$a`gL;n<$y2%%u92>HH~rj<}eEQ;L!<1omI~O z;~|)SEY#u1KADhba?QozoJaRq_h46texvH4*@ zLWapd)%pgXB%o%UU%;dx$pQ7JHzy*@(NaxnFGU@~Yy#26O4@PHIxOQG1YTWgw_+Of zP1GIKKqkR=+!OqpVc;nI6xSnPt#V-q%a&#L>&8cp9|R&A9F47tC`wmI(!HGf`8_?B zjXd|qAm-PHey5%3Pg!)?EJ4gLO3d>mYUtzB?)0ntEjL_%aV&}PMW+0voVqAmBZk%? zxhsq7{o>A)jAqklOVPYB7S8;nEqWt5FB!VThEJ2q+JbMl;`<+U3y2n(@@wp{2 zjof!lsA7>y;L8?7q6FWlQ0tIV+A0=cagK^WQ|@12F4f@<^Akmw+WTLBu&gD8AQ>@< zW*7$CnVKtCT9X#J^}ebn!gzt*$m_!vVu<93v8jijo!ZC(=e$q%Z924Q!)11$$ptfmZ=$IAc8zOBQ8z|Pb^+)+SOBWB#ni!qM>aDdt8 zN2u{U(ta;~!Vm3j`g}|S!^Z~6+Gv5i2dB(nAxasifOr4$;b=L|5}sM7Vjp*!M(q=1 zVw?}ow8%0Uh-W#RiD7!-Kjpx^rdGVur#!rmYg z5VAwpJ9$8=Ts);Qjahr1V4N-%DihfSi<(e;82=CT1#|I-xMzOyJ!P?q-0x@fCW$T9Qfs zU+Y1us6FFqjBytB7{?j|W(-%VxXU$ykL$kNJ$3mB$D_(a?rL*TtYQ4Qf`VF7xwR`+ zso>gm;*@hQsNcev=z&7GtLmKpLG%MIs*cGW7$m+7Va_AQF12Aw?M)}p67#LRSLH$@f> zE5o38bIgf~T)?P^pv` z7;+DdMjl5YwMI=nZJZ9P+-jbTQamBl?2h%}x`o+lIm5frW5Y;vx*F?F!J4jMTI+y= zUy7*oXPd-W8+Bm~bpa0AEhp5Q?+4u^9jP&_b>ms*(&!It+U$|qwBg#cq1sG|r3tV# z@W!+4#$vkKP>T-KkwMG-$)h1|%a1wFi%Qd3Gd_P26jD!d6C8_SRpkradeC?t1f^De z_2)Y-6iz#Ex>85UhyMk`F@jU!vZdruKh)9eA%H%RtYQcOLqE!gjtx%SG>Fn8_c1F% zf5x*ah0txnA~AiLr28ezCR zd6J45-JAM2LiGJ#njyOQ3ze-J=Fpo*xhN=$5LZeA zW>+KC7K@;HBo_*!uid^Qj#=0|%1}K&JmfVz8z_(kOX0CHtm;JRG_in;aZ!*qQ?8=K zm)@yp<6;csuVi4dzIAKNjXfY{@R1hQzj?M`FupOAj`F}CI|dsFV-@3zCY$wY&BN3A zOuv5xBh7>0;(U^cn29TdsoY`GGn6WMC(Hap4*i3HUz%ra@_GB6M@y6L8%~Caak}+C zQ75+P#~yAMh&Y#7QOg)M5m&k#Ud-?1CX>#Um7y$)$l>Vha^@%^{=zC>iR#thmC&D~ zzK-~NWK+9uIbl^|K*M2*C(b#+ilq+*l93t_n)&V~gnxzGh&r)lB>CT=5MH{uUkYb@ z-wFRn1p`Q}zskh4j8_E&dPYIL9bbg57qpxfS(HP3O?<+^pTP1i>&6E#=Kvr}z|h%k zu~^2((-Hbip)}1Sh#6y08A^LSICBZOY5G7o3*7AJruok>nCR|(Q8&;2$7T%EjTCgb z3{s@lfv<_t4@Knh7;F@pwD))W=)hbL=BvfnR>8(TIR*g_Mw2P=>NO8EjodSE^ zxXgY72pfpMkp@SFo>eig(Fhup^fU6Aj_vh=vioMk2;W@4zvliVjyfgijbd&;SQU&J zTPGAC19v+v15O6gj4so`6nGooO`KR9FVjE3GayG;VEbi`jD0u7q30tQU{K?flEA+U z+&m5x^q2(?pm*-UM%+_afIX$MDVJ04OMztK{!Ygt_aia!V5~lm+D8vPRudwGuq+4t zY8b8hnJbopoJAB$8`XvSZi5w%LQZmiaYp%B(S2@@G8-Qg?yDoCtWIrhBcuf5Y*;Am zgtBD$UYxhn-&P^&R(X+y(-1Z62w$x-phgS{S-Ql~d>pUD^anLziOnpQkANz2^Ft&Q zMGSYG9GZ+2;!Jk36*X{6KmCL%UQ$6NU!=${>EVu*8eX)zHX}PDr8SBt`pX*##BL0V z4q5=amCjr3WD#3JEriM!fo1pz!%r#ivhhUeGRP^6uVi3BC!d9FMsS4<3L~3?H!d)w zTS#WmOV{UskiD=YJ{3z8R*6UmS^H1vSQfz&XuRuH{<2r1AL$s6-t&vO}lEnN|Txo!UAc*+L1+$6ZOyUT;i4BoO71s z+I1R$XNpY<1*yZcW<=?&t)({%K-Tr8NLRbWl1g+|FXzk}{d+dJZCjME^Dnu8m^(Dj za0n*&o?#YdsIL(?N%+7Q{XBq^!ye+%M0Ne%?{BkLTeV53ELWgO`d706wb^`oGp@4L zXBn^O0v%r?9eaf05MCz8{h;Bc&kr9qcC&cwEQBL!`<3&zO2o4#J`O0I#1l~97PX1Y z!|gD9XuxMV)4g1~%>1iM;uQZP0o<#AyKcuWCK8xY)`r4N@9MRv;k7#gdFj1-==mRW z9(u|$_M3YNXbUl_IL_l&w1EEk5`hS!W-ojgA*p84%d*zD$}bjx=Jb z)vM zEuNSWSnD^Lx5~afhK2iq=asBB_eaZ*K9z(Frk=Ct-}Zy6@Foy}@*~BhJ0UToLi9zT zIu^8xzM5;2G7CH3*N(3i%s6Ec3oLIlRd3#eP!|Ri;~K|V{-ZgIUt%KDT@(RVHEr17 z8nMasm-FH#J@tPv{>1d7qJ>95B@~6`MZvOCzvONjsJvOdzbCVom}KYhaEH3nL(Kj? zJb^y@V7O6ENmW9QN0}~N(w;&byS}Q<&5e<&5hC=!%sDogawoL#brdt+Y5n-UfZ?R) zU32kAJM>*T$3pHEsE5q>{iNs8U!=RO<9*op)JUmKWLU`3(xDfknog z7)Z~A@+>n+f$nH&8`!{WYG5~ly)c`hdle)8!~W@hn!4)h%6CIu1$3?A$2y|R0gVH6 z`xo_T=X)f;?*psrhi6)CNc*PCy`V5pKq2akP|wAWc&Nrw#!iAWL{GSr2{!N>eJ`PVk4xl0y5Zu6l+W8166i z)GpfgGVrT(eOlT$RWkT(yyi!O#exifh5%e{?&m8XjNpx<0l7MU6&{b>O)^6Tk4Fgr zf?228bbBvR*>MSeXXRNjYDa9u?pr#VG8%cNBVD&h?kw5Fas0RA5kM6Y;U&;_NQ2yP zC5XnJcjc;sUp1B8L+$R{yqePHZ>@S4b=7xhVPG!$yN_HK!Zy-q z(y)=AkNull_v=LJbXZ>F^DjgJd%}71aX%O+8W+5R(lm3R9Xh@IVo_qlbtc3%{>vgT zl5YZ;Tu6e|>8nK?*R{A9FvA+Hpv9`}Z`BZa2R7c8c+eqs@tH_$C*0k5bf4wtav8df zuWuqV@Tp3?iH)2IVO#P-%Tj6u(X+vB=sFT zn_rde@TZ&LW*I3leomU1IgV&)^2H;-Osm4;(oh7qVMe@xfY73%!ez3F-9?u8OFg<) zs($S)VR$`ni=EPh{OOjAPK3x|Srgku9vC^FI%;N!F)8q-)smjc(Q+oLVddsW;+-cyftC-G=Eg(99OzL)=4H+eef+N{YfnoSU1wLpQ+Ewjm%6&B>BE;$xlC85FfN{I?q# zsHw-8^=v$LLw@*pQ5N^6GBS!N-)e;I&n3jW_SL9)(e2y7ks9sh_b0B7T)qcA%p{qw zxQf5C>0LeDuC>?xmD^xaT32OIxW&Iej^ozN5SCMuQB+JkzDeYIe^|6zA1BESW+Ccy zEXQMD^*fynp~JJ2DpiLjCp{+SQ07T2svWXBdxE8%nRpbfOix84e>pv+6JUfOrwy~5 zJm>EGuE-z}$41URlNc||ZE&Z>iX_Mv5=CleWz@%ICtjOmZUdMV{vK~tb`{rcriL{$ z86A4uqQ9=KU_Ll&MT1k34B+|PF)=IUR*f{C5=A2OsJtJPLLXx^#> zl@#b{#lEc!(+BGt;Eo$WvY(DS#+ zuzbI9u95y}Ofu0^e7?7V!IkPy$|^umBmFAq_8q8Myy9Wf=pUT$@!g%Jn0At*a?03Vqxv^*L-|2W)YBpnnr{}(?$d{oC*MO9KtSZ zd#UCiQt;P31kwHXz^@^9vRySm6O80n%)Mk1eP$H8CT5?-wh$8Cv7bVUrWa*4n^wM~ zDzH}M<6c!nT=N;_OI4>?X5~tVs$T^eMjB#DM~@?EO(E$Dc7Gn#jQIP5Vl?)=5D~L0 z%@=-JK9}h$9i(irRC#Gr+qc8)wA81m<(!@i^~b9x{EwN)n#bwfPqtM$weOmAtL~yB zKf6X9SW~xD0rA^=!tI*ZEjSed1hlvYbT7FK_X%LtAP=5AeiO}>!1);D(U@m@YHD>lACPL{`(fJlz?7|^&?0aM`9{83Bli<2?*>A<(|1dXQ+yk0HT0I4z zG+lRbzB>Ytb|wiOKRFir)p2I(-bDtKb26uBF0{Tvt2{Xi>Hs}8HuKOq=PRdKTkQ}f zjZ6bhw^;MGi_&Q02f;uH6zU){HtW-w$mF1t3G zi6UWU!qQMd6mVlCVr(R?gp&SIFt80NpI8(yoZ>j*GPbzqXf_?|L#mtWgAcC6js1dr z1}-*4TS<~0v z*cKp{^Yy~OlzeC&+HOz)QNz70)XERK80Iv0 zh^w&UN?8y@KM{CcJFiDs2+qUJY2L7&SK zVv)lULO)2|Ak3`rOe+`rg`~u^KMme=H$lym`utc^5kha5lecB}lk?wrJJS)I^iNb_ zsz)OQ$_%bsE(P`?w-`jFbtSYPLn93xLJxz#HJ7vhbi&lw31CrA8HjTxK-C+k zl_z7=*bD^TYrE6cst(Id!GaPHr?gYm=EAX zajm5m&1lTZ3BFAKQ!W6!8q{%b3t{nY%+)slcpFq8e5n?t;lkWMN5I zas52tRwtRM(x0HovucP_mw3$OIQvm7!A#1> zwWL}6(F$b!&-f9wv!#t&@+w3W7U{;#TbEV5$FD+`Wrs^u_7(xgP~2Cyck`Pqmpu1NkABB| zS3GW~$@TO~7ye!{Rz6+BTf+uRNY>UJa;-0?-9}eYS-}lrHOvUmE;-Uk0v?Ol#W`v$ z%_DdjvxJRF;Z~(?NpjD{7^~lW*c0To=~mq9+WN;VH#cN@%=r7(q>`@P_B2HH6Z~~# zLwmTc2Mpcu-2yESMo3h#eO=ELJ3B6C)dxxkO;AXS z{?*fccP?SS$8=Yb1_!1cPsX>pmlo2Ncxa4-IUQ&1Ren$R9bnW&aZ6uC;qpAaZAEUV z3X9PK2SH3_qw@1Obes{7njB&{suk5O86%+j2e{AUp)<6EBW(U4UAcsu1UO7Ej zf>|Isv#ZFUMxB2ks?PUxyal`HxOB6j$^*_v)`{OQh#lTn6Z?F~>&)~})Gged?Y0Yf zRJLah{X8Zfn_{|!t*;3G7_>3xh&~=1uPa|PsS+6UzAzh>w0H&7>Ep@Qur=<7TiZKF zP2R=T+rJdX=);%Ge>f2hBNU`(d}~Vad`j&ipUj+mx(4v?YDaHb#;Wa+^yn@=zNu^q zn1!(!DN3e;AoO-yf_kz8=RVn^&yZ(vIQCuvftNjm2n3I=8q61~N;U7TUHFvKhJjZ6 zi%E85EMsMr4L`Wm>8XzzKhq(n<|`k6C8yK3N2oB5+m58Lp-BvFx=?N$Byai@>}w<) zCMjeHSQtu&^(CKwy1&TRX50Q^%a%*K_}TrYkd|l<#zF|8Hu5%OADZomZsG2@zzL3} zEQd$e=U!i7I=$%>S9g@;kb$D0*6fn7rTdk=n~>Z>d`zSjw%0X2pI1$eV~y?LRBRpb z*Lg^`vO&LMFK&li2kcN{aI>fgVr};1gqMv7B(#gXyGd$elfHo=FGy*11EKFoj?4vj zVYDZdp%&kQpD|RV9Xk!_>t;D~I~p(g1}SMyG(?p|dG4%?2j(0|WA0bH3WJKNt#-oZ zTm=<{XP%AtHTy~U=PAW-F?FY9~liO zlCXgdTy8Uc>LYj9@Lz;ENYGu-E5=kv#RA9++$+B9;NjP}+y~E93BjmO-pAZ(7s6h0 zmo(6Tq@4`s(zW~qF{nv-KiqO47Sq)*B7rz~xbIFqdH1NTKQj--(d!H-^y?;GxGOYI zI;iI2mg10cDSV?kd-w+S3?{TGpT4!M00Eu5Up2SfbU*1m!I2t|8WqS3x3uQ4^GWMi z^eXp3wIBR zv8#kD8#nN9Z^KOD0iyAIbkK}NgPk$ZLf*cm^da79&#u$bNI5dlIf68uGVPPrNN<5; z6Qv)1s$4&t5$&J^R>2(j@3gbTpU;xAHC`TAL14T79>Po0Fpk_WfV)sFymka%ERz*@ z6%0&J1$v3w)7wc}Uqxzjgx4wiq<(BQMy>iPUtMMTUaPfRzF9kaDxV55Y0%ub;_GM6 z9!byZ&~%B&pIJ*h(4OaOYgFo_?~47Az@YWh?N{ctWTW&?)uB)?+i-)B1YMZxel9dmUFNuOsn%tad85 zj2PFj#|}Y}8|Sf%*5S;w!$J)i{usOcK2P!b*t47((lNF5=0{Gon*&RE@zEmHNws%l zPqQjz06VxQbaZN;o>&t{f8V!k$;YNE(}8JVL808J^OvU;)8*O2_Q6uw9rQr61>ML0 zvg3ik3-mUsh;P_-CfkO0c|YOUqWq@I#i{ONV+qcFZ!QJ(GR5h^{uk3r;Saw{RD@k0 z+lTd2*7ISnwhOv@%#F8uen!K1U4e|(ps}imyJ8YyX%sSxL8D#5eD8O|&@+2Z7;v`z z^#$1i;$dWZ8T2vur7zybtZI&NGT6-vAA6Vfo zg1!_RprX`cfMu(=a=_NyJ$1nLFqa;Q&zr$9Jy6UX z2ZQ^(jljLJTOT(mQ0&5`JjLzLG}AAL^)dF{+?;H;g1}_;%BuY2rOz$!(|+Pk8H-ne z{HOe9)cWPCU+QXekn{;tQzXscyl6oYq0;xcqCdOxy$IegbjrZ0e^7GsysBN8H=Fv% z06v2bg%6wkQi3c_D7Dw|4-(#M@H*JJ6EJMR5GOBoKH~hjy3S8}Y)r@Y_5Ga{LL#}c zSM*NWplT>)ulbKVQ)~){Q>y?jZxxRBGJ!(;g8YyKz+@PD5b&RazhDO;-UI`r5*Kji<3cAf<>zzd}f>b#7Re;#?hdKFdtkz|F@ z?e&*g6RcJuctp4Gdbj^0+i}R`-;L})ULD|fB_^0rbe?In``Whk5^`J#x29Lcd<7N- zEA{Z!nr7%<<>jZg6t3e}U3f>py}kJR<*;5b+o5Ck41ILFeD}=MkP6VL{<`0F-)0E0 zWHRC>!v0i1BaDFdmi)S~av`jhv=ltJN4l8NNn`^>%dTd6T`Bj0TgsIMp;y}vqvNAJ zh;vNyViLzIkaiIV&3L2lLS&OTu%d@LC2b?2qpH&jG} z3|K#wJZuXbL)hIfJqNx@NdsT(9#V zix#BIiU^dNT^{D)NzfZe{XUZihLEEBPPKlP=3*q!5FF%kAW$om%TR=$R)I_Q))~c{ za_2lDet(%!krN7G3w4j>B(QC0KOE>hxnsQAli|9#%D3?vOJ`bp6_@gte4f`Mo~uSo zOcE?g<0jBK7p0nt^cQwahXR7Ut(*De|wSTq_T2MmDL3jIO>gx{Y0^|IPhGvBM{|9}{PL zaQk2=tW$IrIq0DsA%rWVF(5Yr9=9=2UQZo^E4q?e>$AhI=u5R1WW(#Ti0L8szYmgs%P)5CN= zfSa*FYIi?*Tk@9(1mqjbcX1K5-8&)&n-*FHR?5b9@rjce9a`o;y=qb~QW{DP;ni{< zi*9P~4c#u62giIW7K$e}xef$BRJKJId{+oYvdkWU!qh*P#^m(zEKkHAhYBHE(K*i@ z+I);_Oz8Tro*#L^f_Di?K>5N1n0|rh_%Qr(?7@yRb+!e|#mVk(nf&~n#GE5uU~u)o zE2o>NtD^$7MN;yYFSyK8?FZ}5gXTo8&y;1;=~OtL|JyqIhJsSc7V$ULZz` z({NgV7w^wBrS6jHBfyiHD&Y*wTYHkz-<#)I7czxpl2}i-tjH?_MN_{?oPb(g5SLJ! zR$?5K|41$UYy}rYfw-xW8C)FN37^bOm{mt{)otM-8S?wBOG8cToXmrVc%^iby)iw8 zpQRbzV++O_N<^@R_gt`#Ml;-?UFecD^rI0;w-V(_b-op^dtKe`tbmK++vOSSrXTqR zQ_ky^{Ws7V>=4WV(9yT~vR^DIu)ZX@uS$wdYA>N-%Q+C;o|HPc-0~5vV(T?t%Y@kP zN7x3}G8A`olq^TzGRPrs@aLS|z{*URlwYIiEAC6#+6J32oz$L#-OQDBs=D9Q3YvW@ z+8+&BN2taZ{UrRJMf9Ju=#Og_m)S>-&L#{UWuIk4nKZZV7mw5OtPPcCnqD|xJIU`C zsYvn9Dpsn;u!mH8`1YbU-n>?P+OmXZV67v*q+dN9Ed01#ZU==M0;wUb?3A?HFYPT`C#dmCcfvC%tlMQ%sJcT|uQS?| za?Al}YiRqR7lh0U+IojWzRjJ1wUDx){b+MtBqsEipCb85qR%8DXi=t(m##@ADW>(| zbtp^sQ4Og*zx(BJ!j#Mqm!$E!$Dl@gyZOVsD%T`aygs`$sTawt9s0^E?&By=_RXya ztM}LD6xGgi5DO?yK-2BU6=QJp$T67R=eODs=&!gk0F(<=vQH+s#Kw1y_diLw2M;ZZ zJuX*^f4kzC;!6 z9dMssMip%r&(2V()hdI#HK@Uu`!zfj%(%N5VnEH~ozV2VQ+kSR3=M1wp`8sK+M1CI+@AgGi zxX|xEZ=x18@QwCso9KrHo_giIbGbI8>$WiY%WZU7M?YRluJA2!(5-a;WstNx#ZXjT z&YykbGc5kA^&hna4&?OjuI>?jX^5EuNw3W?tD;Qr@<(1K8dV3YLo_DQrcaetgE6e- zpj05!3)KY|?)51sBu_QVrqU2!{nQf`&VC%|rzrk;eLVK|j(|}KDisJg%R=a8h1?JE3yhu(l@)GC{)K5_ zboOm2FA^JUITT;9^!S{nf#5k$Q%P(|tJ; z;gL0p8eU0X8J7{nXzFu|3cjdNGY-FA2*T?HC;FC`jR8Fh`K2=}Oz3Td<1aT=7B9Y} zKPMc`pUQke#dWTM--@ce->v8_a~vD2Z&!WeQXT#Re-+sosfJzR=iuk(eVmHRf#C3+ zSy{>Z4lyIPKI;S`1rkk?JsZ6|jGR1vxG`Kxs^CE{J>FqpN)*ypWtCbYwOWdqE2}D+ z=R5cPMM}#!{Bj9Izy&f`99OQ{0te=gm6*}L`a?(P00R%Dqt#Ar=1ZGpc5p7s*qgL6 zjR&~850VP|T84@yk(+%QlQ+ATwUu?;d`-4DQq3A)-J*QYN1Oh$CUnmO1rT=xr*E>y zd7pwv4`=Rxgd;+u0i~kEumz`v=Q+WQEfNA9^D)I`vM}Z0&eN0BswR7{H-#o$6~#{3q*KrW zMCMOwyNcx>E!(3!3#Vsw;N$BvUVhI!-e(n#rLP4yU&`CpEb0^v1G-rqpw<~S55Nv%jnS1u}gc+%Jlsyl{1mT|E^ zRco}syoWUJQ>~j~y>b1a%zwo~BF#7;&4hUD%nbXIzjYb$ zsS3XA>UulizfuzPu+}`=KIEt6bCzoOP^3KDK9xo2j@SIemg#}NuI&gM3$^$8^S z);?lI=phpkcs1<(ON#=oHz(~B2kvwgDlSdY(Cm7NBEykiZSS{HNC zRTx0upcqZcYs2KL7iuvC=yRNZp7*c{<`2_5V>i4MiBp5pn~DgMY8SRf7{>_(@h@4J zb*u33KMPzCBz30@ILWyiYID%+RrtqIH=1*Q^zl@wfZTUAJmAv6ISt{1{ol9$&(#Ma zWc|cyU+>yEelzB;puij1b_cyt_&)~Sar5KyvE0vOqh9WJcAHnv`on5hC)mG*ztBf_ z@ATQVZFU822a{j&dFdP!w1G?x6I!^v1w^dWR+{d^A>Xm|S!n!bS?6EHhJZMSO^?m% zNbtEy+yDPMosr>#F-whDmDWDtJ|ltIhvGg^wDNxy_&-+xu#EL$S-Ko7+zv$0l7VUc zFE1Df;|QYiY4F)cyE!IJLe3EAN7{`r5dr?M(z>(j99IcL_pWGaD*f3N%EGw(E+|*d zVvoE+8Uou3g3$g7VLn1WMSN&Rxm?(^zAq~ z3a$9YRh!RO&5z)5a#!}=pfl(pQUTz#))5zIy^s2Jls?WV^i=3s#YfEt!lz4UasK-& zoARtA|5tV-U=dp!G;K@*3j1gG?6ldKWnqw6_w2QnF>6g>&;vjN40$?FsB1U?-(QKv z^gW%hd))8vg=N0ZHkZ1vPyDw!nFR|8UC~K#CM%BT(=J!&+q^c7EPD}lv>jJEgp9&b zzSLUl8#RGFu4qUPESM_dYj0G`E*^zAyxpfvr}y?`Paix zfCQwxpE$1__f?o6VuEc-qTn?aMu~kRtO>>62_;WK`J%tvKczg#e9&3uzxi=N&-OC+ zQW`zJHO76FloM8W`1I$GM_C;v9;%2i|5rRk$arStKM=Eh5NK(|6P;zO<-k>+eyW`= z)^t|9{$6Sv_K%Y*-<;yyc@F;P4FU7fMe&XP6Gu$ur&nY{(2V{kk&HhS5Gdon4sxUE zHVQQ&+wq~VJwN~L!FBlW-iM80mb$u~OlMIWi>;z7Cx*X1b3mqQ0UG+Nlpl~a}e4})%9Ph!(dG)qg3v#SQgEF{oEiQotQSD zEiA+8?L>O29?;7OHRQue99vo*50FTj72@4USv@;X!Km|z8WV=wc+?m%?)xj1pE4-Q zVszR_u(f-t`-8hzMOYTJrw8vP8t{1U=T@{|73if8B+PkCY>1P980iBVaD)22`{-vB z8R~ym37%|&n*W0&+vCe&f(r}#s)8A<5D0i1*k3SVlVfe`S4lRFh4V zHy{!O3>LZpLXLrBd`D^YuGxyHid8Rz~{${S16%G&&?lgpU} zMs&zYqrCL*hBE0hXK-|9DEfG7R6$H`IR z<<55wB(}Y5l0Gkh+3;@=BCut;s@y6Orbp&gS1v1#fBkUOv~5M2UMGGNGey9&s`VC) z-P2$mFW4EA!VYYbT*I2rx^kYzFN$()9e`Le>ZvL|w7W|}`1suXYm3~r#n5>v8Zsuw zYGYQu89G&cpF7GcIxK8J(XtUt=PocGuD-SU$d=Ks{detxS|OGB;c2O1i& zxtZD@e4mAjIL`trIL|n^8GHJT9$9{k*Tjp%{2JU?q0%&Zw09Q1^^Q}YL>!;h1oc-u zNKy3z9+RRUzp4%Ee&h=pm? zD=Mq;Yn5VuTPM!5qYc|JV^_*lF@W`Y*6Ol-&;M3>LB~z{THSeH zY`&=uqdJ65RS}Z2)v}(^3*BnJ-+-86j|r7=lAlM`cYTW69_DR?3gr<@vftediJeXCdE2 z@|i^HFJi3j?)h~&b9dl4!{$5+mT>5JQMn){(OlVK0fr0W9re`pP| za=&r}4)=E-i_|Y027FsE$KSdV&G*GEz&8&q;;(glbiA((q4F|zfoth|X(pdGO(so7 zbf~OG>nFJke-1-%zp@7o58Rw+#*f%}5A!j10$vgzJ#C$Jg+jP5J=zcs(MQ#|@z;gYd-9kO8|Pyb`XA(OhjQQq+buKr^xI|q zB8B}R*Inv^>nPSsF-hLrijsVCC}ou}mH5cOES=5m{V7LhS3gpB(_nt4a0875EK(t~ zku%8|bxAl^C-U!uY&Ld;@-CzAzg@f>`hF>cj|J@cfma1rZ)UN0-K*&j*}5I(TsN za#2DT^?&UKHLmSw7mHKCuV)5o{#eV%izxK$!s}&pG&=0KY&+MG zwQT^J=xH-A|HvOpBQe?2;$6uP>0||Iq;}QLSZd)Ldaqp@n@UR4kEwOA={zg@Ch69Y zhFMld604J~PvGX3t|@=6jeESazxGi0Qi?yvXN%5RW>G1wUq)q>dT60%1@`MI1m2{+ zc&eCwDAhp4ukb$I;;LN5y@EiBbj93%KngE{TG`*k@zQ}rz*!W<_jCvUG|x|63kSuo z`+3V7mNlavNFGX0Pj)B>7-TV%tyg=ZeD%v=^ISmNx$J;-vH3?KGPZs_zbn9AxCf3n zYZ{LlhWhCD0qYBq8z&PPsDP8dUG&A7qU?9CR_$E*c_yJYkd2*bjw#G^JY=5{k%qDX zM-g1HoSu|yIB=)?1p|t2)<|X~(BgMWW?Y@Ej2mhSX;P^u?{8F@p=NSqSbS&WOUcs! zegr|3Bj)0rl5D)WRUgEbT1m zcL2BI;23PGm|N*7my(L=J#Oe>WBPG*vs`qV(B8Gy08I|y;=aLcRDf$@A&zm1+qZtX z3-6Von2B3UyU(&^I1Q{9vM!Ok1B`O_mzux)eCz9oy3c0f`L_X}8D!*q^4_-OVYS%{ z<*60pp6qU5yqgBXWvoi8tSQ$uPEENFp()?Gy)IRMz&$#tyeMH`B4;eKxGDYpG*rN) z35%rWlv?32R#`kM#Xd;eG~au`ap16V-WM=yf6lgt8_E5JV0cSJnE~~Vu^&dY&A#zo zZ!RxWDOnj~`mlI`3P>$>*1%v23ntpIs#z)pJEabBwlXKX6#kr3I6(oc$Y+2FS+4R; zX`xrgB?mCofb&;9ZB@Km+>Z~cv>dw_bxCzyM_!?KywNLP)_;zYC;Hw~*Hx)?D_Ugn zpnA2_kKvy;5Ai%WS|1tJGWcHixcp2?NZ8(j&~($kKCxrfm_=wB4*Fc{<*R9r8}y* z_ZAh)iRe{8HV*^9QY^Cq8z6n-$kt#cM(aE1h&6_1R@#Mnr;gP4G&t)%ofk%1sjT8T` zv&4!Ql+OE&FRmHVtl&Nswuf*6>h_sH)K>pc0Q4$3k8Z`BUdrMrW#=Ku@_f9~4M*-b zGQ-ThQ=JiVupO+r3>z@!N-W2Yq*P*WmJ|F-j2QI!QI7ZEtLazh7eN#ZI{xr;|Y95!4pQPq0vs^EX`d@vL<$Cvma$i)0S@50BiBnoX?rC;F3p>>f|^@tJ3i@+ zKRZ_d3F}~%IP8;LHNcaQh^fd5|WKgFjNZgVqOnXwWuxu1uK$V}4N?uI}5l zDfn$BdXbk7N1@rQF`t2;&a0^B@YM06<}T?^lNwf06f7q-BV{C=aqJ zu**u2&Lm}l$K5A$X-7FGq{N^3BghzwJ&qC2g1-<;2Hk|qB6}g-im(INTpDN0(MJxU zt2~w~`DR3{p|;rH7QX9i)vUIEkgX}($?BWGZb;@w1`+wiRU(~dt>s@f38_yj63>Kc z!#1rrz_bI|-${)^K{IPy*fImSNYZJ|L=lMrfVn$*n1u=cVoT(&9u?Y`Lj@55s^a!K z-GD`G zZPKaVLvQrK*~_%~(q=ul^mJn~j@ESb{pIkH(K*4!87tzLBfKk6+mH)ah1|lXIxFO7x<<8u`?o1Nycd?C}bfiQOY8V8D$q&h(BW6g;Y z4JG(z_hg;&=vfTkt~Lc!7|V4B(T3|Q6`D~mhV9mjFilB zOG72!UeM1DhBEz~8NSQ=n1@q+;fnpIg1TjJ$cJg=$XU*p`W# zna)juyMJ?@pTf*#Kn%*D9s=KMAUgzWsZM^J!ilsq4hO&EAJjl}b44>iB)3CgLp_MKR~wyJD>3V4YqO){>B}J$Hj*_MqmRztH1{uFxjaZN>GcsNULCZY8?&bgTO! z+BD_u=i3J17>RpmKy21=82fONb`;Z$svuw4)t&Pi(*geb;@e4=f=lhRZdC4>as#xN zyhGr&+UU)We&8g|H~jTD);%T8<6f{;iIB?folpi96v$=iCnBJ|(YOE*lCtg@7dHl;i$QhC2|7`e=0A5>b<_#|F|-G3z9cA8 z$Zu>C0eTC!j?f3S4dXIdb4GIShwq0&m<73dr7zntgKX>-2)q95`%h{$nl|HZaF7Nd z@-8&(8eYJn7aoea)4^VspAI{aXBRujTnuTknJu#>;oJ!kh)@=|h>uDn00n7wjGR!X zRlV+A`wcMppHoTfZSi^wVKb~~N<-IyeR?Y<#s!ROgF^o*lRWL5RT7@+qGCkw+jW%x zu;E8WE(7#jui=8k;9UHTzYN#;znA#fvU?djh zA@-;@VJp7$_zHNYHX0Ho-j9O12unH;YuoT!8>$3G29!T%;GNbxpGo%64xT-bXb6s? zZW4>D&?B&MGxF7JK3E_5m}DaM?v0*B93t>py9mWbM&}EaT|2#nbbr|=6Rrz>U(sb= zx$WC&pGBwIYAG&a_oX+5VuddHYGQT(6hW&Cge&pwx|c&X!Dd$7+tu6!8+Y7#wCT{-d zx!GLIO?j@~xt+W*V04akNvlizbA0E}rz+xTVe|Io<7^M$g;l;rExxg4qISg%!_nf3 z5Q+7oxcC~acJY}&(Q+0yzSlT3CKa=gi-9{gtj@_(2#IAu>XmA0l-ZbVu$K?&DB#L!og z%C1GBI~(p`b~)VA{0?EO%k9_D&AdxvNT`&0?64P9^97mMKFoUEbhcd~()ir5_`7VI z&T5Oq{S*6~-Lco>@8BW{no!qWMTc- zY`uk7W?q_4wKa&f0o2L7bO0ln2YHux;f(`mGeSE@omdNF;@1dQ)YR<%n{?*3K*bTi zaga6IMwY$aco{Y0mlHjykkVDc(&wVmnh{XWCp59Le_I&qp83JYyH!VfWukg(kP}Rb=HJ%^02?3y~&Zhv^&^O z?ujimiPPyKPo?bAxbg#w#z5Nz`B-ziRVF7!K4L@{{^c$pqVSWh#<~WKLxqv8@r9mveQff9ftH%#ADl z`CL`pJqP}te;&@E-+>1)u-XPGFaBGNe+>y3u5+KR0{Yj%WoRH+ybmTAM zdO8qZ#!JU&U)$2YcHxy8<=PHz%oD~&W`!`}m_baYvwDVVu4Q>-7~GM2&oD`>d&)5+ z$@4ibsoUNsw?g#j`S-lvUdnVUo$Oot=MY(=`ms1`Q07C13Ca&OmaRF7Lp~Ml>c>(T zyosm1-HH9pSKaiXika{f~Lz5HnLVcPrv z^JnIuL)F4&@wYReUPixP<;4B5SJ9P(;wSf+( z7)ElGhYQ!x1vE0A1r^U+AR zJl8*krcnzIzh-gZPt~4qK9;IGw$XO8sq5}klzD2PrOY0SZ9Tsio`fvk8xxIK^?b$M z0G^SR(2=M9^V{9zamOvwl`pFy<5-=K3bmr-%FW=CL;}sM;BRJUb{8~toTlCbjEc(= zjmQizc?m6EBX+-89az!c!pqK*?q~lj~VBQx8Ny^jA+zDhq$}44shQcg#irnrLop0~24^o`ixy5( zhgIH`Pkw0fms{>|9Y2FpX_xoxk3TZ}!C%a%r;?(WYiNCkzuQ{{*aK_D zKzE#KC-3W?%+9BY6CWHQG;@Yxr`2y5WMLk;gl{`0yKVJCSl>%xT1DbRcVG|a6NBmV zFyCN~2;D`FG;Oe2#XWr(b7fRn550`FxA89^Us8>M^KrKl!4e{3&}rIP#WM!x)4d^& zh3K47v39aco;V43y2`RUhy;H%rn@iC&kDPd7UXOx>giu_+o86-4LjJa7D4aAV3P>? z;(+J{OX|5^=|~dXH~2`nP_oF_9nxW4_q6L=5dB&Y{+?km{<0PNHVS1d085cX1pTVz z=c5CRN2OHpGs=%y;1B%Oi1^&F-ZC08{O#ap@VZ;9O z(ZFa4Q$mGt-&)+Gz?{7DESL`~QSZ~xWS0{V{#7G!@}B;QO3sd0i32|z1n4l9X^d?;0TLkdA&H|Xk>FJE*SIUl%>GBn zYwz$@LJ5OcBj3-`18GyJdzY{R^CmMpF~1z$5~AlQq+DdRO;56)TYFFHS7X{rC~XeC zlQ$A|6^Lez2t-J|2AkLgI!$`Xbky~#1D}?WoCEXZI1Y6G`on?b{gogHtN*zZ-(~-d zr;)#ZN}xtxi^R_PgtK-th&N{i#mJx9FciP~LsMh;zJ3cF+W9?)#DHS*JxNaqzf54) z!dPRkT#;v-dR_m`={+po;!f22qu)E+Y7sUS!%mRzC*-|N8vrrm@;Vz>sm|$BA5U#xaJIK7I;HTB$ZXTx(Gx^ms(Px%_tY@-gn& zO|iJhMB}Deed5``v4*iV*)Y&3|8(9^;LM?P)~Z%nZ%FDFM}94n>mKgRoRWEL42CZ?yIEw}jk>mG1FX$eSVL@4pZ?6c>@TW9GHS z1xy8THc*>KCLdiGf1XGjSUw9fEZmmA!x&fl-n%gQ!2*2+HyH zQjcWgor-%|um-NHIV~$;hz`_#5o!;L}XSw^M>z??kZ=TmE8MA+X(r^9K% z3+fNvzGFq$ei2-b68E-GXP-zRuV4>ba#R3&lmm@+-#h5d!5#vCXvU2nFatSkv*TKY z$Ov4^No1FrQgJ5xcL1I0-{rEt{vbN zt_99sjNrprQeIPKIsVAFRrf~NwHmH_i0ycrI(y~IaU*ZTLSxV zzS2y8fOPTi;5Rw-rk_*23Vxk9T7bn@(lxp+4yx%hDTW`70f{a|mS1&wGqwb929J`W zcD&z675%2$%*Z@pUUP1~`RIxN=YO)E>=|uKOn<&M^`veH2<_&u)Vn5Yxk1rlXR|r3 zoZ71VjX3Ig2vg2fY}s`E%_eotwLGjdIr?G{o{56*DY_v@2_CnlX0$c=qa)b#Zb{K$ zVu>D8%6v41N?ECJ9VNdXx8_#gnvWJ+tj?m#;@-Q^l|I^B zW}AN+`nOZIY;>(b(8>f`UDN&CiDh0KpLBL!46v){IQ_lSZiJuu9HPC0rXO1%7+IP` z8>MRgunZAeJj-5gITwQ!6@3kw_A;}QrIm@SvZ;f_Ayckhln=0mCVo7aed`s}BIK6- z;~QKw#I2hKXk`BCjn~LpgONz-^5s-o{Fec|d{IlxDAImVuKw~Bant|4KVIbQ?7Zk* z>znH$pBxpklKW1HT$EE*$J04xK%;q+(ZG_IwA;}UM}c|PCaI&+s@lv$D;w(h0T6x< zDN0{EGZ~!a-kqAvOYlr^W7#MIRQiF#;J}G`OCV=JZ!0?OvS(b2V;NzzG>%K32k%d6 zh_5vE^t}vISrs7YpJvY@eQ7bAVh6|2$Kv*G#ztk0TOySBToT8m-V3(P(Ug6684=f7 ztH{ZqnEFen!M63T^BkTPQx&S@QLj%!$XtCK;Dq^0IE(79s*=cm@mP{o8xE0alc z)FXux@vjx8Q+#Z-OH<$B9Rg96m7cs#)xB1HWo=u=Ls-IjYU}YjZ17eBdjr4g8MsT2 zbm#z#s@zZ~i!!>ucQG^2wgx#k4dsve`|2p`QsmuGP!Wf;9}y=dnP6BF7*qg##pfPn zZt^F~*#F*@4AHQyw|Q(udRg2fPy}eg|FQr1vWJrSC}KbVMg~%RBwao#I5Cp`?@o;OU6PXzs1jmDrhb>p0n6l%Q&$cy?Qbp};u3tEp(7~#L(yAQ_6C$;R=&J&t zs)c_J&!!|)PHBeZK(cNmKUG~mJ!|T3OLI^bUC+UO;N|CK_bko}(=mZTCfDW3UJHkhr4V zRtexYhBK=Ij)V=Dkvoh&G7p(V9<9O1tqSt8AJc6S^3DkRvH2LSmQ>(ASm{2ejxRoM zuUc77aXt$7gzXf)fBTy3dcV1TFYa9W;YxIYD=HQ2Cv(y_MsB)Q3l``vD_$L)x^ee0 z?8EouFw>HJlLq@ASC&AI^%DK~N=Mgpt#(n1C99O{Rkw>h6QFk-A-Rr|ta#8p;_qor2CfQMu)gt`8 zY(P)`#a{t{XlU`Ol_T-n>M}NWeK9=PNZ4^7!YA<8{cvehs{sg$O$KELc9~0yfXa zIC4}?Vh3y-82gDA+|9nlVhg(Y951OO~fVx z$Qe01M_-0}xQ26*Yo3c}mUR5nW`ePP%4PezL>b|8-3SMI?@0ViHR@CDEuZm*RhWD(`R9M9 zR(duvaQ%M*Hvcm}clBSW;{T6sBvU(7|K2M9?-u;OoFC=CXx#q=f2F2w4BjOHC@4rI XZaGQtX0%WR1v#lHYb%vLHxK;}zorCb literal 0 HcmV?d00001 diff --git a/website/static/img/blog/ekuiper/databend-and-ekuiper-2.PNG b/website/static/img/blog/ekuiper/databend-and-ekuiper-2.PNG new file mode 100644 index 0000000000000000000000000000000000000000..4832475cb86df91d548d7a12a135e47a1d901e7c GIT binary patch literal 13823 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 literal 0 HcmV?d00001 diff --git a/website/static/img/blog/ekuiper/databend-and-ekuiper-3.PNG b/website/static/img/blog/ekuiper/databend-and-ekuiper-3.PNG new file mode 100644 index 0000000000000000000000000000000000000000..4b24654ca3da1f3606bab06a342ba0e276ffa890 GIT binary patch literal 34678 zcmZU)by!qk_b!a0AT81@ASDt*O1E??-Hnve9nu{G(%m9G41$ExAux1F*H8+>P(#ew zzSnoYbKc+K58{H&-p{kwv(~-t`(CkH8j1wC)VLTJ7zD~n@;Vq8kAZJ7K0Uz#K2w$1 zhGSraVkpbYy!FjHy2F8d?r+&kp1&#R6`ufO5o167h#{l>q0f-`BYjD1EGU+B)H6Wr z(7Qea9pJUr6{PQD)zK!*I_aP$+qd|71EZv*C?<}C@X70<^&ih4D!$J1SY1OMb*x>I z6HSQadr+>dwXB|NoWDI@YH^ycGyVSMz1(QZqRfOSEn#%;#)sGc`^$G~cd^aIUD1s% zL3LBeKct@j|8M^DcSnR(GnV?b*7ffd*P-s)87juyL_QDy$3OA5P4}20yXmIhGwXLW z?k(2mR%bNk0CjyjGtafT2%BY#t^N+i*S_n_U5u~D z&K$)5Tm)UBirX32E>bmshn(0Tk)*+p@P|st^;BN)B9mhd2@af4`oCTc;ox|Ld+X)r zrGs2gxFDHw=56g;s_O1{&w}*Fft&nR?NTY{dZ!_>^uOQb|F;22bdm4rLi?F+-C4{W z9aS@$XWd#RlV@L}Uw*`}8-0B@BJv=9a{K>;z<(|~a%T4lQv^ku-*#5VHStZJWv3(X zy2-?%d5*gw$DRGbs-hUGB4{IbP|W`wKL0kZRC^4%rlTFA&N2Sub@$I-h@47C7O!wNBs|FIw6aYsZjtOrd6mxI7-{2Y@ZAe|7NU5o?>-p}>-he?PNeLr$Tx)?`Flsmf5-Z3S~32qaf}%^_uqm$bRmz~ z%vXKpvNm5kzjWueU`^%Tf21ag$v-QPp|^R<85rd&DJ&@HLdXc&|J~+snM5b{XR*gxW)wzs$29?sXB z&(F+2L~_TVjHbG}y0>aWV(2CuHjAlbJ)wsU1~aDB@NVbS7%7*#u^HeG9i5$>@fig! z{;UL8?2YG7I80ZJ*=DQRb>EU({C6NlZ_&i6v68sA{Vo~H6-N8rq0WiwP1@Z3o)lvV z2no54i=9#HGClD4_`-5zF^q8s|7RNE51q-7#lD*L+nU;1mo*ry0a#oTUBLN)*PEM* z;}BX|%7W)Mxr(>8%^^}oyqTks`9JsB{h2iDfbf7kfoJ3^42sYaDyoJN{~C2E@9y>L z_)pw~47!T4vff#{x&8ijI`FPWPr^iBU!QHwD2}9|I_9i?VXWuzOqt0yZP#Ttb)<>Q z>I3#Y^Y2knQR))@Ncdu_+Zx(DSIFfa6aiz4fTe62{H|E&@bg%JID)Ct+`Ha|=j2fP zSsOY|?xuDlRsV(8%0!`U+ z2nhXX@&^7VBENWS(tg3sWeKvlgVcADg>KH9hpae@c?Xm}yIw5dgYplZ)g`UGn{UDD z8b+|tAalQ#b~`RmdCKVOg2w_Htb9ka=8kFL zF2Yi(hj;h{i-oqKg%$FQ1k?iT_;jKI&cDA+;RDeBuyssr!)YV(xWyeNE~i2raWUqGwh zc!#+Acg!78pSbFXXA|9&FMAiKDQJbdQrQW6heRs3f&$#sPL3U%|UlpPnxW{1FpaX`?GK*zve-{n<$Xzr4KkF?z-BHhXEh20$2;GUU5wb5F^t*wh}4_x6^GaI?XYFzI5rct0A@k9aT zU+$HBX!7+dL`WS`P1`qUFg{hFNmCCcGnx)gtCJ(IUQTlNRZg;HaOO1bT)6uD)o02~ z8S(wuGW%lX3n|B-%C}N=@>1{O49qi|a5yMT6A6sICcE(TZS_LkmGrDGNx`G;))pL# z3uY(|c|N*j6J|e#l%dJx1pwNr!b07hX+cmqKWggh+l-zFONy(1beq~J#51op|>(JI(?&-b;+zqsa}EpDt`bN%6Di+K87YVa?6992phAJGd7$KhvpP!!>n|}BHzWh>A zS?dzWF2;A&H+2m?U)Tw@u~%fwo$JC>I=fK^oM1VmzR~wH8e6pjaS>Xt8SRbz%0j|; za;_%iP$Pbmu7(Ip;Pp=CMuxr_M>Ww^K3}zBG$Gx~q|F~!a&I0V;IDK%I%Scs*_qOZ z)Z>#(ol=>$xvq%Pz>5_G*v>Y5raTX3hDWx(|8;Eet4*aju*pvOVb#ngiX}%!{s{sBaIu+PtLVBieS!6*}55kOpoc5 z{4o4B47U>zsRUHT>4OFYowsjjrdOA(beINzWr3_^FaNCc=81UptR;)Bb``B|Du{=b zp3NhQ9k$ zuNd;gxI6f+XD8E~{xi2JwcW2QVsaic6Bt z-uC)$7U6Inxp7L!4)I~Z`=W~lj|qw1VYWirH%YWq!8glmIdbWUvOU?E)+$x^&2k0H zs~>0((RHFf%T1%?pm+6BqWyY2(&ZP)Y(`i7_`2{&ybQJP&-e>z4XeRd-KVDh=1n!; z;gy>nF|?+)49m{c6L0Mi8Kx9WE8sad<-)S>lr!B!g-sYuk0egZ-tPOJu0bJ=M$5rW zg}p>lzn6mUZ?8XJUKFqoLb2R&k_9K6#!f}Oy;*NkQCYN)OCAI%udO%RNF#e{e8VuZ zQ=F?O;3C_)&_YjL6XC?~;tb0Q;^D7Uc_^keGoTbrUlCgqjkL-G{^ti|Z<6WhKEE=2 zwi<%Iqq7pzt`~HgqjW1@-U2nW6iJr|1Sxl>s_v&7a4WN2TfCCJbWkHzQvI${@5*`C z=W_*~(PVW0bNb8$#%u^)@i_U39esaylej^fWo-wv^xz z7hTomS_t|>Kbd}n-`Q?9$+ck2-3O_F@Dz5$kI0tnk82+3R@dUW?)E?L^%W` zH4TxJUWZ}RKbeq1cMu>bFUqKdUGpYC6-WL)lk!WM|2RlzFKTj~rupyCn)W8_?!GWcu0-2Lk$o~|n<@tdROIr8^nOL%^n zmIQ05k!!cR!WT569}wHab9y1b@pLw!;kQc|9+4xGuxV0X1wdKzz2Y)>f~_#nDvEL6 zcp4{bxKMSz#Yw+3-aq(a(S9KJnCgLy*pEW((l?RT$r8{8tsia)rp@*f(2FqPpE~t-DZ6!iL9vl0m?k>$tmEp8JDzL~MlkCNOdEZU`t(vCAL63XpiyG4!Q|`OBqd?Qg=vLtD8BP zB$uTjC--URR>H;B~fD>VyUMX|G4*9@d&@DOpP{WjsJ z`t58ceN9*=@rd- znkqL=z;TOyaoxhV^waCTLJD&8B9h=h{fBoScg7a@ zLT;5v6#m>vaY>2LR3WSI;{eqLOpd69kLsqtZ#Vm7$*JB&2G22td5O65lSc60&dZ|^ zn|(zoRBBKNkPf)ez#~rBWOh*MS`|dx`Zl@mjPUyWF#!i46!eDiR;RsZVxa{h9u?)q zua+oQ!t;5kNa129)p=h}@o#_DDGp3(mf5SxkDGWgx||e>dja}bUB4j#y| zr24ed)TU@62Ku@Vgi2|lyRU1&n>JZF0C4nZhM;t5!Y5UM)92-Tx#BbCJtwQHTGY;~ zkq4xw$VQ&IOvrp8`qthF+Y@)Ic-I-OvqUPkw}!r3Q#V_80umXXZ4nQZVk%>zRBdq~ zTo|r}BoRoZKncugGFocV248LHNd|@&CIbihXO}daYBV#&!paGz=wvGZUK)jpS3~X& z&Bx|y2DsBSBC$|J;&*ecUAAjf-DO);Vksg6FLqz2$C`ba!TEK}>bX=YiOg$|L>7GG-lm@NW^ARmz)BSMK3e$HGrLV% z`T#TPFT0~U26*C(CLDK!z7$tRv+k|a!%3o;ELe(78p{4!18S^VP_^`8o^0+!DMuHhq>!DdveP=2a~^$i zufY%Y7)A9ZzrkM1W?q_{a{FZ@S+u6X*E>R2L{xlz4sL&5rePT(AEFo&?L>MhNvVdi zt3W?fWs2qzJEJbJzNc0{)avkSxZ$$*G@_nWR2&)9YOvx+0#*UOCsZk*nH}f#ORvMZ zMSeBWY6j#`(F=xyfQ?8BqkK?a%5^+<`w&uaKb~ns+wrvA=nhjwFdGB zh4MDF=)}qlc!*C#B-{?^*T9QMxX5>W-^S*Bp3-dm)&JMAy^aN?OoCy9*Nbb$C^sa4 zNKBQdRv?^=3c9%vyhqTue0^teN-pHwnt6Yvr6P4oBf!^YXBCl3Ljxa% zzM7@afL96(eJC%a4Ltc7N#^Y93FPA3REA5QrAUf9u=k5vTFjGlP_i=a-SOFkRIBKZ z=~xmra#SW_Lj=Wnj8RWUFoI4ngou&6QxFBoD6~E|h7F$$;UZI^z?~!S6&W%~woV5b zg!kws3ccnGBjL3wND>Gy6_C@P@(tS1N`!VQNO0(T7uj&nX~uc#%oyfjb@uouxc}gR z%TuUOFa{(%PB$eW-QabcdJ*tBmef1HgbU;LH_&pZ|J5D8I^C*un}kkf8Z8v_z|7Yl zi%*ulo{Kz*5qh_smy|Zy^CZXxz_Xu;I>IIQIS`@;Ri(}{aY85n^wdg~@~`9U6j`U@ z9LN{c=+*Ld{xm4|UN_tZ+mo#fg`_Xm=+}P={-l8<^p9-k2UY$7a&@LeWxp2+LqLbz zy`m|d$R)gkDSjrC0#Vzv74v1A)0K0WDu`H8h5Bid6Z}*E`4Zh`WJlge2$h(@Tq;%#IJ_s?V#hIrrGTDtC!gDF(is~R$ zAmBnu9Fd_IFL3W3TvsMLSmOJqJ}yvzI-pf~I%o2fa=?60CNVhcc2Z!feQ@Tq!leE` zxU!>Yv~BL+2I?jd1~xWA7t(TNpiyw0a3qcGpEdz|)++M%Wx#M6%Wg!zR7kKTF^`C* z>u_Lm-d)+Xr0B!W2NPEbff0I5TYU@)pGtWeonYrpE@amlXlV*2shnmItJ4FMWqA3t zUcH&igwv>C4hE}T6GyV3VI zkMB8-Sf-zdA`$tQR#bfa^e^&QV#zinWFKRQbkaIjhTKX0?_ zSc;_$0XfVF2{^KXuf6NdI$3w`lS+cynC`#VrX*mfElZjF5n4B?)W~o5Ia)YvH{rg# z{Lp9eWYJsQ^6rqXr(IYyLP&x}uD(<#%2wF=m5UhB^B#HYk{BrzoY}bFE~SWM(vWyP z#e8x*@`p-xX59OrD-Xq8BywUGJAeiZz#YHyaEU zbR5df7;pJvGAgmK4oDDt4AT79pp9g*h@x5y@o?Ga6E>7hdDJJ7NLYoI?`(YgD0`XQ z%R-wKPDi!^7NV=llZ$V!f-~Kxpwt-|iLP&V`J*U0NX8nO-clc^7vS=YE7&uoNV!zI za0cqzq+NX_V{jn!tF&}Z*T*F=S|Y4$aGLB5Nw3Et{R@(KZ?3?OpcP1srp*L}R2AZf zOw!h;SD7s#N(FRoMPp1Y_mGTO&j|c8px+NFYil=)S&hDsc&0_IRW-9dU79Q5!avg5>?x))-ZE7``dK2^W<-^;xRt z>70>FI2S4c2lfxv!W3Wfugab;SSObs@D4{pmEVuXA=S!ZbL~LK-e1t~aJ@JYY!}Xf zN^0-8SaO24kA+B~uP3;2wgB8MkX;sdpd3_Wc`#G`nH+PoQtGans=2=VzP;rRh>xG-fSWGkX+aH;VaeLJGi10A3e80Ls0`=li-AA zM%|ojN*4Xpl5#3Iu##?lK1a~$ zRnB2iIAK$U5$h7Hnw^%{ZM8!-eeoaI9_N_il)d|j%>e0=H?|JcW?kF)7N4&_^sq_3 zaM~up+jcX#Lu=0b9zn4+aoKe#k;@B`>BXvgvBX+k$an2h))+h@Z@it2jG1U8OeY{d z)5C(GJii7w_ylVnRH`hgZT*zE{B2z`PdPeo>XSb#S(qAPa}Qm1ZEm{1MBlG%0G&o7 zJGIMc1)ak(&q-?o=L=!ye%OtvuqkbPe|T4TmF7-P6r|>oGm*o z(;2cWGwti9F!QyHA5KT&1i>%6(Lozk*a{>-A7cMmBlXM4*^19v%l%^ppto~HPJM=O z1d8mJft{Zzh^6RFZqLXi*^L-N98%AP5w^Ff8nX5=nhPsPF9eNb;J1ti6N<_7E~!e$ zk4vW=XXE^xOo|l)y88zy40v10^|Tmj_e0S_qCh`6Mupj(Lb13!bj@&of?axedp04w zM}rukECHR439sRYpr#9xJ8T#ar4hHNda)Vg)6P-uV8(t_*B_G+oDuC5)3^b7`G5NrpYKU4&L<4MZ)L{o-2@nu1qrCpV^cT;Mx zZ}Y!+o(jI<$GI%H7uI07wBeTxHkMQn$q% z=fLSjf1lJa)Hm}h_j--*7&$5n*6Rd)pnM8h8kUyt+LkK-me|rnS@oyu=8x=Kf^ht( zZ2;sP^XG?!MZ`i`Jr(J^(iMy>W}F;lMWWB;0H(%^EiGh!YpREq!=6(Jb{jzC)k zP5ouYBv+$6-5tCbU@UrVwj~H~I$1Gi673xW5`3xZ(**&hDZXwaWc#e{`yalx%HWx7 zWxn%B`lZLc1wZ2+;QDLBhaS66RcbQucR3PuG22rv?DX81&k#uJ zWQ*?Qc{p!!hi@|6Z!q9Saf3~4yieADLi6Kg!t8kq)_)@Rr`65KHfp#u;>xVur5R?0 zIIiMv8G*eB1cBNvR|C{5HpW`J?_9Q$p|8K7d9PV`sBVT{U{SP}vMjsUzz=TQ1z_eW z3akx~umm@eE5}?s?r%jO^q#yKKA$%?=f#p+gW zc}ZCJ0^`6Czuymi)FZxgcxke$V0Mf?*EAxG-V^gLpt`o$F8WRh=DTh- zgJ~k?Rcv+kNQ;*}ama%RMgLV}t^}<_OZU%gepPUObvuyVB(qwCmHitd$&Ueu{HDt# zC!XoG#A_}MP$AW2*2(n63C$f51E*J)k`x_4J5}IiTTD*CGU24^p_432(63zxsrx}R zdxGJ1n9E0?(VWiLTg9G&4RRd%NJYv*WNdRemOWZ z1Ac&rRf-fs`HfcYG=tE4_Bd0?hiIkZfgP24e*G5Q^ zqeoXGVqUn{XYi>5G99E!+HOh-|69fUy9{yt1|5N&3Dxb$BsLy}-~G0>C3rrRttsyY z{E*>H8!!%-%-U-p%h#g&mLH7n%W_qLiEcLy2h^^V{N786P?CyN)pieA4UMwfT4Op9 z=XJoYA(}^UWdtXe3P+|nMrNRNE)v(aSTWVsS;v{$SzsiAEtI;@^KDXaW)NXAJlFBK zGO~O-M6}t^zFP+Gf{-LY~?ByLfM^|`k_9@~CP5XTF8^SEMAIY&s8 zqaYqEL)JQ#TP($8^gH>AR~@^%hS7GIAFg{P*GU@3)ol9OyQrk-)`N*M!>+)#F?}|* zEQ@PCjTdzH3zsKh7tC}`br|VdVZodE*RJj~4bF!P8&%@lWdzreW_Qu*_!4oOCF4Wn zXB>;Qu*g?d;loht+kaG&OIIoXS&GjQ)4?0+0oq-YbagX*Q?HEEhx8zxqQG6(U#FY- z42+_^gl!J{gtb;m2dY8(3^GJAXw6^*3D~xaf3NrtGSOfnvSTZDV&TIAwVhEW+kSwBkT>*filT8%1HnbITKY@0<2o40PyT6Jp z?&q?R9jyEzj9jMk`E4mwKrXaNr-vJ%(!5iY+DPCh!^V27iQ>82nNsCxfnH^XTvKeR zyNefVd>?L=S(6F5BWskHLcQV*n&~ z9Uu>7vd}zfZ*s})$=MT&M7?v8ivB_(;^cg%B>l+#_Ue>1iyE0* z5*NDRRJew-2cZf3>9$lDv&%~3?tdhFMs*#svgyji~rK$pn!iVtX zDV5be`$qkxq~Pf|`9zgUNkjPne})oav}5tSxOO=DC-wbLMbXBR%8>`68&qJ`tAVm} z01&l$hocW-PCk$OO-Nj3>4*Y6du<2MV9!sb5Q&%GpWs_>B1Uonx(4v+a)ha?h-oBv5KSo+m;csd%I1gD8?5;uc`c zq@=OW`xqrM@UQdajDHZ0^ZNLc_)k1mw0(h}-BoMIO>2)KxtjN%_CszONEK$s3%a{P z?|BigMZIs?N&b`A>l}V;6G!2CC5()C}iy^L9 zOKWd1kgTOsP8n9DV77CwCy2+e+w|zw9U_=Saz0pLPrss3-zu}v(f~nD?Tq@=JmDu#YKM*}w(o&3yaq>3q zqXvXrYD-|wvxTQX{NxSSj)Pthx!g>93wgepcusNhSRxRpAvw)mQ~VA#Z{@H%q7>l) z+wmY~Gg|fm`wThBj}ao)?}zGdot~N0b;_>JNmmx(RoS~UGOO#wi3Iz82GLyGF)hCR zDdL~=qeO0|4*udwMxud()HH)Nx;#&-+%w8qZh)JMQctWp1z(2Q5B~L=&U%Q;SQ48S zL;~_78las=Js(?>Z&^E&P}JxT9onFH-21Oao8d2QavJ5puTXveAVDs&bdWN)p>-3F zXB(osolGwM#cN%u9#*ly0)OjFH_(yt;i=H>1Y5d9)lgPSrVk1QXfg(r-%~@Tm}rot z4Ct%D$I;mX&n2zhhTCdAg#9Y%AiR|jKn8y4{Gi|N=k6@kHr#PaD;dx-=WTX@jKGg- z1U1;A_Y3c4)#dF<9@tlC1YaS(Tub~6G>4;yyhk7#J5jLxT#%7 z^Yw*^A{caUf|A7kyr$_+AsJEN(dI$?a4i{A>OB))3-pi)P9<@EiezP@Xt^ErH6)b6M@{b$+Kp0Ly629RHY4^4XN*huf7Dw{ZVkW>Wu$tX)Z$~ntg3B z)%YZFCiwcnVmgPtlBYP+k0gkmURvxdTIz0J(|9PZ;N2rZ|FB>JbGgl!WGfU7)i=~ZjqW+g z1>8uQEsPSe%1MK1`Wct~E~@hCNPS{+sT3MJAq@?JSh*^nT>HZ9b)#F(RndpYN~A z?~V!~XM-7TPudOO*7n4`*tyMnvcoPPT&snWX%{EOWBB)xbl{m*L`X zUiS1BeR^nDls3g>7jw4!Pc_4QH9DIVJsW(UJUdwCP>010(AxB5=Mg>t}8%!A9b zLiBH6KI;MeQU=TfnX6c~0(SXqzTxuYYCJ!!IRIxl#ic`SwFZ^KljHu#$)pkXGVG`o zP==Io3I8PSTq<~2zYP!nJCvg5QOy>9>C<&acc=+Gc$T`w=J6L%W=u(7foRZNH4oNf zh_xF3iLCHG2!2(FCLKerhZ8OJNk?-rlMERGepD9>o%E8WaG~B%OV|{_JOBB)l71>?Ef@Kazp4^@C_NttUQK!C53C7 z((fLqVl_ftFA6Cm+X>R!pI4rG7wM#oOJmi2+r;G;H}-$x??J3t!D}|jk1Pec{Smk< zR(WAv_dQBhUhQk4L#~>ndsbKRSu8wcJ1kEpsl;=g%-_)XkW9NyDNhIXMGEzX7>9Bh z-kZ&DZzNSs3c4Irzl`^BbfD0FQ z+4{Q8>m_2bt!DGe?swGv?nno%I3t5{s2Zs4i=7i62?S7Z7t89Wx{P1dr_3A9(sFAW zEu}V5VNbF_9#~hA^OCJh8^KPW&YO6s%g~cG8mZq2p1LHA+t=D8Bu+a{J~NcnhuLe0 z9Sa;XEK%tWYhy2^C3YORR|4&8cH+NI;uC+t#hc*d*PAgt3h&|gtmq79v0BnN?|wZ1 zY#-uk`zMgV^b#g=9QF{)IJSc&av?x=CL=r>B)r-SxwqdU5GA=0XnQP>nXELv-P&Ee zhCYdwB2AjP280zilnerwr{O<@mWR8ASm{}dgwEdhbFNX%`%ip z+t?U9#ZA4a31=PfI5Idl9eFt)Eno-IpQJK3YIxrLbYB(zccHPgJOGf;)%CR`@6=nx zOK-04m)Gv+wv*NxiBs4ReNnpE3soIM6gQ;Ch3yhvf8#n?JoDJhaWclQ&Y8fJmh_Mf;p%eN7Z`QT>>maC)F#lOX9^Dx zAAK!bD0%tY0}n6>-{jMV!rwcSZsX=ZfjPkyZ2T9SY%7>ka&z6f7Dq~t{&~Efwq?H3 z^f=qSX`H{iEt?UL6Mysba&h=F5v4X1NlRCPD{231 zubl-w?X5wb^;0I?$@vL53FiT3b%NI;&BSrH_OAB4hsC-+$4j$Q#d&@j#b5l%ZPgX+ zLTxPK^>vWhVNo&&Kptp<6Fj0vZ65oUW6|YX>K&*As{hf#$Nv%-Whd!$$nY26lbu%N zA!V5t3^EShG{hocjb-=4BF8FyFPiL1x)muSO~vk-A5=v4B>%Crq31d_Lm=$n3m12K zv`!nqnB!s5LmcoZASG`J#g2GA8eZn;;J&NjoiUy)V1M3KLz}ND?a#W4z6MI2?4yd3 zmz=RGu(GV58#im{uEL%tu05V#(V2msJ`YIu0-7Ik)(-rE z_F*-x;B0o3&?2-}!oq>ZiKFf1+`Lys`S^(veG?5r6tgWVdd3i3_&$+!-xQxXG>^{X z7f9%|GN=ah)O~V?c!=#T2j@k-3W4Y>5$2n{-u;BK-Pep|(K$NY2i1?_zJvqrE1P)9 zN`BQAtHfX{tJ+$rDNPXFP=10vsxWVZx>ZRtD7Umw*5=2QD}+rHCto^%kY4=t?Hc+f z1h1xYb}k5z5QVxdhlY#Iain%;-stUbMbrCN4a+TE)sMbfXYt4E1eDN^l!`wG+T~HP z{(!HGvT&-Bnk`Pf<0?PX^~U;H;8vpW(x-=aKCx_&K$slpNqjJg8-6vU@`a7kVi z$Ry``PALm6^5LM}@Jxo~DF5>_?d?-vx^SN-I~-5GP$1_65|F1rgPNH2$P=X1ubajQz>6rNpn zF(`r{#J_sHK;iB%qE{;E7*l#TDYlgV6k+Y77F|-*;zdW5a4ZJbe>D5;D}x0C$t zlxc(#WQvJ0!q@wGAf%#>9+_qK*cxe0!jrgVQ@qQu6)Y9{lN|pl0y3=>{B@-p+K|ew zggd}o#J{)n)vy`&tU9H2-a&4P$K)GqV8qvXQ)B>Z>|L=V(34&m2lD&2p`LU0PxUW< z^|GW{ORXt8F7pc=Z&yDoJ^2GvX<*Hjlo1p=u6Fp%UBGE( zJF|l0!mZ=>*~XmYy3hk>u?AAM2(c$?{)e@)o&sl+|GcV2nlqO>-45gZ@4qzfeqKn2 zi{=}S8N?R!y3S8NDA6@OjZd8INBMR%KWN*!whAiG`+bx!wBs%aSW_kZ0LyImKW8ls z4UP4nxzg$0&NJgrT&5t-qO3E`!scwX0YZjfb94B3x%|d0gn`cQpA?VAw8JbNf*h8z zaB6lTL|Sn( zF_qxtI1AySf_CXG+`?UnwbUEFO{pik#ZD>`&ML{n#!)rgWLxy&FiSOWjWn~|!=nHQ z%N|>Ne*1CAPcgrOP}x=G=qBqD#(q-{E2A7l^wwY;r$%KH>0R;J*!Gi8c62XMfN+?t zGtIwtvwHa}CSM`|N>CCw*T(;{8`PdCT)~0mRmw&6J8MXL4uH@)G27LTVDWsY9i9+J zPHD2;>B0zOFV;bp4R>rm^{edM#@B)@Z1%^yAWBu`*ZBo{8CWX;{E>KGBjT}3 z>KYdT74YL3$W>XQ&MELe;Lt??EpAcmYk>9~W{nm8{h^l}SFFwlgq_QJYLplVFOJ5| zL=1&mh0eHy;j+zNJjPE43gmBA_J93vIfIz@5xI6R;}rJr1c+|G4p85@rWo)I=N^r*i-U)^P$A|I07+;}3GgeCiaM@3*Ax zOB2W1XH=A+mN^e9juYVb9^)c|o_0%R;gU&&)+H4lb`XPSmDWtv&0~DAa-&p=Fl#rb{KlDh(L`BFPZBbp&Y_EoFsltt4J_TOvTjL@SiCJ~ zras)KiOW+i*w9n%e91vXohq}G)|la4#mca6Yidx!OsbNR4ap`Ikz-g#13k@r5T4tO|RaKK<&r0pc%yHS>VM|{Ay19uXt zU~>pit*v;KRfXj8n_Q`l`FNd}SopmDUZX8c@3T1jjy26@YysIp2+OgKc={S(`i<5y z!a4uc4|nCpyS8!Jna9x&yi&kEkfl?Yo+c_UglA4^LWnxFf=|_}%bgVgo|?OSGVd49 zy?Oh?73Ls6z%ZEW@s}v%s+U}xOK-2NqyMbBtRRt2a*@g2D+<1urFQ?Yb2ro;5POEX zJR51-KoY~XnZx3~e?A+#f=o}=?w3P2A0_lz7bLl~?G*%kUDL?oEl%P23*qUG9~8Wf zXY%^&$l^8WS`NsJr0}TM;H4>%|7kLfQ2%xc!3^|CP8HSQm$W?AX}-Bb7g5jNN4I$4&w;)cmrp9N&YC?zJNoGsntTW zE8!p6H3gVzy@VV?h5WiXGWU2*wDbO1+`{$t1Y+#ycwbr0tEZhx<8)JE@S0Tl0PfDj zWWKK(hJdfonJF&pmqm0jBj)q5l*HDDZ`)^?GtJNCPi3%=m2)k`JFa(gp|YQa8XTCi zVhQJF4JCmz`AWVA1b6r_<#h~9Em$xL+ybDinJgxyY3ggOu&AaYF5&fhWXY5*9jxnb z*NtA_=S0?9@!s_hYeAQbm#15U%j@FrXR2yh7hrPtY2EI7zWZ2OD=!-!w{W7eeqL~qr zc*JrvzM+~o`#uZt&OT5&F4zWSoj zi10Fd?8l!vYdQSUKc9nIQ*;xvwk1ueo9^|8BC3;GB%3VRB;U!~RzejiMh42Y3^?2HqlsH&rBsU~e-O&^CRv&9 zxU#?c{Q)k~2D-!V?5G1attquqvLSF$t!|4{DIByePp};5C|Au~@@qVG38Th_*?R2$ zY83d_J2Q?5j$>OB2jQ(%GvAKv^5j|^NefSnPiflcdwotM12|J2wX508GDR!8az0Iw z9nye505t=RErY_98Q4cC@5*9Q45Z$1)*QIC6N_(CJolJ9?mg|}DY%LbG=GQ`Q+OpE z&opi=IhZQ5O`?d0qkc`J*JwX<_KySkjF|#0R^P_w5F}P&0UfP1m_LV-wx}(9~rXNouo9rZ6_3M z6xp}u)USg~v`dJ}6IQ=YEV&@dAZ2PxVg3LBksTg97CmjPaiomN@#4`-Ph*1W!&>5eY>>zkL3pyY$ck zl=F^f32(8Cc1d>Gq&2datpJbISZ(Sx$%I=PVe9Sz+X})sHaL!gU6x5n1-4*5@<(~M z`Z{-sZ_a}u@Fa|`g|+aJwJzdGx%-`|H&1(HAn9XjG*9kdVD^BQ8px-Gg0ZGUr~1;Y z;C(tKbCbA2Zo_KlC92Tb@he2OJtFDHQRwYo^oH%PN8h(gdw+}-FaOMyg_`PK9n2CI z-=9Glm+RVTLRpU3HL*GXyMK=!Wec71Codp1<~%P#PGj+Nm?U_lbHzpDOdTyYbMc7& ztkN$1wJuMo_2$VKP=A~XDP9>^jEK1ed~jEwi-`Bw8 z!Ps*}Mlcb{*1rirP|Jr6+2!Ss=3kb^6|el{m;i-8ikFiB&zv zhS*>x)g3g$5ORCkvq+ zA_xQ(e)@%-6fv*2k+KlQvSaaG z7TP@QbCpUmVO|hn1j)1Z=!qd5zo8BJ+)@%?;_SV>=NCe_Bi)PISe_uVpdi$K%SMhQIsWBaWBu%E{Okmip0QM1E;#$vaz<19G$JuOK@e_k6EjRLkU}Ji zk9v_ir3niQmUUb4-AW|8l-XB1AT)RQ8%`v-fWmwuv@>OXl_)gR_5uEGWa_a+S+to$ zxf|boDg%iclIK{BhOyG!S(_Z`#A)&S67o3H+fJNIA*8Xr$J!=hZ==@rWj)|1SvL2Z z)J$;_sgk|dc|D*X9Gy8sTu>Zc_T3{ke0kbI&-^VUuEh6?*TWI=;x6lf1P~hqN^yC> zmbqx6G1`%W6^i^=Fe%GyjbV+6(zb5_jaxJ9N1c9iV>x8mdyJ6vhZogPtuBgU>9M|Gr7zj>q05~Ll%Ane`j&BLwAuvfehO-}v z6#)vl58R6MT-Y84<0VyYB}=Vdh2InUnMMca@Y%*KCc!2QR14r~R|94uCI1GbMj0fZ z-S1 z?_9bkza`dL-^%`(Jl!dAm_|&VQajp@$?|*SiBjD25yFq)Z615BAAd%BGP?9-$#z?s zyeXI^XI$DzpP^d3ZInBCN=Jim4|Sb0(Q;u>fwKjNQ}lbfaL0Z58X=XPu38pRahn~P z5H>{Hl(Z7XU5Vyci0b-ZWxWMdl{^p(ii+@;&h_shP4RARsq5&Zy+9aVpSo4J{Nw4437 zlil#T!+95uCpphaTv{gI6Iox}uNEr$30cStRL+ z`4^cYo?c0k0ERAxs!8F>>vo@O@Q$)Y7syFf%w6z21gRGgIXA}3~!`$Y7V71-jR z*psS}KCmom14nO6oyCg|CgJxU?bpt>^;BG=V)#K!Ds_=N$VA|J!5p{ucHjw|VCGHK z`FM&Nzn>>W;-ndzmc=69#QEG#Tr6LEXyt^=;e|3T+^P{LzZ5OC0_7Rak~D>mlD-#Z zxQKJze@A#yMgf z_3p*TwI@|py%!~mkqr!Y{viESF8EEc4<0I06kJ+}Pi15=nSOx|NQi0V5Fo?es~>G- z(f2ClE>UbwV{vP||3zGpge6#vF<>qJ8LOGf!$M)1sb^oaZ$qsb<+xa)F~cf+B|81_ z*yq?s?9Cdxr%@(ZE?@EgWFyIxf~gTnb05=SM+i?nTi9-5<_+=uub=N$-sXBfnr16y zuJ?J)L$f(ii2eh{eA+_dE*B+}oLAmEMR|V4H@}bFxe>7US=7_JbgroFb66WVJ#p2H zdcO7Zi7sD7Ye7RvTf52itaH%8PeA1*%mbM2_i>#p?f7)WW2}EuS6<4t@VBQ+Ad~Ni ze3kMet!o!93|*9S?fNEMO7Jvp(ek1gv5;y{9ny?I1f$lBv-Rvv$}iY98476jbyhk39jFMLbWQmAm{kP>iLNve^che zgqtyCpxd_PrmVFz`md-Dc6=t$r^V|18g(BIhpcFWOQJoH2qo^>*`D~*MZErQNrKQy zaQS7y9QDt4cBwGP{H>b#>ba`qhtaS01IWjUf4(PBV;6v2a1=jIU7ny@x?-9n`$gQ(z|Neb( zXSI)4+zO~eKly3uX51qR@9DN{7h02-^CnBuh_ffMuc;h&?K-x8!7BW$LYINgncShr zWO4p0C%<$Gw|yF*5;nGdTYsikceM6XALX)_gDA9*>S>3hvvbc06#TyL_+Ye}qm;N=*d7|E7$iera8->rg zRue*f+UR#8)$Pop){;WR!3P&HnFdfr&G1=M37CvjyQhVTJqn6-I$akNHkJ{NzKDtT znh-nECzbD394O07iaqIjY!uB>Dn?V$HgJpHr}DX9&Gu=I(sJUM7yf#D_xRSB*Uyc) zL7S_jDTq}A&t$$!hzT;hcJSEBb^iD+!)lEtxmHkt)ktppc?m|P;i7@viW5wDjW6a| z%qh=VaI2A@Dh)5h+V|FG7oi6WGqdppi#at76{W9hO2vQ|B0FRlWzwp%8PMjQsdy%X zj;EA-Mm^ky*Y%$aHOdHiCL`nFB~9x-g5y8Ob&3}Z&Ko|i#ps`(s=XdNUc$~)B_KW} zg4k-~DnX1;mq$r1#eF0x@S;2eHZTQ z#lY1FIn9EzvePN!H}cl$79=z}v_qy#RGh!Ey-ri|c~L6g?tOwnsH1&Bcl6EC!B*s9 z>ZWANRbgYuVr7VMS&rddxc%1AWKEmS5vuq_@3h{M60jdLfYg zxADH$#(i!^ZLeQ_D4o~i+~o#zbb_`Sh3v+G=Ll#Ny=GEg&A;b~VYQ(adZDQZi~yi3 zaK4^244hOcmDyRJ>&Lut-@m$HaP5G<$oR!>*KA1exfwOn5a>JNDly?)2{~G+xYHVW z`#hI3f<@zf4ubj^BkxGo8z_X|{uz}waJp)@H@o#`(wLg1GSBl<8nG!6<7hSq+1~U( zr=BBfSN-lptlv5Z>U;$0>}F>;`B1p5aCcWCwowN6n9`PFy`w+LFvv#wg@!M-BzUbj zsEj)9sm4-n-F;?(tSA?+?vT9L`!W7Lm%M)a*r$-B5bXX2E%>hCs_4<0sQJi;FuFjN zO44gs;>1XsVRe=4i;;?tyUlAok2b0f!$N*KT15#NuP2J>F{8yaR=lQEjrH_YY}q?6 z*L5ZOmSx@*Whazyn_5+#3u09)y7HkQjx(Zz>r~T@R-XTCptzP%c)0~jaBgg~n8NG zm=JG1$~vITxI6h0UDrFx0oTK|*ht~GZLG8=d~Q29Kn<~aQ{56tEGjCJ6BieE-rgH= zSV?Ddm^dGsDb_pKEc0mfNd8ob*M0IkcQf;gpQWoA(-TZ^!t4Sk;FY0s^q(ftbeQV4ARO6lQPFe*rUP@Px+RI$08ywya?&Ve@Pd(^1 z>u~u`oK4PqhMTkNrBh!&LMC-Ge^K*x8H8j_dsR&z*KX6gl>Uf_JzmP;p&`%Xgek91 zRjq1&45J_56*TM>|Q@m0#~P&-{9Ep|nGGSLVQj>n2sSxT?%S*X-jP zXcSHNtoHmHe^ZyRdz`t^Y(YA?jJKb=p<1}83lDB!lOwJ_yOl}aR!GsM^icHiJK5%M zk3>Hi#=7hpcfO9pm4+o`54aZeg9y>)@~pt9lXB1^T@04JeY&J;*f&M1@iNV)7@;*E z30@zVi}vOy3mMm#rH(4dYerAp3h~#!Ecm|iacfM1`3wujw0Y%NU>Sd`tm$|G-a4%Z z*mA0C*x?I5EiGZsGCM7?t^H86Zl-yL5Pg@TGrZb-T1lb^4U8f>O%S6>P@_TT1a6$z zO9)5P&rK@*+@}1}$=kI8X48W57!a$z#oUroq~US7n6KV7+Y!Z;OHZK^1+VlZN?_sudg9FEui>1#CI= zR|lV1A6^U!L43P*PfrhXylqp_(J?1S`|S1ci`uAvnCG(=+8sjkH57gLnuIj%H0Ht^ zrf*{G@Xo~+#vScMGuvYCiLgajzTr0sXJwX*=#5_617d=3YS=K=`K31xj2NSHRnZzu zVfc4#169V|ELO*N2dYdG*j(98FMMIKqv0#FThjhsE9-r+?DXiVAL480+#tDb3EOh&65i4|Z6b%ZJgFJk@77j2j}T>)&O$l+gDG--5D zx>Xx}CdC4&(p9RwfXq?bP5QT*hxP07hfdSc5o%Hf(q;jQ;(B@ssBZz?L#>nE^lk~~ z$$u1Z{E(n`YYJZ&*L}w{VTIeG_lib8$6=>GQLNbT)0GihzYI*+i1*g8<<(j=VtBR6 zx5$dge?9MHQL)MR=bc#Y-0A8JVZvD!v-`Z4U%zy2T)<^84#sjc=06TxdP7!WhFQ=~ zO{6z5F(H|9Tb$>{ow+`d(DHzf*l;xcO*-mL^4B{@bb^eUGbkXPO;|*iIF8W1I0qbCL#%5J#YN!Z&Kkm8jhvu}uM$??{diT@w?;K;zHQ2--n+p#C@2D5&m z|A*eDx0i6f>X%xja20vO|0NLmDmNbsUoras{Yr}DQ^sN2PXw*{-1q?#tjViaXVldM zi|RLnz58yM7zR$r?y8w=RLQPxvuxS?_w{Vm9$ArC^fu~qPwsb-3EjPWx2(2sB;?-1 z3d>@WOpW69*&$G`=(U$*jm_A47R#y1lu2WzX?C{rl{6wbzqFOiq-#DM6lC14UW+H> z%X-{Z{B6N?A9H7!FM{G|#qn2GUDqR((El<7{T4jYlwU%d^zXw)@ubWa{WEPD%vB4# z{jY(b(Z6|vav`{7qV*~F>(N?^4l!bPpiC_-H>SdPTh8mTdbBLDy{a9~VWK$%YD}^; z?V*jl#?ZF`=gWM_1)+;4Afnzb5+1Y$kYF4YK z?OAUEQu52)`q;^FCI(jkQT4@clM9bW%Acs}ncAjHk?AUQIdB+zJ^wr;pTc1$dN+t7 zwvH+(zU}^S+U2mh|3d!s8eT*`Ab6So)M|Pj+7TnrsV93+M8@4v1Ksr+@L;r};!b_j zI@clWvi~N13J6m72aC|fmbS~z(6v5|KRVp1`6}UE4PEGrVWIqrIR$&*#*NfoP~=JJ zM5cQMtfzAn=y(wHmPWDJNR(AOf=CGqL5A7}TrE$oxq#B-fn;IKS4LVO^4CEB$$)2N z5ZFI@V~<;J4Z|Ss+A=sgzpUl-^t}q{O;(7s=zD7D4lU*C@R4+w!TsYt8KXm~D<+DdHS88Dis2AY$o4Z;nhg<>6hC z01AVRB|LsLUi3YT4_EP-7a#wVE(<(bmi*(N#WRG1?Q-+J zjM0rpz~F>O?EW}weQdxLDV$y5-JP|a4;LQZ#!h4NQ&2~ngXUGs(MwJZQPRs7o*^T- zeynK0iMyr)x2?w6N;By=gsfnw<=N9{0UPeADd@qT1>37v2S{08J1v@=04V7@nU4_> zEHLSBUqx;IeqUn;GJFSIVXrqywya;NQ{BsAO$dt{`d7RIS$|EVDW$=fr((%`C=f@4KBf|nlvi^{f^{j zpv@l~S^xrxCO@eFK=LYFM`NMV#f!_Z`ik+M)mifI~b+xgF7F3?4=c^lp3m z^z`;dsBY%ekmqkAwj3WnKm$@U&b0Krx1~i{>LYregyB1+i4ud5T_j|>mW0mycZ2ey ze~->1{GOK9L}vtMf3j>q1O3cJQn5j1YMraMU-ew~ex&lC52DPa!_DF<>{ESYje*1+ zg{E@e=vC)9o?(u!oK%7)y`U+(p0jg>1Vgkf&N%cXT;9-rB-nu1HbdwTZ%m+#bf zfn}?OjtvA?%QwH@61V26X=s=j1MSP)F9Haamz|*!_=38tmht4=CWKcDh}Rm2mcI{L zs`kB6asWZQ!tTEXz|t>PA1!y5kQw%>oi_8 z#d?&+w~;aww}J>^yzd)A_>aXKfIU~u6W)Cnj#GwqryO3Ro;CV1vAK-mP!GA)RQufq z`WZG?FbR`H3)+#x+RR_-NeUL1X|D%Vv&YWblqb;H@U#2vf=l712pICK^nH}?V_xeZ z3QGaNxV5CH{6N-YQ_%rA_3eKO$-mvo+gvbr9mFkxEr$Z^$3;o%`V4vZ!PVui-gs^! zL^Z;89JM`|t0xsy1Dj^VelwHrJc5;Dp=zu~GDYj_mf41wn9a-BL13v)h*u#dq&C!b zDWP)hDwm4r;#1Ly2JgV_LO50Y+FTyuYY}iO^JOloCq8SZ`9HC1c;54k9naJY89-! zDY7@OcTP#iX+`2Y+>$o?*RaimpKx~~FhI1^NjJys5l@aTFCL*Im>T~XMMFY)^EPYa zbZ5fC7Px!b>F(>e>?EPuUcGuXZ^|y^k* z0#-t#qjhbm>ZlUM>`6xZQdyl{EhBZhm0zpq7)Qq`OzI$Pz4}7PZbqW)jX0%HQh0E= zTg5EG^Ep|Y$O-e}PES1}=}MPFg}UkVI$B*}%qZX4;}+b%;vzRHxzL+znxU41=;BLe z$A?X_D_WKJ>uJryyK_`h0_mI@|3RZ*zSZQ4*0}QNi`Yg%3?koXccf+ZhK#?xDZTl| z7OZnOXTML_Nw}*Jp_2xkW&{fc$@qRLeS9S0G4aIH^L6lb3}Ow~rJNbxR;^{TwCj># z#qLC{-`af$+&hxa03k)xy5PnH01s(1fvgn27~af=#PW-( z0mLyr!-k$T)i(Q1NI2h}NCiE2a@-LDbt~I_7X<}{Nuhc8cDze@D2K*N@=GF&7C(+j zTxn~ZI^0|0Bc@+pgzYkP$@^BNQG0|qIjS~F0@@uZs45S*uQuPtk6-Gnu$+)hzr*7P zv4(jdmC-3c&&&FXq|fasTxRj`t4@8VBc$dusDuW*q|~*CQnQ*mIy&6G%-C@^fsG#3 z8t|GwU*OpJ$JcByDRXN;k7Dp$Dd0%01Rjqnc@?X~LU6MU9RizQRb43V)jbh-!iu8x zWJE$FCF|FJ#HtulFst^^yZnMaV4v9%&^ah4mLn>^y6~9&;LQSK@mO^go&6{K{AHkiJBfJmfCO(EP z|F{#E6PB3D5Ur+g0Whwn0p)F|{I36&5K?n5$^-~#X=rZVkm=MHP&D6JotW&I>4<;t zya*$7Nu4{dA@`&tOhIQL6}*HJfF*Ks9&<&uQ5M%*uA_!8o_1r%_)~LwmF!)%3@i2~m{1*NXc(V$_h(TCwl__IT2$ zhq4)P$B{wC5IZTEh?mr|G@m|xaA`uE@mAbI2INLJqTZ%{%m>8S(?dvoigvRgP3>x0 zQJ)FL${HnTBXkPS3kLf$imu$mkDSEZyAWL0beINOZVHCNm;f=+By(;tT>7W=Z4(C7xt zMMqqr!1ndo#DLi2%rUwNiNI}E9_P0_Uyq>@b>t&?+rFM;1t&$hm^L1|-}kK<(TOe2 zhr9h%&kirJJ}g*={C3ET;+tF=$^B>SzW-ANq2~#LFFeq2E3=t<=~o!SuKrA4y-{&^ zOpe)0t2s`Nw~KS(?0Qw#hY#XjM|h{Tw)^9K`#WnK?<}BfaYx5AscUIP3#WTa2J&sP z;DbJzYxJ(kd>LyIA1yC@d?^Mhpk^2U=$MeW9*%0`VggnjOP0rH3T@_jdJIESyzf?S zRV+QV#>0T7l=mTM23kL#*4|lmneK?EJdFZ7m&7Z?&2dryKdR&>Exp&OXP=C518Hd_Pepy9)ko6pv$8!pHT{L+)l=0IK~IKj+9yst#jm&hGPa8@tCZ{PueD0}Mgb-EaDo(=l;kv+i8o>RQ)LpyS zakLx2di9DNRXCgnq|%PspXT7tTTd*`0u(L!zm z!)An2AU{%709VSCoO2?9`}h0tYY+0yn@6febU>!lQJv4^wLge#SZ|RaSN41o;yQbj zT&nkj-!*_CJQMbtltE~~We+Cc7x-eda-P3+W&o;dlKbSy)JUR>S~N#2$uIS@@sw zn)U&~tZ6RnG6P@xSQKMS{URXI#Y$X&U4n!Eg2?`|xK$j~#Hb>zV#p|pl2!mIoy!pm zME6{w?jlhZ%memN$*Et;X3oye+anaNM(31=N^SDdzK}xXpuIcfzW{FD0%0hOMOkPy~#m`R#!bQ?xUKsBbwJimOr(~t)#V=5k8?xs}E*T3da zw;`buC9wmvsc+G}MCcBt%138V!EyEjIl7b)N$6f^Y-e>+C?H1qA)yvS+%X3#j%WJh z-((Xq{P;&tIr^;w9h{G~56Hh}?Q!5U`T4ad8SID;^I$(~=z0j&`)Q;ozgg;6V97ua z4+Uk3XGSg0_0mkmSP@&f^~QiJz8GZMRMT3_Q8f(VGSihnUXz1q9EMUwcVjX_A(A&j zZzChWo%_%Y89@3>vEiv$umz}cg{*jQLnP8vhiYlni)pMqtOR=}8^4Gk?CthS{lgWG z8uXH|qSiCC%KElzT*vRgNX{Ux42D-fAS#CBts`Z_@xY@-Js9<{-DzX;qo?ZY~QY5S8_5p#rx&Y$|8TIzZ=>n_R0S5BbGT8P=kpN>cr+VyQu zrGsL3qD;T(yxbKU5JR=TGws1v&u%(L9$=+fY_t~5(-O)t2>VhjUnBgZvFDJvPr89UE@l4CdO73@ww))9ktNyg0hM9+o$Dnb zYjE$1tagwzIKT=4v}x_Xfuq@E2e3Ob5ac+HEV!QCnYTj%h=V!AR(eBogxgVdF+6rubPxsiXS2Xaup%P48 zEHE6%D`nZrh&tBfy?QA#by|Ma9;&a5EQH6h>;$Yime^6fC<-SQMv1%tswzBef|IF{ zpt4NlgRU%CYysJWJIBnZRg}LAtLr#{ef%MAw*o9Ak@+^AUnOh@Ek8z?1!L}Txn2@W z({Q%m1a{mFAwDQ1gErtO2y7k0AA4$my&vy)z)F!!Q8Am(@{Lkue>WEsoPlB1Q&w=; z#Zm>!7%q!k@%j=-E5$DI8bMSBu!`Ge5gF^<9XL=UONnJ)Cg^e3^+08fW$&aB!m7v% z=l-kFHSiHe?DPJXAFVULyR&7wu9Xcc(O?nCfjV{rBV*db-*t!L;BQW>VrZ;AsDm~T zLih=NkQm+Z_eZ005uUQRxZ#QVPNAlSl$FqC+3wT~IC+BI1R@C`@q@q^m>;8V+phu? zcaOKwX#T+{3Dte(`&@?{SKF^nGI-)$dcAT2-fHoxqpQX8l>w zS|WLn99I|3yuuLNRNLB~uPn$X44M+C$6QJfBhLl#oVLyQD9F&CLHG<`->hk!ekr1u z9LP;TYOW|JE>imFN<;0H`S|S6E$I<`=S?tfm%c{(-Fnsg>MvmQsGcI5jn46 zeBG8o_nAg5cVgZ&pKPR%)VZB^c5s@61ZFRT$CoZ^k>EcMP=nd2wZaL@5=8*i3zh!A z_TDKN?eCzj`0_QZa%!}n*NTAhI>#uroSf?H(e_rNURoDVNN`AENpKLy%vBky-`A8| zQK6eKw-6*j&R{#BJNX?BQOQ3zY!E%{WL^`nnSHcDmRe`zcVyl^!C1Gd$etd^^V(%% z`eSrk-9I9ayA}tzV_(h1Y&zcEdDL~(wVIX4n0%lk^E7GpvBDP~;)MSuR%Pd&wH^De zV1A7Jk^p^klFVUPXi*?fri9`(1D+{tfj|aiM(vg@6PA>xix8u%tkx`1?_>wv5&pcV ztlV~A;C?e4c=62AalHGPO*T*LWPzcTvAJRgGk=Dt|K~3Kc`;kP?JTVA4gd);O@#t@pF;V@h{VkolNv2 zBO|L99M(u^pA|=9m#fr9@#EvR zfl)zG2d!IkPg5eJojFV5&f~Kqgi%xT75&c~7utLdtTQV_yvMZ_VuYS9rI{urB_)@X zJl*o;X_RFNdWn>CtfD)gMA)VL=sNK%mZywjx4wP5Vb#J>Y;i$x{xnVqPc%dQ%B z8l%N`{5XJ`BCIDB4ljd$p9A!KW<(q}=Z`+zk2m6upyRH;>dR&3w{Uzc=KMf_E27pT zSo^l-5ca!qA0-o#b90sXmU36#{Q3oX1I_u;taPT&`x|-qiR__wZMAj=H_ea zTK9KTDl4_6EU@Qn#TtAkN>|bTcCknNCAT2O;1WGt@ox*OT)$@3t-u!na+tqBPEejd zz2Hk;NTcT=7sNOjvR@&?IPY%-sLr=7u!AWfWP4sTP8h+o)_l&@6wG8_M-1r)hHz#+TZF z{!ht&sn1cX%M2yC`#N#!XXurFgCzqcR8i~%_VYoP)S*c5!K%|~?206->8Aqn7o1Cm{D*|k_>V2$koWljk=`}#K-537?yG_I&=}o}8u{DwpgA^-V ztco!`Y_-S;ud#{A9a>(~+t6+9`+SwbKN88WVAr2nm))r}#4xNuBKxW$K>Dh1*B|Da z$CIui7$KYE>Z*^AXIcZ=*Gxg*>Z+wPM$5`7KPKOxg>tQ#Jv;IJH`DmA=sIOKRCm>J`7qkGz-Wwz4Y&=&-)~#V z-@gy+?@x*LV=P^f(t|8I@u9Id9lL>aRTZqRNzkbTaHc$o3SE6r($88csi`GzsWgYo z1?_#7XR-OVqF%XriOeU-RX|X1!v%;Ya}K^c@~=ZI6BQvs>LH{p=OX0Z_<$7lN^8}$ z@erKZvo>IAzPI;iHqcOMF}mO>4m&WAiizlP-kg7$4_Xj?h0%#-@aD`+H{x*5DvDQ0 z5f=u?v#Nv0lRqu3j#6vZDmIJu3k+F2Q&JZ6bL8i~rKfkV0r(3Hn^U55&bdrW z09s5q;smiY9+NFZ!F!VU&mvhaHCiH=pPTDmHiIE(nov?w(r^UNKM6kn4htE-2qF7C z59hkNIvN)^f>7>#c4#Oi8FXw*d2*f=nMM#XIBqT8MMfk-Aqz#?UK-QbfcKv8VI1A-o?4pSjXW5b}HS=H})K>5t&u-Cal6qjyaMxuGBV6%8%! zuCvP>@!~XYmo8q^CnZ2{+d=Ok64YbGxq&^NGKwr zt6F|RE;^9wF~H?o3WqML(Q^(=C#Y?O+2f@X*f57bH}_reEle-OmW|Rxip7d8ZD(y{ zyn&2*f}zpQIP#Y#JbGkD<2yMR_g`ZW@DBKTa5&ksr;yd~qxLrE5&_-TIX7F}OGn2t zzuP9Jragcz#o=1tWcJ10Ea-4+2#h_?O7F; z9cyryNdFw-!@v8FGt9UYDA<;M(=2nYij3L=k$>*6t4l>|Sq%3Jhx>MDHo|e{0U<|i<=J6MpEKfPee^Gaa>_XB!L^osUmnl_~ zv+p$%{S$pvvU{rZn*!IZ^p2Xj0B+y$}6xkt9Q-Lt3s16ID zUi7B5Ub=K?hOK|KI6q%CmcqCnoDrFrdeF>mdR{6k2Y7tw@&j>MEmp_gtUpsDjf2m&G*0f{_YJ`-g)yG&J;K>S=jXGfaK^Q z18QNf#XQuxeLoU&a+JjVE%-CyF)+i@ee5&6f}zTlGm5ZS#pIa-Iyx;a7lFaFu8C4~ z+G)}0V+((=IgiDW3pzi-F(C~%vc|+1FRwEkHCfd+Y;arSpWeMYPBymcjEU}HH2^V0 zvF1DU9j8B5K==05HgIjl$a2Q11toBOZ2kX4u6t%;D#I25aO*(d#fY3?R8*3ZB83TZ z`D{B3#p+Cl1rQZWb(#fT56eBYVZ-XZEt`e)&?_?2h%J!IQXjA*$JurJHr%HRg(vUj z;BNz}065jr)318(CxuJg*27&ZD7OVvIPqy&ywYao=KY@wzkQQEF%13m@pm)7x`>B{ zhIXT*kF2e&r+c)@ba3p>Tu|OR`zHmn;agf-Hu&}`tDxpL{yosq5!Wza*f4B}O8Nes zf2e`?Y^cKGr-CMrDulX#(BG5sNwJApv$LrM$NXGd!eqbiAlPj z(+xJZP6yM=&`Fu8qn!^P%F5zAK-YO)qH&`abuI!<$qHSka z%xU&|$fkeBC<|KY#s@Hl&uIN1LW{b#Zy{SW^_D6BRW!cLE?c`SSWksSg$`oomRt!j zIbBCbE9a>fDcSIZ>tMpH($mxLaF&l8%=FCC4w{9aIn9dRSgGDcB;Au3;+LV#T)Alvpi0SPv8RCZdCE ztsqMEur;Z?G&WAN2lDDh1isKDjKL8)!zTBpIq+GQ(P#smwDgLKgLkFKady$1^E%d7 z84nH)sBS+0n;DAe)C&`ky5Fa9>+SE4gfKBA1ei~nQmpbD5PG`PGI))+VlKVj+uh|1 zKTcQskSi-SwPOkXfijFU@3OSA+We^HuAX;r zR+)mI9q+)h5u}S5<0uc2H_HBRC{vrm+%o zh3LL=a08Mpa}#E%q^$cFv~bE>PPSeqd!6L0GI>c|GageqpZ@Jzm*E~%NnOU(mfCuH zdJf9xT&GzvIllzUqsdW8hj?6KeHiuqxkI9s^9G_+ z5#7jJfW>s;ydeERi(gM(Ix!y~-&`&-7Gk|I7#Mgyn%rr7S(i+IeEaawg^U661sefV zh*V~<9I5;7J;-uIf`D`l3~ty6EQ5i<8c;$VgzN*dWv16}GH;{-OqCDg?c*81Hj4M?eq z_5!3BI@xfx1rt!j6vj?GL%M_u?BX9*Ix z50V+0rWRUDvQn}rzXaX-I~bt^ra0#+O);^zezo7SvijmA+l60`e(>v>o1d5Wk2>>( znzTWz#!J`k6EC9ZTSny&cgE( z8pjV^7EE-ypUdp#rKht`^=KL+P?&X>!OgZ`SgKr^G+5f)vc=F)Q(LG`ebeWTV2I~~&L0^gZgB8K z%0D>cDqEUF`Vm8M(lQ#0WVhzBkM2DNQs;2I)QbP8Kq{i0G>|AMi>;G1Rl0%>#%wyH zSFuH`jJyM0k_iHn>dEG40oW^e8cb6pjW<9|^XLCv_yt z)5#L|%T@JXkmlRdzQ44AQ}!6fR~XP>nj5anFs8N8KDvyNZg{{PK;AN53=dL;AsUSq zsO~uFAl_E^#E4g00|m7A$5+S)NLo=kr( z;dgzbzRA+XCWJfk`s#T$dOA8ET0J6PF7X(S8SK1Wf0)B_u4{{;f8G@*+ZZK*cCa16 zvn!`f^XMbZEdu7g#|f0<50;c@jSos@X=ZYFLIW_bZLb=zi1p9ivMWEH=+tr<{hP1L zvOYc5N?2!Xh{aXZZv#*9=MRR5OC!hD!#}svJ_Kj~{%`n?Qv}EU|6j}_bQ|uy|My1- zoHoZ>m#L5asS5}Qy_W3&WiH5%=yX6ZPL8-`3Y}8MBfF z#9T5D#2z(`u2S#t-5hM;Aso<&JF@Ds(PF#F!xJv_LgbM2?=L;yhr>h19n(l}&79ui zHn2tEj~P+eI%FM(?)%V=dl0ZYvtsE^1B1Rzl|?kEP?y{-g1Uk zY2xpyMH@r~ZpTX8^U1at`sL4V^N`MDw}X1^a9fA@?@KM0Y`REEHAvM{v)-lc7u@J) zJo$2*f`DG}7)u<1>p$VvbPc`@V~T#5yMLDf!P%8J`!Wr$H8Jb>xF483)J)_^`u)li z72R(6ZPzNFbA1jC4c+h?E2mOo`}+!R`^H6+{OyjJnVEBUZXW7#JCQf9d-xL2g`RzR z>aZO2li}gvwcIE3>Xl*V{#_r`m)rVZ;!VreY5x(IXTMM;Zecws($j7%_ae zhL_CM^KGDgUh*6J_1s4$v0B14{NE+`oWGy?j*-u&?^%1}P|1 z6gxzTv^C32-oJPD_NxHtT2NLqG`O2IT9za9PgEZ7x4|ziE*>6%6%TDg$NB}1Z6B_C z<9y|4pQ6BZT}R+lD^FejNrf#1w60_DgX=@7$i@3bJycaN5dvy7X`=TN} zAN5(@zt_*l8#-b>dz$^d1UUr->le@$KEIDVbHu!QEka%di^cBfz(+Ae*H4n{6RA#J z6Mny#S3z}KkHCmqer>~?3IFoF80+~@7s<)VHx*-q>{lUUq9Gw6VdSoF-Fjqp>iD0f z@Z*hzR{zC|7sKHQA z#a~PO?S-opYxs03|L7pIGq9>yV*HP+dK8Xa+xP7l3FLX&sXb4XWSP)4aKQvxdpO?% z=5yWM-FLF=Pu?afjmw%VmAde$Hb_`l_*OCeqt>e)vH1Rv_%_vgOqFK(u@r+d1n-Bp zrm}y$sTN(*F*Gu|e(sEWb$$5p61UCG%?6M^W74y-Vzg9Mt?ZPQJMTBV<|Of~xkJ|w z!TsgjTbZ%&)H_BZ9~JL#d}2%fahZqn1PNRqmWvIf5g$}vyc)X|xf-v>GV;Jz{M0Fe z3(X^As%@X=e0E$r?du4nz4P%(srUR~Da&pj7d>)3^6*_GNi_+VS9QB{u<`I1IZVtdoU^?juf!iA@1P@3 z?e~D0sUbis{%?vmlxK3M zjDLrn+3~schLo^?soU`H1OGd}UR1dM2sN{_c<3k;6}i69QusrvBlvg5NNiu&?q~G4 zmXZ9Z{|v{HFL-UR6?g&avd`8YzkIRB;>z>+)~l7U8%o zUQ8NmF|cXb)uFCQ(O~wg&oh8 zqgCwx3+7#U6*q3h2Dpjs`u)Zdr=9TdETqmJ;y zTw0uXCVe%Qmvm4Rcg_x5T^5JQnsRpA3H~=~YtH5oI@ITX%yQK!3UIOa-_KvAp9x!X zS-SC)NwMyb?dRdWj;iCYJKX>M^s$d4-U*pchLAQkmN#4#Ht@Z=-BRj$HDPfT#oH}N zc;L%mvixtfb)xjWnrg)@f6$f^<>X(Ix->*Pw@3Kz2bSwJlA80?=QSN}@Z&Wtd-9WM zgz@kFW_jmyqBNKDW?=V|6UDhviUa@NEKE>th8@?e)zK{if}^8LNxzfHJBj|}vN946 LA7tHs{_g(+YkoTV literal 0 HcmV?d00001 diff --git a/website/static/img/blog/ekuiper/databend-and-ekuiper-4.PNG b/website/static/img/blog/ekuiper/databend-and-ekuiper-4.PNG new file mode 100644 index 0000000000000000000000000000000000000000..b511f390cc4e69c94599131804a30d3be45db850 GIT binary patch literal 20888 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