From be1b007f2f5495c283b93b3b831658b92ce223c8 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 23 Aug 2017 23:19:36 +0200 Subject: [PATCH] DRC tests integrated now + refactoring Refactoring: * more consistent behavior of output redirection * ut library was refactored and split into several .h/.cc * concept of long runners introduced * long runners can be skipped with "test_is_long_runner()" * same for editable/non-editable mode tests --- src/drc/drc/built-in-macros/drc.lym | 4 +- src/drc/unit_tests/drcBasicTests.cc | 34 + src/drc/unit_tests/drcSimpleTests.cc | 142 +++ src/drc/unit_tests/drcSuiteTests.cc | 91 ++ src/drc/unit_tests/unit_tests.pro | 2 + src/unit_tests/dbOASISReader.cc | 96 +- src/unit_tests/tlEvents.cc | 1 + src/ut/ut.pro | 6 +- src/ut/utHead.h | 472 +------- src/ut/utMain.cc | 1545 +++++++------------------- src/ut/utTestBase.cc | 458 ++++++++ src/ut/utTestBase.h | 498 +++++++++ src/ut/utTestConsole.cc | 412 +++++++ src/ut/utTestConsole.h | 141 +++ testdata/drc/drcSimpleTests_1.drc | 21 + testdata/drc/drcSimpleTests_2.drc | 90 ++ testdata/drc/drcSimpleTests_3.drc | 19 + testdata/drc/drcSimpleTests_au1.gds | Bin 0 -> 431 bytes testdata/drc/drcSimpleTests_au2.gds | Bin 0 -> 1849 bytes testdata/drc/drcSimpleTests_au3.gds | Bin 0 -> 437 bytes testdata/drc/drcSuiteTests.drc | 494 ++++++++ testdata/drc/drcSuiteTests_au1.gds | Bin 0 -> 9653 bytes testdata/drc/drcSuiteTests_au2.gds | Bin 0 -> 38991 bytes testdata/drc/drcSuiteTests_au3.gds | Bin 0 -> 710484 bytes testdata/drc/drcSuiteTests_au4.gds | Bin 0 -> 38991 bytes 25 files changed, 2834 insertions(+), 1692 deletions(-) create mode 100644 src/drc/unit_tests/drcSimpleTests.cc create mode 100644 src/drc/unit_tests/drcSuiteTests.cc create mode 100644 src/ut/utTestBase.cc create mode 100644 src/ut/utTestBase.h create mode 100644 src/ut/utTestConsole.cc create mode 100644 src/ut/utTestConsole.h create mode 100644 testdata/drc/drcSimpleTests_1.drc create mode 100644 testdata/drc/drcSimpleTests_2.drc create mode 100644 testdata/drc/drcSimpleTests_3.drc create mode 100644 testdata/drc/drcSimpleTests_au1.gds create mode 100644 testdata/drc/drcSimpleTests_au2.gds create mode 100644 testdata/drc/drcSimpleTests_au3.gds create mode 100644 testdata/drc/drcSuiteTests.drc create mode 100644 testdata/drc/drcSuiteTests_au1.gds create mode 100644 testdata/drc/drcSuiteTests_au2.gds create mode 100644 testdata/drc/drcSuiteTests_au3.gds create mode 100644 testdata/drc/drcSuiteTests_au4.gds diff --git a/src/drc/drc/built-in-macros/drc.lym b/src/drc/drc/built-in-macros/drc.lym index d914dfa81..3f103f38c 100644 --- a/src/drc/drc/built-in-macros/drc.lym +++ b/src/drc/drc/built-in-macros/drc.lym @@ -4181,14 +4181,14 @@ CODE @output_layout_file = nil else @output_layout = RBA::Layout::new - @output_cell = @output_layout.create_cell(cellname.to_s || "TOP") + @output_cell = cellname && @output_layout.create_cell(cellname.to_s) @output_layout_file = arg end elsif arg.is_a?(RBA::Layout) @output_layout = arg - @output_cell = @output_layout.create_cell(cellname.to_s || "TOP") + @output_cell = cellname && (@output_layout.cell(cellname.to_s) || @output_layout.create_cell(cellname.to_s)) @output_layout_file = nil elsif arg.is_a?(RBA::Cell) diff --git a/src/drc/unit_tests/drcBasicTests.cc b/src/drc/unit_tests/drcBasicTests.cc index fa8b37941..5ea3343e9 100644 --- a/src/drc/unit_tests/drcBasicTests.cc +++ b/src/drc/unit_tests/drcBasicTests.cc @@ -63,3 +63,37 @@ TEST(1) this->compare_layouts (layout, au, ut::NoNormalization); } +TEST(2) +{ + lym::Macro drc; + drc.set_text ( + "dbu 0.001\n" + "def compare(a, b, ex)\n" + " a = a.to_s\n" + " b = b.to_s\n" + " if a != b\n" + " raise(ex + \" (actual=#{a}, ref=#{b})\")\n" + " end\n" + "end\n" + "compare(0.1.um, 0.1, \"unexpected value when converting um\")\n" + "compare(0.1.micron, 0.1, \"unexpected value when converting micron\")\n" + "compare(0.1.um2, 0.1, \"unexpected value when converting um2\")\n" + "compare(0.1.mm2, 100000.0, \"unexpected value when converting mm2\")\n" + "compare(120.dbu, 0.12, \"unexpected value when converting dbu\")\n" + "compare((0.1.um + 120.dbu), 0.22, \"unexpected value when adding values\")\n" + "compare(0.1.mm, 100.0, \"unexpected value when converting mm\")\n" + "compare(1e-6.m, 1.0, \"unexpected value when converting m\")\n" + "compare(1.um, 1.0, \"unexpected value when converting integer um\")\n" + "compare(1.micron, 1.0, \"unexpected value when convering integer micron\")\n" + "compare(1.um2, 1.0, \"unexpected value when converting integer um2\")\n" + "compare(1.mm2, 1000000.0, \"unexpected value when converting integer mm2\")\n" + "compare((1.um + 120.dbu), 1.12, \"unexpected value when adding integer values\")\n" + "compare(1.mm, 1000.0, \"unexpected value when converting integer mm\")\n" + "compare(1.m, 1000000.0, \"unexpected value when converting integer m\")\n" + ); + drc.set_interpreter (lym::Macro::DSLInterpreter); + drc.set_dsl_interpreter ("drc-dsl"); + + EXPECT_EQ (drc.run (), 0); +} + diff --git a/src/drc/unit_tests/drcSimpleTests.cc b/src/drc/unit_tests/drcSimpleTests.cc new file mode 100644 index 000000000..6beda7b00 --- /dev/null +++ b/src/drc/unit_tests/drcSimpleTests.cc @@ -0,0 +1,142 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2017 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include "utHead.h" +#include "dbReader.h" +#include "lymMacro.h" + +TEST(1) +{ + std::string rs = ut::testsrc (); + rs += "/testdata/drc/drcSimpleTests_1.drc"; + + std::string au = ut::testsrc (); + au += "/testdata/drc/drcSimpleTests_au1.gds"; + + std::string output = this->tmp_file ("tmp.gds"); + + { + // Set some variables + lym::Macro config; + config.set_text (tl::sprintf ( + "$drc_test_source = nil\n" + "$drc_test_target = \"%s\"\n" + , output) + ); + config.set_interpreter (lym::Macro::Ruby); + EXPECT_EQ (config.run (), 0); + } + + lym::Macro drc; + drc.load_from (rs); + EXPECT_EQ (drc.run (), 0); + + db::Layout layout; + + { + tl::InputStream stream (output); + db::Reader reader (stream); + reader.read (layout); + } + + this->compare_layouts (layout, au, ut::NoNormalization); +} + +TEST(2) +{ + std::string rs = ut::testsrc (); + rs += "/testdata/drc/drcSimpleTests_2.drc"; + + std::string input = ut::testsrc (); + input += "/testdata/drc/drctest.gds"; + + std::string au = ut::testsrc (); + au += "/testdata/drc/drcSimpleTests_au2.gds"; + + std::string output = this->tmp_file ("tmp.gds"); + + { + // Set some variables + lym::Macro config; + config.set_text (tl::sprintf ( + "$drc_test_source = \"%s\"\n" + "$drc_test_target = \"%s\"\n" + , input, output) + ); + config.set_interpreter (lym::Macro::Ruby); + EXPECT_EQ (config.run (), 0); + } + + lym::Macro drc; + drc.load_from (rs); + EXPECT_EQ (drc.run (), 0); + + db::Layout layout; + + { + tl::InputStream stream (output); + db::Reader reader (stream); + reader.read (layout); + } + + this->compare_layouts (layout, au, ut::NoNormalization); +} + +TEST(3) +{ + std::string rs = ut::testsrc (); + rs += "/testdata/drc/drcSimpleTests_3.drc"; + + std::string input = ut::testsrc (); + input += "/testdata/drc/drctest.gds"; + + std::string au = ut::testsrc (); + au += "/testdata/drc/drcSimpleTests_au3.gds"; + + std::string output = this->tmp_file ("tmp.gds"); + + { + // Set some variables + lym::Macro config; + config.set_text (tl::sprintf ( + "$drc_test_source = \"%s\"\n" + "$drc_test_target = \"%s\"\n" + , input, output) + ); + config.set_interpreter (lym::Macro::Ruby); + EXPECT_EQ (config.run (), 0); + } + + lym::Macro drc; + drc.load_from (rs); + EXPECT_EQ (drc.run (), 0); + + db::Layout layout; + + { + tl::InputStream stream (output); + db::Reader reader (stream); + reader.read (layout); + } + + this->compare_layouts (layout, au, ut::NoNormalization); +} diff --git a/src/drc/unit_tests/drcSuiteTests.cc b/src/drc/unit_tests/drcSuiteTests.cc new file mode 100644 index 000000000..10c5a1ab4 --- /dev/null +++ b/src/drc/unit_tests/drcSuiteTests.cc @@ -0,0 +1,91 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2017 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include "utHead.h" +#include "dbReader.h" +#include "lymMacro.h" + +void runtest (ut::TestBase *_this, int mode) +{ + std::string rs = ut::testsrc (); + rs += "/testdata/drc/drcSuiteTests.drc"; + + std::string input = ut::testsrc (); + input += "/testdata/drc/drctest.gds"; + + std::string au = ut::testsrc (); + au += "/testdata/drc/drcSuiteTests_au"; + au += tl::to_string (mode); + au += ".gds"; + + std::string output = _this->tmp_file ("tmp.gds"); + + { + // Set some variables + lym::Macro config; + config.set_text (tl::sprintf ( + "$drc_test_source = \"%s\"\n" + "$drc_test_target = \"%s\"\n" + "$drc_test_mode = %d\n" + , input, output, mode) + ); + config.set_interpreter (lym::Macro::Ruby); + EXPECT_EQ (config.run (), 0); + } + + lym::Macro drc; + drc.load_from (rs); + EXPECT_EQ (drc.run (), 0); + + db::Layout layout; + + { + tl::InputStream stream (output); + db::Reader reader (stream); + reader.read (layout); + } + + _this->compare_layouts (layout, au, ut::NoNormalization); +} + +TEST(1) +{ + runtest (_this, 1); +} + +TEST(2) +{ + test_is_long_runner (); + runtest (_this, 2); +} + +TEST(3) +{ + test_is_long_runner (); + runtest (_this, 3); +} + +TEST(4) +{ + test_is_long_runner (); + runtest (_this, 4); +} diff --git a/src/drc/unit_tests/unit_tests.pro b/src/drc/unit_tests/unit_tests.pro index a508e756f..1965c0fa1 100644 --- a/src/drc/unit_tests/unit_tests.pro +++ b/src/drc/unit_tests/unit_tests.pro @@ -9,6 +9,8 @@ include($$PWD/../../lib_ut.pri) SOURCES = \ drcBasicTests.cc \ + drcSimpleTests.cc \ + drcSuiteTests.cc \ INCLUDEPATH += ../drc ../../rdb ../../db ../../tl ../../gsi ../../lym ../../ut DEPENDPATH += ../drc ../../rdb ../../db ../../tl ../../gsi ../../lym ../../ut diff --git a/src/unit_tests/dbOASISReader.cc b/src/unit_tests/dbOASISReader.cc index 9d9e8f247..23a4e7839 100644 --- a/src/unit_tests/dbOASISReader.cc +++ b/src/unit_tests/dbOASISReader.cc @@ -47,7 +47,7 @@ TEST(1_1) try { db::LayerMap map = reader.read (layout); } catch (tl::Exception &ex) { - ut::print_error (ex.msg ()); + tl::error << ex.msg (); error = true; } EXPECT_EQ (error, false) @@ -78,7 +78,7 @@ TEST(1_2) try { db::LayerMap map = reader.read (layout); } catch (tl::Exception &ex) { - ut::print_error (ex.msg ()); + tl::error << ex.msg (); error = true; } EXPECT_EQ (error, false) @@ -109,7 +109,7 @@ TEST(1_3) try { db::LayerMap map = reader.read (layout); } catch (tl::Exception &ex) { - ut::print_error (ex.msg ()); + tl::error << ex.msg (); error = true; } EXPECT_EQ (error, false) @@ -140,7 +140,7 @@ TEST(1_4) try { db::LayerMap map = reader.read (layout); } catch (tl::Exception &ex) { - ut::print_error (ex.msg ()); + tl::error << ex.msg (); error = true; } EXPECT_EQ (error, false) @@ -171,7 +171,7 @@ TEST(1_5) try { db::LayerMap map = reader.read (layout); } catch (tl::Exception &ex) { - ut::print_error (ex.msg ()); + tl::error << ex.msg (); error = true; } EXPECT_EQ (error, false) @@ -229,7 +229,7 @@ TEST(10_1) try { db::LayerMap map = reader.read (layout); } catch (tl::Exception &ex) { - ut::print_error (ex.msg ()); + tl::error << ex.msg (); error = true; } EXPECT_EQ (error, false) @@ -295,7 +295,7 @@ TEST(11_1) try { db::LayerMap map = reader.read (layout); } catch (tl::Exception &ex) { - ut::print_error (ex.msg ()); + tl::error << ex.msg (); error = true; } EXPECT_EQ (error, false) @@ -526,7 +526,7 @@ TEST(11_2) try { db::LayerMap map = reader.read (layout); } catch (tl::Exception &ex) { - ut::print_error (ex.msg ()); + tl::error << ex.msg (); error = true; } EXPECT_EQ (error, false) @@ -592,7 +592,7 @@ TEST(11_3) try { db::LayerMap map = reader.read (layout); } catch (tl::Exception &ex) { - ut::print_error (ex.msg ()); + tl::error << ex.msg (); error = true; } EXPECT_EQ (error, false) @@ -692,7 +692,7 @@ TEST(11_4) try { db::LayerMap map = reader.read (layout); } catch (tl::Exception &ex) { - ut::print_error (ex.msg ()); + tl::error << ex.msg (); error = true; } EXPECT_EQ (error, false) @@ -923,7 +923,7 @@ TEST(11_5) try { db::LayerMap map = reader.read (layout); } catch (tl::Exception &ex) { - ut::print_error (ex.msg ()); + tl::error << ex.msg (); error = true; } EXPECT_EQ (error, false) @@ -1024,7 +1024,7 @@ TEST(11_6) try { db::LayerMap map = reader.read (layout); } catch (tl::Exception &ex) { - ut::print_error (ex.msg ()); + tl::error << ex.msg (); error = true; } EXPECT_EQ (error, false) @@ -1067,7 +1067,7 @@ TEST(11_7) try { db::LayerMap map = reader.read (layout); } catch (tl::Exception &ex) { - ut::print_error (ex.msg ()); + tl::error << ex.msg (); error = true; } EXPECT_EQ (error, false) @@ -1161,7 +1161,7 @@ TEST(12_1) try { db::LayerMap map = reader.read (layout); } catch (tl::Exception &ex) { - ut::print_error (ex.msg ()); + tl::error << ex.msg (); error = true; } EXPECT_EQ (error, false) @@ -1211,7 +1211,7 @@ TEST(13_1) db::LayerMap map = reader.read (layout); EXPECT_EQ (map.to_string (), "layer_map('1/2 : \\'AA;L5A\\' (1/2)';'1/5 : \\'AA;L5A\\' (1/5)';'1/6 : \\'AA;L5A\\' (1/6)';'1/8 : \\'AA;L5A\\' (1/8)';'5/2 : \\'AA;L5A;H5A;E5A;I56A;E5L4\\' (5/2)';'5/5 : \\'AA;L5A;H5A;E5A;I56A;E5H4;E5I47\\' (5/5)';'5/6 : \\'AA;L5A;H5A;E5A;I56A;E5H4;E5I47\\' (5/6)';'5/8 : \\'AA;L5A;H5A;E5A;I56A;E5H4\\' (5/8)';'6/2 : \\'AA;H5A;I56A\\' (6/2)';'6/5 : \\'AA;H5A;I56A\\' (6/5)';'6/6 : \\'AA;H5A;I56A\\' (6/6)';'6/8 : \\'AA;H5A;I56A\\' (6/8)';'7/2 : \\'AA;H5A\\' (7/2)';'7/5 : \\'AA;H5A\\' (7/5)';'7/6 : \\'AA;H5A\\' (7/6)';'7/8 : \\'AA;H5A\\' (7/8)')") } catch (tl::Exception &ex) { - ut::print_error (ex.msg ()); + tl::error << ex.msg (); error = true; } EXPECT_EQ (error, false) @@ -1261,7 +1261,7 @@ TEST(13_2) db::LayerMap map = reader.read (layout); EXPECT_EQ (map.to_string (), "layer_map('1/2 : \\'AA;L5A\\' (1/2)';'1/5 : \\'AA;L5A\\' (1/5)';'1/6 : \\'AA;L5A\\' (1/6)';'1/8 : \\'AA;L5A\\' (1/8)';'5/2 : \\'AA;L5A;H5A;E5A;I56A\\' (5/2)';'5/5 : \\'AA;L5A;H5A;E5A;I56A\\' (5/5)';'5/6 : \\'AA;L5A;H5A;E5A;I56A\\' (5/6)';'5/8 : \\'AA;L5A;H5A;E5A;I56A\\' (5/8)';'6/2 : \\'AA;H5A;I56A\\' (6/2)';'6/5 : \\'AA;H5A;I56A\\' (6/5)';'6/6 : \\'AA;H5A;I56A\\' (6/6)';'6/8 : \\'AA;H5A;I56A\\' (6/8)';'7/2 : \\'AA;H5A\\' (7/2)';'7/5 : \\'AA;H5A\\' (7/5)';'7/6 : \\'AA;H5A\\' (7/6)';'7/8 : \\'AA;H5A\\' (7/8)')"); } catch (tl::Exception &ex) { - ut::print_error (ex.msg ()); + tl::error << ex.msg (); error = true; } EXPECT_EQ (error, false) @@ -1327,7 +1327,7 @@ TEST(13_3) db::LayerMap map = reader.read (layout); EXPECT_EQ (map.to_string (), "layer_map('1/2 : \\'TAA;TL5A;AA;L5A\\' (1/2)';'1/5 : \\'TAA;TL5A;AA;L5A\\' (1/5)';'1/6 : \\'TAA;TL5A;AA;L5A\\' (1/6)';'1/8 : \\'TAA;TL5A;AA;L5A\\' (1/8)';'5/2 : \\'TAA;TL5A;TH5A;TE5A;TI56A;AA;L5A;H5A;E5A;I56A\\' (5/2)';'5/5 : \\'TAA;TL5A;TH5A;TE5A;TI56A;AA;L5A;H5A;E5A;I56A\\' (5/5)';'5/6 : \\'TAA;TL5A;TH5A;TE5A;TI56A;AA;L5A;H5A;E5A;I56A\\' (5/6)';'5/8 : \\'TAA;TL5A;TH5A;TE5A;TI56A;AA;L5A;H5A;E5A;I56A\\' (5/8)';'6/2 : \\'TAA;TH5A;TI56A;AA;H5A;I56A\\' (6/2)';'6/5 : \\'TAA;TH5A;TI56A;AA;H5A;I56A\\' (6/5)';'6/6 : \\'TAA;TH5A;TI56A;AA;H5A;I56A\\' (6/6)';'6/8 : \\'TAA;TH5A;TI56A;AA;H5A;I56A\\' (6/8)';'7/2 : \\'TAA;TH5A;AA;H5A\\' (7/2)';'7/5 : \\'TAA;TH5A;AA;H5A\\' (7/5)';'7/6 : \\'TAA;TH5A;AA;H5A\\' (7/6)';'7/8 : \\'TAA;TH5A;AA;H5A\\' (7/8)')"); } catch (tl::Exception &ex) { - ut::print_error (ex.msg ()); + tl::error << ex.msg (); error = true; } EXPECT_EQ (error, false) @@ -1393,7 +1393,7 @@ TEST(13_4) db::LayerMap map = reader.read (layout); EXPECT_EQ (map.to_string (), "layer_map('1/2 : \\'TAA;TL5A;AA;L5A\\' (1/2)';'1/5 : \\'TAA;TL5A;AA;L5A\\' (1/5)';'1/6 : \\'TAA;TL5A;AA;L5A\\' (1/6)';'1/8 : \\'TAA;TL5A;AA;L5A\\' (1/8)';'5/2 : \\'TAA;TL5A;TH5A;TE5A;TI56A;AA;L5A;H5A;E5A;I56A\\' (5/2)';'5/5 : \\'TAA;TL5A;TH5A;TE5A;TI56A;AA;L5A;H5A;E5A;I56A\\' (5/5)';'5/6 : \\'TAA;TL5A;TH5A;TE5A;TI56A;AA;L5A;H5A;E5A;I56A\\' (5/6)';'5/8 : \\'TAA;TL5A;TH5A;TE5A;TI56A;AA;L5A;H5A;E5A;I56A\\' (5/8)';'6/2 : \\'TAA;TH5A;TI56A;AA;H5A;I56A\\' (6/2)';'6/5 : \\'TAA;TH5A;TI56A;AA;H5A;I56A\\' (6/5)';'6/6 : \\'TAA;TH5A;TI56A;AA;H5A;I56A\\' (6/6)';'6/8 : \\'TAA;TH5A;TI56A;AA;H5A;I56A\\' (6/8)';'7/2 : \\'TAA;TH5A;AA;H5A\\' (7/2)';'7/5 : \\'TAA;TH5A;AA;H5A\\' (7/5)';'7/6 : \\'TAA;TH5A;AA;H5A\\' (7/6)';'7/8 : \\'TAA;TH5A;AA;H5A\\' (7/8)')"); } catch (tl::Exception &ex) { - ut::print_error (ex.msg ()); + tl::error << ex.msg (); error = true; } EXPECT_EQ (error, false) @@ -1436,7 +1436,7 @@ TEST(14_1) try { db::LayerMap map = reader.read (layout); } catch (tl::Exception &ex) { - ut::print_error (ex.msg ()); + tl::error << ex.msg (); error = true; } EXPECT_EQ (error, false) @@ -1469,7 +1469,7 @@ TEST(2_1) try { db::LayerMap map = reader.read (layout); } catch (tl::Exception &ex) { - ut::print_error (ex.msg ()); + tl::error << ex.msg (); error = true; } EXPECT_EQ (error, false) @@ -1504,7 +1504,7 @@ TEST(2_2) try { db::LayerMap map = reader.read (layout); } catch (tl::Exception &ex) { - ut::print_error (ex.msg ()); + tl::error << ex.msg (); error = true; } EXPECT_EQ (error, false) @@ -1561,7 +1561,7 @@ TEST(2_4) try { db::LayerMap map = reader.read (layout); } catch (tl::Exception &ex) { - ut::print_error (ex.msg ()); + tl::error << ex.msg (); error = true; } EXPECT_EQ (error, false) @@ -1618,7 +1618,7 @@ TEST(2_6) try { db::LayerMap map = reader.read (layout); } catch (tl::Exception &ex) { - ut::print_error (ex.msg ()); + tl::error << ex.msg (); error = true; } EXPECT_EQ (error, false) @@ -1869,7 +1869,7 @@ TEST(3_1) try { db::LayerMap map = reader.read (layout); } catch (tl::Exception &ex) { - ut::print_error (ex.msg ()); + tl::error << ex.msg (); error = true; } EXPECT_EQ (error, false) @@ -1903,7 +1903,7 @@ TEST(3_10) try { db::LayerMap map = reader.read (layout); } catch (tl::Exception &ex) { - ut::print_error (ex.msg ()); + tl::error << ex.msg (); error = true; } EXPECT_EQ (error, false) @@ -2176,7 +2176,7 @@ TEST(3_12) try { db::LayerMap map = reader.read (layout); } catch (tl::Exception &ex) { - ut::print_error (ex.msg ()); + tl::error << ex.msg (); error = true; } EXPECT_EQ (error, false) @@ -2427,7 +2427,7 @@ TEST(3_2) try { db::LayerMap map = reader.read (layout); } catch (tl::Exception &ex) { - ut::print_error (ex.msg ()); + tl::error << ex.msg (); error = true; } EXPECT_EQ (error, false) @@ -2722,7 +2722,7 @@ TEST(3_5) try { db::LayerMap map = reader.read (layout); } catch (tl::Exception &ex) { - ut::print_error (ex.msg ()); + tl::error << ex.msg (); error = true; } EXPECT_EQ (error, false) @@ -2822,7 +2822,7 @@ TEST(3_9) try { db::LayerMap map = reader.read (layout); } catch (tl::Exception &ex) { - ut::print_error (ex.msg ()); + tl::error << ex.msg (); error = true; } EXPECT_EQ (error, false) @@ -2879,7 +2879,7 @@ TEST(4_1) try { db::LayerMap map = reader.read (layout); } catch (tl::Exception &ex) { - ut::print_error (ex.msg ()); + tl::error << ex.msg (); error = true; } EXPECT_EQ (error, false) @@ -3008,7 +3008,7 @@ TEST(4_2) try { db::LayerMap map = reader.read (layout); } catch (tl::Exception &ex) { - ut::print_error (ex.msg ()); + tl::error << ex.msg (); error = true; } EXPECT_EQ (error, false) @@ -3077,7 +3077,7 @@ TEST(5_1) try { db::LayerMap map = reader.read (layout); } catch (tl::Exception &ex) { - ut::print_error (ex.msg ()); + tl::error << ex.msg (); error = true; } EXPECT_EQ (error, false) @@ -3113,7 +3113,7 @@ TEST(5_2) try { db::LayerMap map = reader.read (layout); } catch (tl::Exception &ex) { - ut::print_error (ex.msg ()); + tl::error << ex.msg (); error = true; } EXPECT_EQ (error, false) @@ -3291,7 +3291,7 @@ TEST(5_3) try { db::LayerMap map = reader.read (layout); } catch (tl::Exception &ex) { - ut::print_error (ex.msg ()); + tl::error << ex.msg (); error = true; } EXPECT_EQ (error, false) @@ -3354,7 +3354,7 @@ TEST(6_1) try { db::LayerMap map = reader.read (layout); } catch (tl::Exception &ex) { - ut::print_error (ex.msg ()); + tl::error << ex.msg (); error = true; } EXPECT_EQ (error, false) @@ -3488,7 +3488,7 @@ TEST(7_1) try { db::LayerMap map = reader.read (layout); } catch (tl::Exception &ex) { - ut::print_error (ex.msg ()); + tl::error << ex.msg (); error = true; } EXPECT_EQ (error, false) @@ -3540,7 +3540,7 @@ TEST(8_1) try { db::LayerMap map = reader.read (layout); } catch (tl::Exception &ex) { - ut::print_error (ex.msg ()); + tl::error << ex.msg (); error = true; } EXPECT_EQ (error, false) @@ -3583,7 +3583,7 @@ TEST(8_2) try { db::LayerMap map = reader.read (layout); } catch (tl::Exception &ex) { - ut::print_error (ex.msg ()); + tl::error << ex.msg (); error = true; } EXPECT_EQ (error, false) @@ -3626,7 +3626,7 @@ TEST(8_3) try { db::LayerMap map = reader.read (layout); } catch (tl::Exception &ex) { - ut::print_error (ex.msg ()); + tl::error << ex.msg (); error = true; } EXPECT_EQ (error, false) @@ -3669,7 +3669,7 @@ TEST(8_4) try { db::LayerMap map = reader.read (layout); } catch (tl::Exception &ex) { - ut::print_error (ex.msg ()); + tl::error << ex.msg (); error = true; } EXPECT_EQ (error, false) @@ -3712,7 +3712,7 @@ TEST(8_5) try { db::LayerMap map = reader.read (layout); } catch (tl::Exception &ex) { - ut::print_error (ex.msg ()); + tl::error << ex.msg (); error = true; } EXPECT_EQ (error, false) @@ -3759,7 +3759,7 @@ TEST(8_6) try { db::LayerMap map = reader.read (layout); } catch (tl::Exception &ex) { - ut::print_error (ex.msg ()); + tl::error << ex.msg (); error = true; } EXPECT_EQ (error, false) @@ -3802,7 +3802,7 @@ TEST(8_7) try { db::LayerMap map = reader.read (layout); } catch (tl::Exception &ex) { - ut::print_error (ex.msg ()); + tl::error << ex.msg (); error = true; } EXPECT_EQ (error, false) @@ -3845,7 +3845,7 @@ TEST(8_8) try { db::LayerMap map = reader.read (layout); } catch (tl::Exception &ex) { - ut::print_error (ex.msg ()); + tl::error << ex.msg (); error = true; } EXPECT_EQ (error, false) @@ -3945,7 +3945,7 @@ TEST(9_1) try { db::LayerMap map = reader.read (layout); } catch (tl::Exception &ex) { - ut::print_error (ex.msg ()); + tl::error << ex.msg (); error = true; } EXPECT_EQ (error, false) @@ -3984,7 +3984,7 @@ TEST(9_2) try { db::LayerMap map = reader.read (layout); } catch (tl::Exception &ex) { - ut::print_error (ex.msg ()); + tl::error << ex.msg (); error = true; } EXPECT_EQ (error, false) @@ -4036,7 +4036,7 @@ TEST(99) try { db::LayerMap map = reader.read (layout); } catch (tl::Exception &ex) { - ut::print_error (ex.msg ()); + tl::error << ex.msg (); error = true; } EXPECT_EQ (error, false) @@ -4053,7 +4053,7 @@ TEST(99) try { db::LayerMap map = reader.read (layout); } catch (tl::Exception &ex) { - ut::print_error (ex.msg ()); + tl::error << ex.msg (); error = true; } EXPECT_EQ (error, false) diff --git a/src/unit_tests/tlEvents.cc b/src/unit_tests/tlEvents.cc index a91ae0e8a..29bbba07d 100644 --- a/src/unit_tests/tlEvents.cc +++ b/src/unit_tests/tlEvents.cc @@ -24,6 +24,7 @@ #include "tlEvents.h" #include "utHead.h" +#include // Object with event class Observed diff --git a/src/ut/ut.pro b/src/ut/ut.pro index 4633385ca..2ca3b436d 100644 --- a/src/ut/ut.pro +++ b/src/ut/ut.pro @@ -11,10 +11,14 @@ TEMPLATE = lib # Input HEADERS = \ utHead.h \ - utCommon.h + utTestBase.h \ + utTestConsole.h \ + utCommon.h SOURCES = \ utMain.cc \ + utTestConsole.cc \ + utTestBase.cc \ INCLUDEPATH = ../tl ../db ../gsi ../lay ../ext ../lib DEPENDPATH = ../tl ../db ../gsi ../lay ../ext ../lib diff --git a/src/ut/utHead.h b/src/ut/utHead.h index a9c06c198..5177bc3e4 100644 --- a/src/ut/utHead.h +++ b/src/ut/utHead.h @@ -24,482 +24,20 @@ #ifndef HDR_utHead #define HDR_utHead -#include -#include -#include -#include -#include -#include - #include "utCommon.h" -#include "tlString.h" -#include "tlException.h" -#include "tlStaticObjects.h" -#include "dbStatic.h" -#include "dbTypes.h" -#include "gsiExpression.h" -#include "gsiInterpreter.h" - -#include -#include - -namespace db { - class Layout; - class LayerMap; -} - -namespace rba { - class RubyInterpreter; -} - -namespace pya { - class PythonInterpreter; -} +#include "utTestBase.h" +#include "utTestConsole.h" namespace ut { +extern tl::LogTee ctrl; +extern tl::LogTee noctrl; + /** * @brief The unit test execution function */ UT_PUBLIC int main (int argc, char **argv); -/** - * @brief Prints a error message to the unit test output stream - */ -UT_PUBLIC void print_error (const std::string &s); - -/** - * @brief A detailed diff printer - */ -UT_PUBLIC void write_detailed_diff (std::ostream &os, const std::string &subject, const std::string &ref); - -/** - * @brief Returns true, if the test is run in verbose mode - * Verbose mode is enabled through the "-v" option - */ -UT_PUBLIC bool verbose (); - -/** - * @brief Returns the Ruby interpreter - */ -UT_PUBLIC rba::RubyInterpreter *ruby_interpreter (); - -/** - * @brief Returns the Python interpreter - */ -UT_PUBLIC pya::PythonInterpreter *python_interpreter (); - -/** - * @brief Returns true, if the unit test is run in debug mode - * In debug mode, the unit tests shall offer information on how to fix the - * test. Specifically if layout compare is involved, it shall display the golden - * file name and the actual one and terminate to allow updating the files. - */ -UT_PUBLIC bool is_debug_mode (); - -/** - * @brief Specifies the normalization mode for compare_layouts - */ -enum NormalizationMode -{ - NoNormalization, // no normalization - take the test subject as it is - WriteGDS2, // normalize subject by writing to GDS2 and reading back - WriteOAS // normalize subject by writing to OASIS and reading back -}; - -/** - * @brief Gets the path of the test data - * This path is specified through the environment variable $TESTSRC - */ -UT_PUBLIC std::string testsrc (); - -/** - * @brief Gets the path of the private test data - * This path is specified through the environment variable $TESTSRC and the - * private testdata directory. If no private test data is available, this - * method will throw a CancelException which makes the test skipped. - */ -UT_PUBLIC std::string testsrc_private (); - -/** - * @brief A basic exception for the unit test framework - */ -struct Exception - : public tl::Exception -{ - Exception (const std::string &msg) - : tl::Exception (msg) - { } -}; - -/** - * @brief A utility class to capture the warning, error and info channels - * - * Instantiate this class inside a test. Then run the test and finally - * obtain the collected output with CaptureChannel::captured_text(). - */ -class UT_PUBLIC CaptureChannel : public tl::Channel -{ -public: - CaptureChannel (); - - std::string captured_text () const - { - return m_text.str (); - } - - void clear () - { - m_text.str (std::string ()); - } - -protected: - virtual void puts (const char *s); - virtual void endl (); - virtual void end (); - virtual void begin (); - -private: - std::ostringstream m_text; -}; - -/** - * @brief A generic compare operator - */ -template -inline bool equals (const X &a, const Y &b) -{ - return a == b; -} - -/** - * @brief A specialization of the compare operator for doubles - */ -UT_PUBLIC bool equals (double a, double b); - -/** - * @brief Specialization of comparison of pointers vs. integers (specifically "0") - */ -template -inline bool equals (X *a, int b) -{ - return a == (X *) size_t (b); -} - -/** - * @brief A specialization of comparison of double vs "anything" - */ -template -inline bool equals (double a, const Y &b) -{ - return equals (a, double (b)); -} - -/** - * @brief A specialization of comparison of "anything" vs. double - */ -template -inline bool equals (const X &a, double b) -{ - return equals (double (a), b); -} - -/** - * @brief A specialization of the compare operator for const char * - */ -inline bool equals (const char *a, const char *b) -{ - return equals (std::string (a), std::string (b)); -} - -/** - * @brief A specialization of the compare operator for std::string vs. const char * - */ -inline bool equals (const std::string &a, const char *b) -{ - return equals (a, std::string (b)); -} - -/** - * @brief A specialization of the compare operator for std::string vs. const char * - */ -inline bool equals (const char *a, const std::string &b) -{ - return equals (std::string (a), b); -} - -/** - * @brief The base class for tests - */ -struct UT_PUBLIC TestBase -{ - /** - * @brief Constructor - */ - TestBase (const std::string &file, const std::string &name); - - /** - * @brief Destructor - */ - virtual ~TestBase () { } - - /** - * @brief Actually runs the test - * @return True, if the test was successful - */ - bool do_test (const std::string &mode); - - /** - * @brief Raises an exception with the given string - * This version prints the last checkpoint for reference. - */ - void raise (const std::string &s); - - /** - * @brief Raises an exception with the given string, file and line number - */ - void raise (const std::string &file, int line, const std::string &s); - - /** - * @brief Registers a checkpoint - */ - void checkpoint (const std::string &file, int line); - - /** - * @brief Resets the checkpoints set - */ - void reset_checkpoint (); - - /** - * @brief Compares a layout with a golden layout file - * @param layout The layout to compare - * @param au_file The golden file path - * @param norm The normalization mode (see NormalizationMode) - * @param tolerance A tolerance applied when comparing shapes in database units - * The layout is normalized by writing to a file and reading back - */ - void compare_layouts (const db::Layout &layout, const std::string &au_file, NormalizationMode norm = WriteGDS2, db::Coord tolerance = 0); - - /** - * @brief Compares a layout with a golden layout file with layer mapping - * @param layout The layout to compare - * @param au_file The golden file path - * @param lmap The layer mapping object - * @param read_all_others If true, all other layers will be read too - * @param norm The normalization mode (see NormalizationMode) - * @param tolerance A tolerance applied when comparing shapes in database units - * The layout is normalized by writing to a file and reading back - */ - void compare_layouts (const db::Layout &layout, const std::string &au_file, const db::LayerMap &lmap, bool read_all_others, NormalizationMode norm = WriteGDS2, db::Coord tolerance = 0); - - /** - * @brief Compares two text files - */ - void compare_text_files (const std::string &path_a, const std::string &path_b); - - /** - * @brief The test's name - * @return The name of the test - */ - const std::string &name () const - { - return m_test; - } - - /** - * @brief Prepares a temporary file path - * @param fn The actual name of the file - * @return A path suitable for writing a temporary file - * The directory for the file will be created within this method. - */ - std::string tmp_file (const std::string &fn = "tmp") const; - - /** - * @brief Removes all temporary files - */ - void remove_tmp_folder (); - - /** - * @brief A generic diff printer - */ - template - void diff (const std::string &file, int line, const std::string &msg, const X &subject, const Y & /*ref*/) - { - std::ostringstream sstr; - sstr << msg << " (actual value is " << subject << ")"; - raise (file, line, sstr.str ()); - } - - /** - * @brief A generic diff printer - */ - template - void detailed_diff (const std::string &file, int line, const std::string &msg, const X &subject, const Y &ref) - { - std::ostringstream sstr; - sstr << msg << std::endl; - ut::write_detailed_diff (sstr, tl::to_string (subject), tl::to_string (ref)); - raise (file, line, sstr.str ()); - } - - /** - * @brief A diff printer for int vs. something - */ - template - void diff (const std::string &file, int line, const std::string &msg, int subject, const Y &ref) - { - detailed_diff (file, line, msg, subject, ref); - } - - /** - * @brief A diff printer for unsigned int vs. something - */ - template - void diff (const std::string &file, int line, const std::string &msg, unsigned int subject, const Y &ref) - { - detailed_diff (file, line, msg, subject, ref); - } - - /** - * @brief A diff printer for long vs. something - */ - template - void diff (const std::string &file, int line, const std::string &msg, long subject, const Y &ref) - { - detailed_diff (file, line, msg, subject, ref); - } - - /** - * @brief A diff printer for unsigned long vs. something - */ - template - void diff (const std::string &file, int line, const std::string &msg, unsigned long subject, const Y &ref) - { - detailed_diff (file, line, msg, subject, ref); - } - - /** - * @brief A diff printer for long long vs. something - */ - template - void diff (const std::string &file, int line, const std::string &msg, long long subject, const Y &ref) - { - detailed_diff (file, line, msg, subject, ref); - } - - /** - * @brief A diff printer for unsigned long long vs. something - */ - template - void diff (const std::string &file, int line, const std::string &msg, unsigned long long subject, const Y &ref) - { - detailed_diff (file, line, msg, subject, ref); - } - - /** - * @brief A diff printer for bool - */ - inline void diff (const std::string &file, int line, const std::string &msg, bool subject, bool ref) - { - detailed_diff (file, line, msg, subject, ref); - } - - /** - * @brief A diff printer for double - */ - inline void diff (const std::string &file, int line, const std::string &msg, double subject, double ref) - { - detailed_diff (file, line, msg, subject, ref); - } - - /** - * @brief A diff printer for strings - */ - inline void diff (const std::string &file, int line, const std::string &msg, const std::string &subject, const std::string &ref) - { - detailed_diff (file, line, msg, subject, ref); - } - - /** - * @brief A diff printer for strings vs. const char * - */ - inline void diff (const std::string &file, int line, const std::string &msg, const std::string &subject, const char *ref) - { - detailed_diff (file, line, msg, subject, ref); - } - - /** - * @brief A diff printer for strings vs. const char * - */ - inline void diff (const std::string &file, int line, const std::string &msg, const char *subject, const std::string &ref) - { - detailed_diff (file, line, msg, subject, ref); - } - - /** - * @brief A diff printer for C strings - */ - inline void diff (const std::string &file, int line, const std::string &msg, const char *subject, const char *ref) - { - diff (file, line, msg, std::string (subject), std::string (ref)); - } - - /** - * @brief Main entry point for the compare feature (EXPECT_EQ and EXPECT_NE) - */ - template - void eq_helper (bool eq, const T1 &a, const T2 &b, const char *what_expr, const char *equals_expr, const char *file, int line) - { - if (ut::equals (a, b) != eq) { - std::ostringstream sstr; - sstr << what_expr << " does not equal " << equals_expr; - diff (file, line, sstr.str (), a, b); - } - } - -private: - virtual void execute (ut::TestBase *_this) throw (tl::Exception) = 0; - - std::string m_test; - std::string m_testdir; - // last checkpoint - std::string m_cp_file; - int m_cp_line; - bool m_any_failed; - QString m_testtmp; -}; - -/** - * @brief The registration facility for tests - */ -struct Registrar -{ - static void reg (ut::TestBase *t) - { - if (! ms_instance) { - ms_instance = new Registrar (); - } - ms_instance->m_tests.push_back (t); - } - - static Registrar *instance () - { - return ms_instance; - } - - const std::vector &tests () const - { - return m_tests; - } - -private: - static Registrar *ms_instance; - - Registrar () : m_tests () { } - - std::vector m_tests; -}; - } // namespace ut #define TEST(NAME) \ diff --git a/src/ut/utMain.cc b/src/ut/utMain.cc index aa8e09718..ba6aa4fa6 100644 --- a/src/ut/utMain.cc +++ b/src/ut/utMain.cc @@ -20,110 +20,33 @@ */ - #include "utHead.h" -#include "rba.h" -#include "pya.h" #include "tlStaticObjects.h" #include "tlTimer.h" #include "tlSystemPaths.h" -#include "tlFileUtils.h" +#include "tlCommandLineParser.h" #include "layApplication.h" -#include "gsiExpression.h" -#include "gsiExternalMain.h" +#include "rba.h" +#include "pya.h" #include "gsiDecl.h" -#include "dbStatic.h" +#include "gsiExternalMain.h" -#include "dbLayoutDiff.h" -#include "dbWriter.h" -#include "dbGDS2Reader.h" -#include "dbOASISReader.h" -#include "dbGDS2Writer.h" -#include "dbOASISWriter.h" -#include "dbGDS2Reader.h" - -#include -#include +#include +#include +#include #if !defined(_WIN32) -# include # include #endif #if defined(_WIN32) # include #endif -#include -#include -#include - namespace ut { -// -------------------------------------------------------------------------------------- - -ut::Registrar *ut::Registrar::ms_instance = 0; - -static bool s_verbose_flag = false; -static bool s_debug_mode = false; -static bool s_continue_flag = false; -static pya::PythonInterpreter *sp_python_interpreter = 0; -static rba::RubyInterpreter *sp_ruby_interpreter = 0; - -bool verbose () -{ - return s_verbose_flag; -} - -pya::PythonInterpreter *python_interpreter () -{ - return sp_python_interpreter; -} - -rba::RubyInterpreter *ruby_interpreter () -{ - return sp_ruby_interpreter; -} - -bool is_debug_mode () -{ - return s_debug_mode; -} - -std::string testsrc () -{ - const char *ts = getenv ("TESTSRC"); - if (! ts) { - throw tl::Exception ("TESTSRC undefined"); - } - return ts; -} - -std::string testsrc_private () -{ - QDir d (QDir (tl::to_qstring (ut::testsrc ())).filePath (QString::fromUtf8 ("private"))); - if (! d.exists ()) { - throw tl::CancelException (); - } - return tl::to_string (d.path ()); -} - - -bool equals (double a, double b) -{ - double m = fabs (0.5 * (a + b)); - if (m < 1e-30) { - // resolution limit is 1e-30 - return true; - } else { - double d = fabs (a - b); - // we consider two values equal for the purpose of unit tests if they have the - // same value within 1e-10 (0.00000001%). - return d < 1e-10 * m; - } -} - -std::string replicate (const char *s, size_t n) +// TODO: move this to tlString.h +static std::string replicate (const char *s, size_t n) { std::string res; res.reserve (strlen (s) * n); @@ -134,394 +57,8 @@ std::string replicate (const char *s, size_t n) return res; } -void print_error (const std::string &s) -{ - tl::error << "ERROR: " << s; -} - -// ------------------------------------------------ -// CaptureChannel implementation - -CaptureChannel::CaptureChannel () -{ - tl::info.add (this, false); - tl::error.add (this, false); - tl::warn.add (this, false); -} - -void CaptureChannel::puts (const char *s) -{ - m_text << s; -} - -void CaptureChannel::endl () -{ - m_text << "\n"; -} - -void CaptureChannel::end () -{ - // .. nothing yet .. -} - -void CaptureChannel::begin () -{ - // .. nothing yet .. -} - -// ------------------------------------------------ -// tl::Channel implementations for redirecting the log output - -const char *ANSI_RED = "\033[31;1m"; -const char *ANSI_BLUE = "\033[34m"; -const char *ANSI_GREEN = "\033[32m"; -const char *ANSI_RESET = "\033[0m"; - -/** - * @brief Redirects the interpreter output to ut::print_stdout and ut::print_stderr - */ -class TestConsole - : public gsi::Console -{ -public: - static TestConsole *instance () - { - tl_assert (ms_instance != 0); - return ms_instance; - } - - TestConsole (FILE *file, bool xml_format) - : m_file (file), m_xml_format (xml_format), m_col (0), m_max_col (250), m_columns (50), m_rows (0), m_is_tty (false) - { - ms_instance = this; - m_indent = 4; - - m_is_tty = isatty (fileno (file)) && ! xml_format; - -#if !defined(_WIN32) - if (m_is_tty) { - struct winsize ws; - ioctl (fileno (stdout), TIOCGWINSZ, &ws); - m_columns = std::max (0, (int) ws.ws_col); - m_rows = std::max (0, (int) ws.ws_row); - } -#endif - } - - ~TestConsole () - { - if (ms_instance == this) { - ms_instance = 0; - } - } - - int indent () const - { - return m_indent; - } - - bool xml_format () const - { - return m_xml_format; - } - - void write_str (const char *text, output_stream os) - { - if (os == OS_stderr) { - begin_error (); - basic_write (text); - end (); - } else { - basic_write (text); - } - } - - void raw_write (const char *text) - { - fputs (text, m_file); - } - - virtual void flush () - { - fflush (m_file); - } - - virtual bool is_tty () - { - // NOTE: this assumes we are delivering to stdout - return m_is_tty; - } - - virtual int columns () - { - return std::max (m_columns - m_indent, 0); - } - - virtual int rows () - { - return m_rows; - } - - int real_columns () - { - return m_columns; - } - - void begin_error () - { - if (m_is_tty) { - fputs (ANSI_RED, m_file); - } - } - - void begin_info () - { - if (m_is_tty) { - fputs (ANSI_GREEN, m_file); - } - } - - void begin_warn () - { - if (m_is_tty) { - fputs (ANSI_BLUE, m_file); - } - } - - void end () - { - if (m_is_tty) { - fputs (ANSI_RESET, m_file); - } - } - - void basic_write (const char *s) - { - if (m_xml_format) { - - for (const char *cp = s; *cp; ++cp) { - if (*cp == '&') { - fputs ("&", m_file); - } else if (*cp == '<') { - fputs ("<", m_file); - } else if (*cp == '>') { - fputs (">", m_file); - } else { - fputc (*cp, m_file); - } - } - - } else { - - // line length limitation - this assumes we are always printing to the same terminal - // or we don't mix stderr/stdout. - const char *cp; - for (cp = s; *cp; ++cp) { - if (*cp == '\n' || *cp == '\r') { - m_col = 0; - fputc (*cp, m_file); - } else { - if (m_col == 0) { - for (int i = 0; i < m_indent; ++i) { - fputc (' ', m_file); - } - m_col = m_indent; - } - if (m_col > m_max_col) { - // ignore char - } else if (m_col == m_max_col) { - fputs (" ...", m_file); - ++m_col; - } else if (*cp == '\033') { - // skip ANSI escape sequences (no increment of s_col) - const char *cpend = cp + 1; - if (*cpend == '[') { - ++cpend; - while (*cpend && *cpend != 'm') { - ++cpend; - } - if (*cpend) { - ++cpend; - } - } - while (cp != cpend) { - fputc (*cp++, m_file); - } - --cp; - } else { - fputc (*cp, m_file); - ++m_col; - } - } - } - - } - } - -private: - FILE *m_file; - bool m_xml_format; - int m_col; - int m_max_col; - int m_columns, m_rows; - bool m_is_tty; - int m_indent; - static TestConsole *ms_instance; -}; - -TestConsole *TestConsole::ms_instance = 0; - -class InfoChannel : public tl::Channel -{ -public: - InfoChannel (int verbosity) - : m_verbosity (verbosity) - { - // .. nothing yet .. - } - -protected: - virtual void puts (const char *s) - { - if (tl::verbosity () >= m_verbosity) { - TestConsole::instance ()->basic_write (s); - } - } - - virtual void endl () - { - if (tl::verbosity () >= m_verbosity) { - TestConsole::instance ()->basic_write ("\n"); - } - } - - virtual void end () - { - TestConsole::instance ()->flush (); - } - - virtual void begin () - { - // .. nothing yet .. - } - -private: - int m_verbosity; -}; - -class WarningChannel : public tl::Channel -{ -public: - WarningChannel () - { - // .. nothing yet .. - } - -protected: - virtual void puts (const char *s) - { - TestConsole::instance ()->basic_write (s); - } - - virtual void endl () - { - TestConsole::instance ()->basic_write ("\n"); - } - - virtual void end () - { - TestConsole::instance ()->end (); - TestConsole::instance ()->flush (); - } - - virtual void begin () - { - TestConsole::instance ()->begin_warn (); - } -}; - -class ErrorChannel : public tl::Channel -{ -public: - ErrorChannel () - { - // .. nothing yet .. - } - -protected: - virtual void puts (const char *s) - { - TestConsole::instance ()->basic_write (s); - } - - virtual void endl () - { - TestConsole::instance ()->basic_write ("\n"); - } - - virtual void end () - { - TestConsole::instance ()->end (); - TestConsole::instance ()->flush (); - } - - virtual void begin () - { - TestConsole::instance ()->begin_error (); - } -}; - -void write_detailed_diff (std::ostream &os, const std::string &subject, const std::string &ref) -{ - os << replicate (" ", TestConsole::instance ()->indent ()) << "Actual value is: " << tl::to_string (subject) << std::endl - << replicate (" ", TestConsole::instance ()->indent ()) << "Reference value is: " << tl::to_string (ref) << std::endl - ; -} - -class CtrlChannel : public tl::Channel -{ -public: - CtrlChannel (bool with_xml) - : m_with_xml (with_xml) - { - // .. nothing yet .. - } - -protected: - virtual void puts (const char *s) - { - if (m_with_xml == TestConsole::instance ()->xml_format ()) { - TestConsole::instance ()->raw_write (s); - } - } - - virtual void endl () - { - if (m_with_xml == TestConsole::instance ()->xml_format ()) { - TestConsole::instance ()->raw_write ("\n"); - } - } - - virtual void end () - { - if (m_with_xml == TestConsole::instance ()->xml_format ()) { - TestConsole::instance ()->end (); - TestConsole::instance ()->flush (); - } - } - - virtual void begin () - { - if (m_with_xml == TestConsole::instance ()->xml_format ()) { - TestConsole::instance ()->begin_info (); - } - } - -private: - bool m_with_xml; -}; - -std::string +// TODO: move this to tlString.h +static std::string escape_xml (const std::string &s) { std::string res; @@ -541,302 +78,6 @@ escape_xml (const std::string &s) return res; } -tl::LogTee noctrl (new CtrlChannel (false), true); -tl::LogTee ctrl (new CtrlChannel (true), true); - -// -------------------------------------------------------------------------------------- -// TestBase implementation - -static QDir testtmp () -{ - // Ensures the test temp directory is present - const char *tt = getenv ("TESTTMP"); - if (! tt) { - throw tl::Exception ("TESTTMP undefined"); - } - - return QDir (tl::to_qstring (tt)); -} - -TestBase::TestBase (const std::string &file, const std::string &name) - : m_cp_line (0), m_any_failed (false) -{ - QFileInfo f (tl::to_qstring (file)); - m_test = tl::to_string (f.baseName ()) + ":" + name; - m_testdir = tl::to_string (f.baseName ()) + "_" + name; - ut::Registrar::reg (this); -} - -bool TestBase::do_test (const std::string & /*mode*/) -{ - ut::ctrl << ""; - - try { - - // Ensures the test temp directory is present - QDir dir (testtmp ()); - QDir tmpdir (dir.absoluteFilePath (tl::to_qstring (m_testdir))); - if (tmpdir.exists () && ! tl::rm_dir_recursive (tmpdir.absolutePath ())) { - throw tl::Exception ("Unable to clean temporary dir: " + tl::to_string (tmpdir.absolutePath ())); - } - if (! dir.mkpath (tl::to_qstring (m_testdir))) { - throw tl::Exception ("Unable to create path for temporary files: " + tl::to_string (tmpdir.absolutePath ())); - } - dir.cd (tl::to_qstring (m_testdir)); - - m_testtmp = dir.absolutePath (); - - static std::string testname_value; - static std::string testtmp_value; - - putenv (const_cast ("TESTNAME=")); - testname_value = std::string ("TESTNAME=") + m_test; - putenv (const_cast (testname_value.c_str ())); - - putenv (const_cast ("TESTTMP_WITH_NAME=")); - testtmp_value = std::string ("TESTTMP_WITH_NAME=") + m_testtmp.toUtf8().constData(); - putenv (const_cast (testtmp_value.c_str ())); - - reset_checkpoint (); - - tl::Timer timer; - timer.start(); - - execute (this); - - timer.stop(); - - m_testtmp.clear (); - - ut::ctrl << ""; - - ut::noctrl << "Time: " << timer.sec_wall () << "s (wall) " << timer.sec_user () << "s (user) " << timer.sec_sys () << "s (sys)"; - ut::ctrl << ""; - - } catch (...) { - ut::ctrl << ""; - throw; - } - - return (!m_any_failed); -} - -std::string TestBase::tmp_file (const std::string &fn) const -{ - tl_assert (! m_testtmp.isEmpty ()); - QDir dir (m_testtmp); - return tl::to_string (dir.absoluteFilePath (tl::to_qstring (fn))); -} - -/** - * @brief Recursively empties a directory - */ -static void empty_dir (QDir dir) -{ - QStringList entries = dir.entryList (QDir::AllEntries | QDir::NoDotAndDotDot); - for (QStringList::const_iterator e = entries.begin (); e != entries.end (); ++e) { - QString epath = dir.absoluteFilePath (*e); - if (QFileInfo (epath).isDir ()) { - empty_dir (QDir (epath)); - dir.rmdir (*e); - } else if (! dir.remove (*e)) { - throw tl::Exception ("Unable to remove file or directory: " + tl::to_string (dir.filePath (*e))); - } - } -} - -void TestBase::remove_tmp_folder () -{ - // Ensures the test temp directory is present - QDir dir (testtmp ()); - if (dir.cd (tl::to_qstring (m_test))) { - - empty_dir (dir); - - dir.cdUp (); - if (! dir.rmdir (tl::to_qstring (m_test))) { - throw tl::Exception ("Unable to remove directory: " + tl::to_string (dir.filePath (tl::to_qstring (m_test)))); - } - - } -} - -void TestBase::checkpoint (const std::string &file, int line) -{ - m_cp_file = file; - m_cp_line = line; -} - -void TestBase::reset_checkpoint () -{ - m_cp_file = std::string (); - m_cp_line = 0; -} - -void TestBase::raise (const std::string &file, int line, const std::string &msg) -{ - std::ostringstream sstr; - sstr << file << ", line " << line << ": " << msg; - if (s_continue_flag) { - tl::error << sstr.str (); - m_any_failed = true; - } else { - throw ut::Exception (sstr.str ()); - } -} - -void TestBase::raise (const std::string &msg) -{ - std::ostringstream sstr; - if (m_cp_line > 0) { - sstr << "(last checkpoint: " << m_cp_file << ", line " << m_cp_line << "): "; - } - sstr << msg; - if (s_continue_flag) { - tl::error << sstr.str (); - m_any_failed = true; - } else { - throw ut::Exception (sstr.str ()); - } -} - -void TestBase::compare_layouts (const db::Layout &layout, const std::string &au_file, NormalizationMode norm, db::Coord tolerance) -{ - compare_layouts (layout, au_file, db::LayerMap (), true, norm, tolerance); -} - -void TestBase::compare_layouts (const db::Layout &layout, const std::string &au_file, const db::LayerMap &lm, bool read_other_layers, NormalizationMode norm, db::Coord tolerance) -{ - // normalize the layout by writing to GDS and reading from .. - - // generate a "unique" name ... - unsigned int hash = 0; - for (const char *cp = au_file.c_str (); *cp; ++cp) { - hash = (hash << 4) ^ (hash >> 4) ^ ((unsigned int) *cp); - } - - std::string tmp_file; - - if (norm == WriteGDS2) { - - tmp_file = ut::TestBase::tmp_file (tl::sprintf ("tmp_%x.gds", hash)); - - tl::OutputStream stream (tmp_file.c_str ()); - db::GDS2Writer writer; - db::SaveLayoutOptions options; - writer.write (const_cast (layout), stream, options); - - } else { - - tmp_file = ut::TestBase::tmp_file (tl::sprintf ("tmp_%x.oas", hash)); - - tl::OutputStream stream (tmp_file.c_str ()); - db::OASISWriter writer; - db::SaveLayoutOptions options; - writer.write (const_cast (layout), stream, options); - - } - - const db::Layout *subject = 0; - db::Layout layout2; - - if (norm != NoNormalization) { - - // read all layers from the original layout, so the layer table is the same - for (db::Layout::layer_iterator l = layout.begin_layers (); l != layout.end_layers (); ++l) { - layout2.insert_layer ((*l).first, *(*l).second); - } - - tl::InputStream stream (tmp_file); - db::Reader reader (stream); - reader.read (layout2); - - subject = &layout2; - - } else { - subject = &layout; - } - - bool equal = false; - bool any = false; - - int n = 0; - for ( ; ! equal; ++n) { - - db::Layout layout_au; - - // read all layers from the original layout, so the layer table is the same - for (db::Layout::layer_iterator l = layout.begin_layers (); l != layout.end_layers (); ++l) { - layout_au.insert_layer ((*l).first, *(*l).second); - } - - db::LoadLayoutOptions options; - options.get_options ().layer_map = lm; - options.get_options ().create_other_layers = read_other_layers; - - std::string fn = au_file; - if (n > 0) { - fn += tl::sprintf (".%d", n); - } - - if (QFileInfo (tl::to_qstring (fn)).exists ()) { - - if (n == 1 && any) { - throw tl::Exception (tl::sprintf ("Inconsistent reference variants for %s: there can be either variants (.1,.2,... suffix) or a single file (without suffix)", au_file)); - } - - any = true; - - tl::InputStream stream (fn); - db::Reader reader (stream); - reader.read (layout_au, options); - - equal = db::compare_layouts (*subject, layout_au, (n > 0 ? db::layout_diff::f_silent : db::layout_diff::f_verbose) | db::layout_diff::f_flatten_array_insts /*| db::layout_diff::f_no_text_details | db::layout_diff::f_no_text_orientation*/, tolerance, 100 /*max diff lines*/); - if (equal && n > 0) { - tl::info << tl::sprintf ("Found match on golden reference variant %s", fn); - } - - } else if (n > 0) { - if (! any) { - tl::warn << tl::sprintf ("No golden data found (%s)", au_file); - } - break; - } - - } - - if (! equal) { - raise (tl::sprintf ("Compare failed - see\n actual: %s\n golden: %s%s", - tl::to_string (QFileInfo (tl::to_qstring (tmp_file)).absoluteFilePath ()), - tl::to_string (QFileInfo (tl::to_qstring (au_file)).absoluteFilePath ()), - (n > 1 ? "\nand variants" : ""))); - } -} - -static std::string read_file (const std::string &path) -{ - QFile file (tl::to_qstring (path)); - if (! file.open (QIODevice::ReadOnly | QIODevice::Text)) { - tl::warn << tl::sprintf ("Unable to open file %s", path); - } - - QByteArray ba = file.readAll (); - return std::string (ba.constData (), 0, ba.size ()); -} - -void TestBase::compare_text_files (const std::string &path_a, const std::string &path_b) -{ - std::string text_a = read_file (path_a); - std::string text_b = read_file (path_b); - - if (text_a != text_b) { - raise (tl::sprintf ("Compare failed - see:\n file 1: %s\n file 2: %s", - tl::to_string (QFileInfo (tl::to_qstring (path_a)).absoluteFilePath ()), - tl::to_string (QFileInfo (tl::to_qstring (path_b)).absoluteFilePath ()))); - } -} - - static int main_cont (int argc, char **argv); int @@ -851,197 +92,371 @@ main (int argc, char **argv) return ret; } +int +run_tests (const std::vector &selected_tests, bool editable, bool non_editable, bool slow, lay::Application &app, bool gsi_coverage, const std::vector &class_names_vector) +{ + std::set class_names; + class_names.insert (class_names_vector.begin (), class_names_vector.end ()); + + tl::Timer grand_timer; + grand_timer.start (); + + int failed_ne = 0, failed_e = 0; + std::vector failed_tests_e, failed_tests_ne; + int skipped_ne = 0, skipped_e = 0; + std::vector skipped_tests_e, skipped_tests_ne; + + for (int e = 0; e < 2; ++e) { + + if ((non_editable && e == 0) || (editable && e == 1)) { + + std::string mode (e == 0 ? "non-editable" : "editable"); + ut::ctrl << ""; + + ut::noctrl << replicate ("=", TestConsole::instance ()->real_columns ()); + ut::noctrl << "Running tests in " << mode << " mode ..."; + app.set_editable (e != 0); + + int failed = 0; + std::vector failed_tests; + int skipped = 0; + std::vector skipped_tests; + + tl::Timer timer; + + timer.start (); + + try { + + failed = 0; + failed_tests.clear (); + skipped = 0; + skipped_tests.clear (); + + for (std::vector ::const_iterator t = selected_tests.begin (); t != selected_tests.end (); ++t) { + (*t)->remove_tmp_folder (); + } + + for (std::vector ::const_iterator t = selected_tests.begin (); t != selected_tests.end (); ++t) { + + ut::ctrl << "name () << "\">"; + + ut::noctrl << replicate ("-", TestConsole::instance ()->real_columns ()); + ut::noctrl << "Running " << (*t)->name (); + + try { + + if (! (*t)->do_test (e != 0, slow)) { + + ut::ctrl << "name ()) << " failed (continued mode - see previous messages)" << "\"/>"; + tl::error << "Test " << (*t)->name () << " failed (continued mode - see previous messages)"; + + failed_tests.push_back (*t); + ++failed; + + } + + } catch (tl::CancelException &) { + + ut::ctrl << ""; + tl::error << "Test " << (*t)->name () << " skipped"; + + skipped_tests.push_back (*t); + ++skipped; + + } catch (tl::Exception &ex) { + + ut::ctrl << ""; + tl::error << "Test " << (*t)->name () << " failed:"; + tl::info << ex.msg (); + + failed_tests.push_back (*t); + ++failed; + + } + + ut::ctrl << ""; + + } + + } catch (tl::Exception &ex) { + tl::error << "Caught tl::exception: " << ex.msg (); + failed = 1; + } catch (std::exception &ex) { + tl::error << "Caught std::exception: " << std::string (ex.what ()); + failed = 1; + } catch (...) { + tl::error << "Caught unspecific exception"; + failed = 1; + } + + timer.stop (); + + ut::ctrl << ""; + + ut::noctrl << replicate ("=", TestConsole::instance ()->real_columns ()); + ut::noctrl << "Summary"; + + if (skipped > 0) { + if (e == 0) { + skipped_tests_ne = skipped_tests; + skipped_ne = skipped; + } else { + skipped_tests_e = skipped_tests; + skipped_e = skipped; + } + tl::warn << skipped << " test(s) skipped"; + } + + if (failed > 0) { + if (e == 0) { + failed_tests_ne = failed_tests; + failed_ne = failed; + } else { + failed_tests_e = failed_tests; + failed_e = failed; + } + tl::warn << failed << " test(s) failed"; + } else { + tl::info << "All tests passed in " << mode << " mode."; + } + + ut::ctrl << ""; + + ut::noctrl << "Total time: " << timer.sec_wall () << "s (wall) " << timer.sec_user () << "s (user) " << timer.sec_sys () << "s (sys)"; + ut::ctrl << ""; + + ut::ctrl << ""; + + } + + } + + grand_timer.stop (); + + // GSI diagnostics: print all methods that have not been called + if (gsi_coverage) { + + ut::noctrl << replicate ("=", TestConsole::instance ()->real_columns ()); + ut::noctrl << "GSI coverage test"; + + ut::ctrl << ""; + + bool first = true; + for (gsi::ClassBase::class_iterator c = gsi::ClassBase::begin_classes (); c != gsi::ClassBase::end_classes (); ++c) { + + if (gsi_coverage && !class_names.empty () && class_names.find (c->name ()) == class_names.end ()) { + continue; + } + + bool first_of_class = true; + for (gsi::ClassBase::method_iterator m = c->begin_methods (); m != c->end_methods (); ++m) { + + if (!dynamic_cast (*m) && !(*m)->was_called ()) { + + if (first) { + first = false; + tl::warn << "GSI coverage test failed - the following methods were not called:"; + } + if (first_of_class) { + tl::warn << replicate (" ", TestConsole::instance ()->indent ()) << "Class " << c->name (); + first_of_class = false; + } + tl::warn << replicate (" ", TestConsole::instance ()->indent () * 2) << (*m)->to_string (); + + } + + } + + } + + if (first) { + tl::info << "GSI coverage test passed."; + } + + ut::ctrl << ""; + + } + + ut::noctrl << ut::replicate ("=", TestConsole::instance ()->real_columns ()); + ut::noctrl << "Grand Summary"; + + ut::ctrl << ""; + + if (skipped_e + skipped_ne > 0) { + if (non_editable) { + tl::warn << "Skipped in non-editable mode"; + for (std::vector ::const_iterator f = skipped_tests_ne.begin (); f != skipped_tests_ne.end (); ++f) { + tl::warn << replicate (" ", TestConsole::instance ()->indent ()) << (*f)->name (); + } + } + if (editable) { + tl::warn << "Skipped in editable mode"; + for (std::vector ::const_iterator f = skipped_tests_e.begin (); f != skipped_tests_e.end (); ++f) { + tl::warn << replicate (" ", TestConsole::instance ()->indent ()) << (*f)->name (); + } + } + tl::warn << tl::to_string (skipped_e + skipped_ne) << " test(s) skipped"; + } + + int result = failed_e + failed_ne; + if (result > 0) { + if (non_editable) { + tl::warn << "Failed in non-editable mode"; + for (std::vector ::const_iterator f = failed_tests_ne.begin (); f != failed_tests_ne.end (); ++f) { + tl::warn << replicate (" ", TestConsole::instance ()->indent ()) << (*f)->name (); + } + } + if (editable) { + tl::warn << "Failed in editable mode"; + for (std::vector ::const_iterator f = failed_tests_e.begin (); f != failed_tests_e.end (); ++f) { + tl::warn << replicate (" ", TestConsole::instance ()->indent ()) << (*f)->name (); + } + } + tl::warn << tl::to_string (result) << " test(s) failed"; + } else { + tl::info << "All tests passed."; + } + + ut::ctrl << ""; + + ut::noctrl << "Grand total time: " << grand_timer.sec_wall () << "s (wall) " << grand_timer.sec_user () << "s (user) " << grand_timer.sec_sys () << "s (sys)"; + ut::ctrl << ""; + + return result; +} + int main_cont (int argc, char **argv) { - pya::PythonInterpreter::initialize (); - gsi::initialize_external (); - - // Search and initialize plugin unit tests - - QStringList name_filters; - name_filters << QString::fromUtf8 ("*.ut"); - - QDir inst_dir (tl::to_qstring (tl::get_inst_path ())); - QStringList inst_modules = inst_dir.entryList (name_filters); - inst_modules.sort (); - - for (QStringList::const_iterator im = inst_modules.begin (); im != inst_modules.end (); ++im) { - - QFileInfo ut_file (inst_dir.path (), *im); - if (ut_file.exists () && ut_file.isReadable ()) { - - std::string pp = tl::to_string (ut_file.absoluteFilePath ()); - tl::log << "Loading unit tests " << pp; - - // NOTE: since we are using a different suffix ("*.ut"), we can't use QLibrary. -#ifdef _WIN32 - // there is no "dlopen" on mingw, so we need to emulate it. - HINSTANCE handle = LoadLibraryW ((const wchar_t *) tl::to_qstring (pp).constData ()); - if (! handle) { - std::cerr << tl::sprintf ("Unable to load plugin tests: %s with error message: %s", pp.c_str (), GetLastError ()) << std::endl; - exit (1); - } -#else - void *handle; - handle = dlopen (tl::string_to_system (pp).c_str (), RTLD_LAZY); - if (! handle) { - std::cerr << tl::sprintf ("Unable to load plugin tests: %s", pp.c_str ()) << std::endl; - exit (1); - } -#endif - - } - - } - - // No side effects - tl::set_klayout_path (std::vector ()); - - int ac = 2; - static char av0[] = "unit_test"; - static char av1[] = "-z"; // don't show main window - static char av2[] = "-nc"; // No configuration file - static char av3[] = "-rx"; // No mplicit macros - char *av[] = { av0, av1, av2, av3, 0 }; - lay::Application app (ac, av, false); - app.autorun (); - -#if QT_VERSION < 0x050000 - QTextCodec::setCodecForTr (QTextCodec::codecForName ("utf8")); -#endif - - bool editable = true, non_editable = true; - bool exclude = false; - bool gsi_coverage = false; - bool gsi_coverage_selected = false; - std::set class_names; - std::vector test_list; - std::vector exclude_test_list; - - bool xml_format = false; - - for (int i = 1; i < argc; ++i) { - - std::string a = argv[i]; - if (a == "-h") { - - std::cout << "unit_test " << std::endl - << "Options:" << std::endl - << " -a XML output format" << std::endl - << " -l List tests and exit" << std::endl - << " -e Editable mode only" << std::endl - << " -ne Non-editable mode only" << std::endl - << " -c Continue on error" << std::endl - << " -v Verbose mode" << std::endl - << " -d debug mode (stop on error, indicate fix instructions)" << std::endl - << " -g GSI coverage mode - print GSI methods that have not been called" << std::endl - << " -gg GSI coverage mode, confined to this class (can be given multiple times)" << std::endl - << " -x Exclude following tests" << std::endl - << "Test list: list of match strings selecting some tests (default: all)" << std::endl; - return 0; - - } else if (a == "-l") { - - std::cout << "List of installed tests:" << std::endl; - for (std::vector::const_iterator i = ut::Registrar::instance()->tests ().begin (); i != ut::Registrar::instance()->tests ().end (); ++i) { - std::cout << " " << (*i)->name () << std::endl; - } - return 0; - - } else if (a == "-a") { - - xml_format = true; - - } else if (a == "-g") { - - gsi_coverage = true; - - } else if (a == "-gg") { - - gsi_coverage = true; - gsi_coverage_selected = true; - if (i + 1 < argc) { - ++i; - class_names.insert (argv [i]); - } - - } else if (a == "-e") { - - non_editable = false; - editable = true; - - } else if (a == "-ne") { - - non_editable = true; - editable = false; - - } else if (a == "-c") { - - ut::s_continue_flag = true; - - } else if (a == "-d") { - - ut::s_debug_mode = true; - - } else if (a == "-v") { - - ut::s_verbose_flag = true; - - } else if (a == "-x") { - - exclude = true; - - } else { - - if (exclude) { - exclude_test_list.push_back (a); - } else { - test_list.push_back (a); - } - - } - - } - - ut::TestConsole console (stdout, xml_format); - - // redirect the log channels - tl::warn.clear (); - tl::warn.add (new ut::WarningChannel (), true); - tl::info.clear (); - tl::info.add (new ut::InfoChannel (0), true); - tl::log.clear (); - tl::log.add (new ut::InfoChannel (10), true); - tl::error.clear (); - tl::error.add (new ut::ErrorChannel (), true); - int result = 0; try { - tl::Timer grand_timer; + pya::PythonInterpreter::initialize (); + gsi::initialize_external (); - grand_timer.start (); + // Search and initialize plugin unit tests - ut::sp_ruby_interpreter = dynamic_cast (&app.ruby_interpreter ()); - ut::sp_python_interpreter = dynamic_cast (&app.python_interpreter ()); + QStringList name_filters; + name_filters << QString::fromUtf8 ("*.ut"); + + QDir inst_dir (tl::to_qstring (tl::get_inst_path ())); + QStringList inst_modules = inst_dir.entryList (name_filters); + inst_modules.sort (); + + for (QStringList::const_iterator im = inst_modules.begin (); im != inst_modules.end (); ++im) { + + QFileInfo ut_file (inst_dir.path (), *im); + if (ut_file.exists () && ut_file.isReadable ()) { + + std::string pp = tl::to_string (ut_file.absoluteFilePath ()); + tl::log << "Loading unit tests " << pp; + + // NOTE: since we are using a different suffix ("*.ut"), we can't use QLibrary. +#ifdef _WIN32 + // there is no "dlopen" on mingw, so we need to emulate it. + HINSTANCE handle = LoadLibraryW ((const wchar_t *) tl::to_qstring (pp).constData ()); + if (! handle) { + throw tl::Exception (tl::sprintf ("Unable to load plugin tests: %s with error message: %s", pp.c_str (), GetLastError ())); + } +#else + void *handle; + handle = dlopen (tl::string_to_system (pp).c_str (), RTLD_LAZY); + if (! handle) { + throw tl::Exception (tl::sprintf ("Unable to load plugin tests: %s", pp.c_str ())); + } +#endif + + } - if (ut::sp_ruby_interpreter) { - ut::sp_ruby_interpreter->push_console (&console); - } - if (ut::sp_python_interpreter) { - ut::sp_python_interpreter->push_console (&console); } - ut::ctrl << ""; - ut::ctrl << ""; + // No side effects + tl::set_klayout_path (std::vector ()); + + int ac = 2; + static char av0[] = "unit_test"; + static char av1[] = "-z"; // don't show main window + static char av2[] = "-nc"; // No configuration file + static char av3[] = "-rx"; // No mplicit macros + char *av[] = { av0, av1, av2, av3, 0 }; + lay::Application app (ac, av, false); + app.autorun (); + + #if QT_VERSION < 0x050000 + QTextCodec::setCodecForTr (QTextCodec::codecForName ("utf8")); + #endif + + bool editable = false, non_editable = false; + bool gsi_coverage = false; + std::vector class_names; + std::vector test_list; + std::vector exclude_test_list; + + bool xml_format = false; + bool list_tests = false; + bool slow = false; + bool verbose = false; + bool debug_mode = false; + bool continue_flag = false; + + tl::CommandLineOptions cmd; + cmd << tl::arg ("-a", &xml_format, "Provide XML output format (JUnit format)") + << tl::arg ("-l", &list_tests, "Lists tests and exits") + << tl::arg ("-e", &editable, "Uses editable mode") + << tl::arg ("-ne", &non_editable, "Uses non-editable mode") + << tl::arg ("-c", &continue_flag, "Continues after an error") + << tl::arg ("-i", &debug_mode, "Uses debug mode", + "In debug mode, execution stops after an error and if possible, fix instructions are " + "printed." + ) + << tl::arg ("-s", &slow, "Includes slow (long runner) tests") + << tl::arg ("-v", &verbose, "Provides verbose output") + << tl::arg ("-g", &gsi_coverage, "Produces a GSI test coverage statistics") + << tl::arg ("*-gg=class", &class_names, "Produces a specific GDS coverage statistics" + "With this specification, coverage will be printed for this specific class. " + "This option can be present multiple times." + ) + << tl::arg ("-x=test", &exclude_test_list, "Exclude the following tests" + "This option can be given multiple times or with a comma-separated list " + "of pattern. Test tests matching one of the exclude pattern " + "are not executed." + ) + << tl::arg ("?*test", &test_list, "The pattern for the tests to execute") + ; + + cmd.brief ("The runner executable for execution of the unit tests"); + + cmd.parse (argc, argv); + + if (!editable && !non_editable) { + editable = non_editable = true; + } + + if (!class_names.empty ()) { + gsi_coverage = true; + } + + if (list_tests) { + tl::info << "List of installed tests:"; + for (std::vector::const_iterator i = ut::Registrar::instance()->tests ().begin (); i != ut::Registrar::instance()->tests ().end (); ++i) { + tl::info << " " << (*i)->name (); + } + throw tl::CancelException (); + } + + ut::set_verbose (verbose); + ut::set_continue_flag (continue_flag); + ut::set_debug_mode (debug_mode); + + ut::TestConsole console (stdout, xml_format); ut::noctrl << replicate ("=", console.real_columns ()); ut::noctrl << "Entering KLayout test suite"; tl::info << "TESTSRC=" << ut::testsrc (); - tl::info << "TESTTMP=" << tl::to_string (ut::testtmp ().absolutePath ()); + tl::info << "TESTTMP=" << tl::to_string (QDir (tl::to_qstring (ut::testtmp ())).absolutePath ()); const std::vector *selected_tests = 0; std::vector subset; @@ -1053,11 +468,11 @@ main_cont (int argc, char **argv) for (std::vector::const_iterator i = ut::Registrar::instance()->tests ().begin (); i != ut::Registrar::instance()->tests ().end (); ++i) { bool exclude = false; - for (std::vector::const_iterator m = exclude_test_list.begin (); m != exclude_test_list.end (); ++m) { + + for (std::vector::const_iterator m = exclude_test_list.begin (); m != exclude_test_list.end () && !exclude; ++m) { QRegExp re (tl::to_qstring (*m), Qt::CaseInsensitive, QRegExp::Wildcard); if (re.indexIn (tl::to_qstring ((*i)->name ())) == 0) { exclude = true; - break; } } @@ -1076,236 +491,22 @@ main_cont (int argc, char **argv) selected_tests = &ut::Registrar::instance()->tests (); } - ut::s_verbose_flag = false; - int failed_ne = 0, failed_e = 0; - std::vector failed_tests_e, failed_tests_ne; - int skipped_ne = 0, skipped_e = 0; - std::vector skipped_tests_e, skipped_tests_ne; + try { - for (int e = 0; e < 2; ++e) { + ut::ctrl << ""; + ut::ctrl << ""; - if ((non_editable && e == 0) || (editable && e == 1)) { + result = run_tests (*selected_tests, editable, non_editable, slow, app, gsi_coverage, class_names); - std::string mode (e == 0 ? "non-editable" : "editable"); - ut::ctrl << ""; - - ut::noctrl << replicate ("=", console.real_columns ()); - ut::noctrl << "Running tests in " << mode << " mode ..."; - app.set_editable (e != 0); - - int failed = 0; - std::vector failed_tests; - int skipped = 0; - std::vector skipped_tests; - - tl::Timer timer; - - timer.start (); - - try { - - failed = 0; - failed_tests.clear (); - skipped = 0; - skipped_tests.clear (); - - for (std::vector ::const_iterator t = selected_tests->begin (); t != selected_tests->end (); ++t) { - (*t)->remove_tmp_folder (); - } - - for (std::vector ::const_iterator t = selected_tests->begin (); t != selected_tests->end (); ++t) { - - ut::ctrl << "name () << "\">"; - - ut::noctrl << replicate ("-", TestConsole::instance ()->real_columns ()); - ut::noctrl << "Running " << (*t)->name (); - - try { - - if (! (*t)->do_test (mode)) { - - ut::ctrl << "name ()) << " failed (continued mode - see previous messages)" << "\"/>"; - tl::error << "Test " << (*t)->name () << " failed (continued mode - see previous messages)"; - - failed_tests.push_back (*t); - ++failed; - - } - - } catch (tl::CancelException &) { - - ut::ctrl << ""; - tl::error << "Test " << (*t)->name () << " skipped"; - - skipped_tests.push_back (*t); - ++skipped; - - } catch (tl::Exception &ex) { - - ut::ctrl << ""; - tl::error << "Test " << (*t)->name () << " failed:"; - tl::info << ex.msg (); - - failed_tests.push_back (*t); - ++failed; - - } - - ut::ctrl << ""; - - } - - } catch (tl::Exception &ex) { - tl::error << "Caught tl::exception: " << ex.msg (); - failed = 1; - } catch (std::exception &ex) { - tl::error << "Caught std::exception: " << std::string (ex.what ()); - failed = 1; - } catch (...) { - tl::error << "Caught unspecific exception"; - failed = 1; - } - - timer.stop (); - - ut::ctrl << ""; - - ut::noctrl << replicate ("=", console.real_columns ()); - ut::noctrl << "Summary"; - - if (skipped > 0) { - if (e == 0) { - skipped_tests_ne = skipped_tests; - skipped_ne = skipped; - } else { - skipped_tests_e = skipped_tests; - skipped_e = skipped; - } - tl::warn << skipped << " test(s) skipped"; - } - - if (failed > 0) { - if (e == 0) { - failed_tests_ne = failed_tests; - failed_ne = failed; - } else { - failed_tests_e = failed_tests; - failed_e = failed; - } - tl::warn << failed << " test(s) failed"; - } else { - tl::info << "All tests passed in " << mode << " mode."; - } - - ut::ctrl << ""; - - ut::noctrl << "Total time: " << timer.sec_wall () << "s (wall) " << timer.sec_user () << "s (user) " << timer.sec_sys () << "s (sys)"; - ut::ctrl << ""; - - ut::ctrl << ""; - - } + ut::ctrl << ""; + } catch (...) { + ut::ctrl << ""; + throw; } - grand_timer.stop (); - - // GSI diagnostics: print all methods that have not been called - if (gsi_coverage) { - - ut::noctrl << replicate ("=", console.real_columns ()); - ut::noctrl << "GSI coverage test"; - - ut::ctrl << ""; - - bool first = true; - for (gsi::ClassBase::class_iterator c = gsi::ClassBase::begin_classes (); c != gsi::ClassBase::end_classes (); ++c) { - - if (gsi_coverage_selected && class_names.find (c->name ()) == class_names.end ()) { - continue; - } - - bool first_of_class = true; - for (gsi::ClassBase::method_iterator m = c->begin_methods (); m != c->end_methods (); ++m) { - - if (!dynamic_cast (*m) && !(*m)->was_called ()) { - - if (first) { - first = false; - tl::warn << "GSI coverage test failed - the following methods were not called:"; - } - if (first_of_class) { - tl::warn << replicate (" ", console.indent ()) << "Class " << c->name (); - first_of_class = false; - } - tl::warn << replicate (" ", console.indent () * 2) << (*m)->to_string (); - - } - - } - - } - - if (first) { - tl::info << "GSI coverage test passed."; - } - - ut::ctrl << ""; - - } - - ut::noctrl << ut::replicate ("=", console.real_columns ()); - ut::noctrl << "Grand Summary"; - - ut::ctrl << ""; - - if (skipped_e + skipped_ne > 0) { - if (non_editable) { - tl::warn << "Skipped in non-editable mode"; - for (std::vector ::const_iterator f = skipped_tests_ne.begin (); f != skipped_tests_ne.end (); ++f) { - tl::warn << replicate (" ", console.indent ()) << (*f)->name (); - } - } - if (editable) { - tl::warn << "Skipped in editable mode"; - for (std::vector ::const_iterator f = skipped_tests_e.begin (); f != skipped_tests_e.end (); ++f) { - tl::warn << replicate (" ", console.indent ()) << (*f)->name (); - } - } - tl::warn << tl::to_string (skipped_e + skipped_ne) << " test(s) skipped"; - } - - result = failed_e + failed_ne; - if (result > 0) { - if (non_editable) { - tl::warn << "Failed in non-editable mode"; - for (std::vector ::const_iterator f = failed_tests_ne.begin (); f != failed_tests_ne.end (); ++f) { - tl::warn << replicate (" ", console.indent ()) << (*f)->name (); - } - } - if (editable) { - tl::warn << "Failed in editable mode"; - for (std::vector ::const_iterator f = failed_tests_e.begin (); f != failed_tests_e.end (); ++f) { - tl::warn << replicate (" ", console.indent ()) << (*f)->name (); - } - } - tl::warn << tl::to_string (result) << " test(s) failed"; - } else { - tl::info << "All tests passed."; - } - - ut::ctrl << ""; - - ut::noctrl << "Grand total time: " << grand_timer.sec_wall () << "s (wall) " << grand_timer.sec_user () << "s (user) " << grand_timer.sec_sys () << "s (sys)"; - ut::ctrl << ""; - - if (ut::sp_ruby_interpreter) { - ut::sp_ruby_interpreter->remove_console (&console); - } - if (ut::sp_python_interpreter) { - ut::sp_python_interpreter->remove_console (&console); - } - + } catch (tl::CancelException &) { + result = 0; } catch (tl::Exception &ex) { tl::error << ex.msg (); result = -1; @@ -1317,12 +518,8 @@ main_cont (int argc, char **argv) result = -1; } - ut::ctrl << ""; - return result; } - - } // namespace ut diff --git a/src/ut/utTestBase.cc b/src/ut/utTestBase.cc new file mode 100644 index 000000000..2b9fedf59 --- /dev/null +++ b/src/ut/utTestBase.cc @@ -0,0 +1,458 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2017 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include "utTestBase.h" +#include "utTestConsole.h" +#include "utHead.h" +#include "tlFileUtils.h" +#include "tlTimer.h" +#include "tlStream.h" +#include "dbLayout.h" +#include "dbStreamLayers.h" +#include "dbGDS2Writer.h" +#include "dbOASISWriter.h" +#include "dbReader.h" +#include "dbCommonReader.h" +#include "dbLayoutDiff.h" + +#include "pya.h" +#include "rba.h" + +#include + +namespace ut +{ + +// -------------------------------------------------------------------------------------- + +static bool s_verbose_flag = false; +static bool s_debug_mode = false; +static bool s_continue_flag = false; + +bool verbose () +{ + return s_verbose_flag; +} + +void set_verbose (bool f) +{ + s_verbose_flag = f; +} + +void set_continue_flag (bool f) +{ + s_continue_flag = f; +} + +bool is_debug_mode () +{ + return s_debug_mode; +} + +void set_debug_mode (bool f) +{ + s_debug_mode = f; +} + +pya::PythonInterpreter *python_interpreter () +{ + pya::PythonInterpreter *ip = pya::PythonInterpreter::instance (); + tl_assert (ip != 0); + return ip; +} + +rba::RubyInterpreter *ruby_interpreter () +{ + rba::RubyInterpreter *ip = rba::RubyInterpreter::instance (); + tl_assert (ip != 0); + return ip; +} + +std::string testsrc () +{ + const char *ts = getenv ("TESTSRC"); + if (! ts) { + throw tl::Exception ("TESTSRC undefined"); + } + return ts; +} + +std::string testsrc_private () +{ + QDir d (QDir (tl::to_qstring (ut::testsrc ())).filePath (QString::fromUtf8 ("private"))); + if (! d.exists ()) { + throw tl::CancelException (); + } + return tl::to_string (d.path ()); +} + +std::string testtmp () +{ + // Ensures the test temp directory is present + const char *tt = getenv ("TESTTMP"); + if (! tt) { + throw tl::Exception ("TESTTMP undefined"); + } + return tt; +} + +bool equals (double a, double b) +{ + double m = fabs (0.5 * (a + b)); + if (m < 1e-30) { + // resolution limit is 1e-30 + return true; + } else { + double d = fabs (a - b); + // we consider two values equal for the purpose of unit tests if they have the + // same value within 1e-10 (0.00000001%). + return d < 1e-10 * m; + } +} + +// TODO: move this to tlString.h +static std::string replicate (const char *s, size_t n) +{ + std::string res; + res.reserve (strlen (s) * n); + while (n > 0) { + res += s; + --n; + } + return res; +} + +// -------------------------------------------------------------------------------------- +// TestBase implementation + +ut::Registrar *ut::Registrar::ms_instance = 0; + +TestBase::TestBase (const std::string &file, const std::string &name) + : m_editable (false), m_slow (false), m_cp_line (0), m_any_failed (false) +{ + QFileInfo f (tl::to_qstring (file)); + m_test = tl::to_string (f.baseName ()) + ":" + name; + m_testdir = tl::to_string (f.baseName ()) + "_" + name; + ut::Registrar::reg (this); +} + +bool TestBase::do_test (bool editable, bool slow) +{ + m_editable = editable; + m_slow = slow; + + ut::ctrl << ""; + + try { + + // Ensures the test temp directory is present + QDir dir (tl::to_qstring (testtmp ())); + QDir tmpdir (dir.absoluteFilePath (tl::to_qstring (m_testdir))); + if (tmpdir.exists () && ! tl::rm_dir_recursive (tmpdir.absolutePath ())) { + throw tl::Exception ("Unable to clean temporary dir: " + tl::to_string (tmpdir.absolutePath ())); + } + if (! dir.mkpath (tl::to_qstring (m_testdir))) { + throw tl::Exception ("Unable to create path for temporary files: " + tl::to_string (tmpdir.absolutePath ())); + } + dir.cd (tl::to_qstring (m_testdir)); + + m_testtmp = dir.absolutePath (); + + static std::string testname_value; + static std::string testtmp_value; + + putenv (const_cast ("TESTNAME=")); + testname_value = std::string ("TESTNAME=") + m_test; + putenv (const_cast (testname_value.c_str ())); + + putenv (const_cast ("TESTTMP_WITH_NAME=")); + testtmp_value = std::string ("TESTTMP_WITH_NAME=") + m_testtmp.toUtf8().constData(); + putenv (const_cast (testtmp_value.c_str ())); + + reset_checkpoint (); + + tl::Timer timer; + timer.start(); + + execute (this); + + timer.stop(); + + m_testtmp.clear (); + + ut::ctrl << ""; + + ut::noctrl << "Time: " << timer.sec_wall () << "s (wall) " << timer.sec_user () << "s (user) " << timer.sec_sys () << "s (sys)"; + ut::ctrl << ""; + + } catch (...) { + ut::ctrl << ""; + throw; + } + + return (!m_any_failed); +} + +std::string TestBase::tmp_file (const std::string &fn) const +{ + tl_assert (! m_testtmp.isEmpty ()); + QDir dir (m_testtmp); + return tl::to_string (dir.absoluteFilePath (tl::to_qstring (fn))); +} + +/** + * @brief Recursively empties a directory + */ +static void empty_dir (QDir dir) +{ + QStringList entries = dir.entryList (QDir::AllEntries | QDir::NoDotAndDotDot); + for (QStringList::const_iterator e = entries.begin (); e != entries.end (); ++e) { + QString epath = dir.absoluteFilePath (*e); + if (QFileInfo (epath).isDir ()) { + empty_dir (QDir (epath)); + dir.rmdir (*e); + } else if (! dir.remove (*e)) { + throw tl::Exception ("Unable to remove file or directory: " + tl::to_string (dir.filePath (*e))); + } + } +} + +void TestBase::remove_tmp_folder () +{ + // Ensures the test temp directory is present + QDir dir (tl::to_qstring (testtmp ())); + if (dir.cd (tl::to_qstring (m_test))) { + + empty_dir (dir); + + dir.cdUp (); + if (! dir.rmdir (tl::to_qstring (m_test))) { + throw tl::Exception ("Unable to remove directory: " + tl::to_string (dir.filePath (tl::to_qstring (m_test)))); + } + + } +} + +void TestBase::checkpoint (const std::string &file, int line) +{ + m_cp_file = file; + m_cp_line = line; +} + +void TestBase::reset_checkpoint () +{ + m_cp_file = std::string (); + m_cp_line = 0; +} + +void TestBase::raise (const std::string &file, int line, const std::string &msg) +{ + std::ostringstream sstr; + sstr << file << ", line " << line << ": " << msg; + if (s_continue_flag) { + tl::error << sstr.str (); + m_any_failed = true; + } else { + throw ut::Exception (sstr.str ()); + } +} + +void TestBase::raise (const std::string &msg) +{ + std::ostringstream sstr; + if (m_cp_line > 0) { + sstr << "(last checkpoint: " << m_cp_file << ", line " << m_cp_line << "): "; + } + sstr << msg; + if (s_continue_flag) { + tl::error << sstr.str (); + m_any_failed = true; + } else { + throw ut::Exception (sstr.str ()); + } +} + +void TestBase::test_is_editable_only () +{ + if (!m_editable) { + throw tl::CancelException (); + } +} + +void TestBase::test_is_non_editable_only () +{ + if (m_editable) { + throw tl::CancelException (); + } +} + +void TestBase::test_is_long_runner () +{ + if (!m_slow) { + throw tl::CancelException (); + } +} + +void TestBase::compare_layouts (const db::Layout &layout, const std::string &au_file, NormalizationMode norm, db::Coord tolerance) +{ + compare_layouts (layout, au_file, db::LayerMap (), true, norm, tolerance); +} + +void TestBase::compare_layouts (const db::Layout &layout, const std::string &au_file, const db::LayerMap &lm, bool read_other_layers, NormalizationMode norm, db::Coord tolerance) +{ + // normalize the layout by writing to GDS and reading from .. + + // generate a "unique" name ... + unsigned int hash = 0; + for (const char *cp = au_file.c_str (); *cp; ++cp) { + hash = (hash << 4) ^ (hash >> 4) ^ ((unsigned int) *cp); + } + + std::string tmp_file; + + if (norm == WriteGDS2) { + + tmp_file = ut::TestBase::tmp_file (tl::sprintf ("tmp_%x.gds", hash)); + + tl::OutputStream stream (tmp_file.c_str ()); + db::GDS2Writer writer; + db::SaveLayoutOptions options; + writer.write (const_cast (layout), stream, options); + + } else { + + tmp_file = ut::TestBase::tmp_file (tl::sprintf ("tmp_%x.oas", hash)); + + tl::OutputStream stream (tmp_file.c_str ()); + db::OASISWriter writer; + db::SaveLayoutOptions options; + writer.write (const_cast (layout), stream, options); + + } + + const db::Layout *subject = 0; + db::Layout layout2; + + if (norm != NoNormalization) { + + // read all layers from the original layout, so the layer table is the same + for (db::Layout::layer_iterator l = layout.begin_layers (); l != layout.end_layers (); ++l) { + layout2.insert_layer ((*l).first, *(*l).second); + } + + tl::InputStream stream (tmp_file); + db::Reader reader (stream); + reader.read (layout2); + + subject = &layout2; + + } else { + subject = &layout; + } + + bool equal = false; + bool any = false; + + int n = 0; + for ( ; ! equal; ++n) { + + db::Layout layout_au; + + // read all layers from the original layout, so the layer table is the same + for (db::Layout::layer_iterator l = layout.begin_layers (); l != layout.end_layers (); ++l) { + layout_au.insert_layer ((*l).first, *(*l).second); + } + + db::LoadLayoutOptions options; + options.get_options ().layer_map = lm; + options.get_options ().create_other_layers = read_other_layers; + + std::string fn = au_file; + if (n > 0) { + fn += tl::sprintf (".%d", n); + } + + if (QFileInfo (tl::to_qstring (fn)).exists ()) { + + if (n == 1 && any) { + throw tl::Exception (tl::sprintf ("Inconsistent reference variants for %s: there can be either variants (.1,.2,... suffix) or a single file (without suffix)", au_file)); + } + + any = true; + + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (layout_au, options); + + equal = db::compare_layouts (*subject, layout_au, (n > 0 ? db::layout_diff::f_silent : db::layout_diff::f_verbose) | db::layout_diff::f_flatten_array_insts /*| db::layout_diff::f_no_text_details | db::layout_diff::f_no_text_orientation*/, tolerance, 100 /*max diff lines*/); + if (equal && n > 0) { + tl::info << tl::sprintf ("Found match on golden reference variant %s", fn); + } + + } else if (n > 0) { + if (! any) { + tl::warn << tl::sprintf ("No golden data found (%s)", au_file); + } + break; + } + + } + + if (! equal) { + raise (tl::sprintf ("Compare failed - see\n actual: %s\n golden: %s%s", + tl::to_string (QFileInfo (tl::to_qstring (tmp_file)).absoluteFilePath ()), + tl::to_string (QFileInfo (tl::to_qstring (au_file)).absoluteFilePath ()), + (n > 1 ? "\nand variants" : ""))); + } +} + +void TestBase::write_detailed_diff (std::ostream &os, const std::string &subject, const std::string &ref) +{ + os << replicate (" ", TestConsole::instance ()->indent ()) << "Actual value is: " << tl::to_string (subject) << std::endl + << replicate (" ", TestConsole::instance ()->indent ()) << "Reference value is: " << tl::to_string (ref) << std::endl + ; +} + +static std::string read_file (const std::string &path) +{ + QFile file (tl::to_qstring (path)); + if (! file.open (QIODevice::ReadOnly | QIODevice::Text)) { + tl::warn << tl::sprintf ("Unable to open file %s", path); + } + + QByteArray ba = file.readAll (); + return std::string (ba.constData (), 0, ba.size ()); +} + +void TestBase::compare_text_files (const std::string &path_a, const std::string &path_b) +{ + std::string text_a = read_file (path_a); + std::string text_b = read_file (path_b); + + if (text_a != text_b) { + raise (tl::sprintf ("Compare failed - see:\n file 1: %s\n file 2: %s", + tl::to_string (QFileInfo (tl::to_qstring (path_a)).absoluteFilePath ()), + tl::to_string (QFileInfo (tl::to_qstring (path_b)).absoluteFilePath ()))); + } +} + +} diff --git a/src/ut/utTestBase.h b/src/ut/utTestBase.h new file mode 100644 index 000000000..796475661 --- /dev/null +++ b/src/ut/utTestBase.h @@ -0,0 +1,498 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2017 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#ifndef HDR_utTestBase +#define HDR_utTestBase + +#include "utCommon.h" +#include "dbTypes.h" +#include "tlException.h" +#include "tlString.h" + +#include +#include +#include +#include + +namespace db +{ + class Layout; + class LayerMap; +} + +namespace rba +{ + class RubyInterpreter; +} + +namespace pya +{ + class PythonInterpreter; +} + +namespace ut +{ + +/** + * @brief Returns true, if the test is run in verbose mode + * Verbose mode is enabled through the "-v" option + */ +UT_PUBLIC bool verbose (); + +/** + * @brief Sets verbose mode + */ +UT_PUBLIC void set_verbose (bool v); + +/** + * @brief Enables or disables "continue" mode + * In continue mode, the execution will proceed even in case of an error. + */ +UT_PUBLIC void set_continue_flag (bool f); + +/** + * @brief Returns the Ruby interpreter + */ +UT_PUBLIC rba::RubyInterpreter *ruby_interpreter (); + +/** + * @brief Returns the Python interpreter + */ +UT_PUBLIC pya::PythonInterpreter *python_interpreter (); + +/** + * @brief Returns true, if the unit test is run in debug mode + * In debug mode, the unit tests shall offer information on how to fix the + * test. Specifically if layout compare is involved, it shall display the golden + * file name and the actual one and terminate to allow updating the files. + */ +UT_PUBLIC bool is_debug_mode (); + +/** + * @brief Enables or disables debug mode + */ +UT_PUBLIC void set_debug_mode (bool f); + +/** + * @brief Gets the path of the test data + * This path is specified through the environment variable $TESTSRC + */ +UT_PUBLIC std::string testsrc (); + +/** + * @brief Gets the path of the private test data + * This path is specified through the environment variable $TESTSRC and the + * private testdata directory. If no private test data is available, this + * method will throw a CancelException which makes the test skipped. + */ +UT_PUBLIC std::string testsrc_private (); + +/** + * @brief Gets the path to the temporary data + * This path is specified through the environment variable $TESTTMP + */ +UT_PUBLIC std::string testtmp (); + +/** + * @brief A basic exception for the unit test framework + */ +struct Exception + : public tl::Exception +{ + Exception (const std::string &msg) + : tl::Exception (msg) + { } +}; + +/** + * @brief Specifies the normalization mode for compare_layouts + */ +enum NormalizationMode +{ + NoNormalization, // no normalization - take the test subject as it is + WriteGDS2, // normalize subject by writing to GDS2 and reading back + WriteOAS // normalize subject by writing to OASIS and reading back +}; + +/** + * @brief A generic compare operator + */ +template +inline bool equals (const X &a, const Y &b) +{ + return a == b; +} + +/** + * @brief A specialization of the compare operator for doubles + */ +UT_PUBLIC bool equals (double a, double b); + +/** + * @brief Specialization of comparison of pointers vs. integers (specifically "0") + */ +template +inline bool equals (X *a, int b) +{ + return a == (X *) size_t (b); +} + +/** + * @brief A specialization of comparison of double vs "anything" + */ +template +inline bool equals (double a, const Y &b) +{ + return equals (a, double (b)); +} + +/** + * @brief A specialization of comparison of "anything" vs. double + */ +template +inline bool equals (const X &a, double b) +{ + return equals (double (a), b); +} + +/** + * @brief A specialization of the compare operator for const char * + */ +inline bool equals (const char *a, const char *b) +{ + return equals (std::string (a), std::string (b)); +} + +/** + * @brief A specialization of the compare operator for std::string vs. const char * + */ +inline bool equals (const std::string &a, const char *b) +{ + return equals (a, std::string (b)); +} + +/** + * @brief A specialization of the compare operator for std::string vs. const char * + */ +inline bool equals (const char *a, const std::string &b) +{ + return equals (std::string (a), b); +} + +/** + * @brief The base class for tests + */ +struct UT_PUBLIC TestBase +{ + /** + * @brief Constructor + */ + TestBase (const std::string &file, const std::string &name); + + /** + * @brief Destructor + */ + virtual ~TestBase () { } + + /** + * @brief Actually runs the test + * @return True, if the test was successful + */ + bool do_test (bool editable, bool slow); + + /** + * @brief Indicates that the test is a long runner + * In this case the test is skipped unless in slow mode. + */ + void test_is_long_runner (); + + /** + * @brief Indicates that the test is a test that should run in editable mode only. + * In this case the test is skipped unless in editable mode. + */ + void test_is_editable_only (); + + /** + * @brief Indicates that the test is a test that should run in non-editable mode only. + * In this case the test is skipped unless in non-editable mode. + */ + void test_is_non_editable_only (); + + /** + * @brief Raises an exception with the given string + * This version prints the last checkpoint for reference. + */ + void raise (const std::string &s); + + /** + * @brief Raises an exception with the given string, file and line number + */ + void raise (const std::string &file, int line, const std::string &s); + + /** + * @brief Registers a checkpoint + */ + void checkpoint (const std::string &file, int line); + + /** + * @brief Resets the checkpoints set + */ + void reset_checkpoint (); + + /** + * @brief Compares a layout with a golden layout file + * @param layout The layout to compare + * @param au_file The golden file path + * @param norm The normalization mode (see NormalizationMode) + * @param tolerance A tolerance applied when comparing shapes in database units + * The layout is normalized by writing to a file and reading back + */ + void compare_layouts (const db::Layout &layout, const std::string &au_file, NormalizationMode norm = WriteGDS2, db::Coord tolerance = 0); + + /** + * @brief Compares a layout with a golden layout file with layer mapping + * @param layout The layout to compare + * @param au_file The golden file path + * @param lmap The layer mapping object + * @param read_all_others If true, all other layers will be read too + * @param norm The normalization mode (see NormalizationMode) + * @param tolerance A tolerance applied when comparing shapes in database units + * The layout is normalized by writing to a file and reading back + */ + void compare_layouts (const db::Layout &layout, const std::string &au_file, const db::LayerMap &lmap, bool read_all_others, NormalizationMode norm = WriteGDS2, db::Coord tolerance = 0); + + /** + * @brief Compares two text files + */ + void compare_text_files (const std::string &path_a, const std::string &path_b); + + /** + * @brief The test's name + * @return The name of the test + */ + const std::string &name () const + { + return m_test; + } + + /** + * @brief Prepares a temporary file path + * @param fn The actual name of the file + * @return A path suitable for writing a temporary file + * The directory for the file will be created within this method. + */ + std::string tmp_file (const std::string &fn = "tmp") const; + + /** + * @brief Removes all temporary files + */ + void remove_tmp_folder (); + + /** + * @brief A generic diff printer + */ + template + void diff (const std::string &file, int line, const std::string &msg, const X &subject, const Y & /*ref*/) + { + std::ostringstream sstr; + sstr << msg << " (actual value is " << subject << ")"; + raise (file, line, sstr.str ()); + } + + /** + * @brief A generic diff printer + */ + template + void detailed_diff (const std::string &file, int line, const std::string &msg, const X &subject, const Y &ref) + { + std::ostringstream sstr; + sstr << msg << std::endl; + write_detailed_diff (sstr, tl::to_string (subject), tl::to_string (ref)); + raise (file, line, sstr.str ()); + } + + /** + * @brief A diff printer for int vs. something + */ + template + void diff (const std::string &file, int line, const std::string &msg, int subject, const Y &ref) + { + detailed_diff (file, line, msg, subject, ref); + } + + /** + * @brief A diff printer for unsigned int vs. something + */ + template + void diff (const std::string &file, int line, const std::string &msg, unsigned int subject, const Y &ref) + { + detailed_diff (file, line, msg, subject, ref); + } + + /** + * @brief A diff printer for long vs. something + */ + template + void diff (const std::string &file, int line, const std::string &msg, long subject, const Y &ref) + { + detailed_diff (file, line, msg, subject, ref); + } + + /** + * @brief A diff printer for unsigned long vs. something + */ + template + void diff (const std::string &file, int line, const std::string &msg, unsigned long subject, const Y &ref) + { + detailed_diff (file, line, msg, subject, ref); + } + + /** + * @brief A diff printer for long long vs. something + */ + template + void diff (const std::string &file, int line, const std::string &msg, long long subject, const Y &ref) + { + detailed_diff (file, line, msg, subject, ref); + } + + /** + * @brief A diff printer for unsigned long long vs. something + */ + template + void diff (const std::string &file, int line, const std::string &msg, unsigned long long subject, const Y &ref) + { + detailed_diff (file, line, msg, subject, ref); + } + + /** + * @brief A diff printer for bool + */ + inline void diff (const std::string &file, int line, const std::string &msg, bool subject, bool ref) + { + detailed_diff (file, line, msg, subject, ref); + } + + /** + * @brief A diff printer for double + */ + inline void diff (const std::string &file, int line, const std::string &msg, double subject, double ref) + { + detailed_diff (file, line, msg, subject, ref); + } + + /** + * @brief A diff printer for strings + */ + inline void diff (const std::string &file, int line, const std::string &msg, const std::string &subject, const std::string &ref) + { + detailed_diff (file, line, msg, subject, ref); + } + + /** + * @brief A diff printer for strings vs. const char * + */ + inline void diff (const std::string &file, int line, const std::string &msg, const std::string &subject, const char *ref) + { + detailed_diff (file, line, msg, subject, ref); + } + + /** + * @brief A diff printer for strings vs. const char * + */ + inline void diff (const std::string &file, int line, const std::string &msg, const char *subject, const std::string &ref) + { + detailed_diff (file, line, msg, subject, ref); + } + + /** + * @brief A diff printer for C strings + */ + inline void diff (const std::string &file, int line, const std::string &msg, const char *subject, const char *ref) + { + diff (file, line, msg, std::string (subject), std::string (ref)); + } + + /** + * @brief Main entry point for the compare feature (EXPECT_EQ and EXPECT_NE) + */ + template + void eq_helper (bool eq, const T1 &a, const T2 &b, const char *what_expr, const char *equals_expr, const char *file, int line) + { + if (ut::equals (a, b) != eq) { + std::ostringstream sstr; + sstr << what_expr << " does not equal " << equals_expr; + diff (file, line, sstr.str (), a, b); + } + } + +private: + virtual void execute (ut::TestBase *_this) throw (tl::Exception) = 0; + + void write_detailed_diff (std::ostream &os, const std::string &subject, const std::string &ref); + + bool m_editable, m_slow; + std::string m_test; + std::string m_testdir; + // last checkpoint + std::string m_cp_file; + int m_cp_line; + bool m_any_failed; + QString m_testtmp; +}; + +/** + * @brief The registration facility for tests + */ +struct Registrar +{ + static void reg (ut::TestBase *t) + { + if (! ms_instance) { + ms_instance = new Registrar (); + } + ms_instance->m_tests.push_back (t); + } + + static Registrar *instance () + { + return ms_instance; + } + + const std::vector &tests () const + { + return m_tests; + } + +private: + static Registrar *ms_instance; + + Registrar () : m_tests () { } + + std::vector m_tests; +}; + +} + +#endif + diff --git a/src/ut/utTestConsole.cc b/src/ut/utTestConsole.cc new file mode 100644 index 000000000..fa47cdcbe --- /dev/null +++ b/src/ut/utTestConsole.cc @@ -0,0 +1,412 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2017 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include "utTestConsole.h" +#include "utHead.h" + +#include "rba.h" +#include "pya.h" + +#include + +#if !defined(_WIN32) +# include +# include +#endif +#if defined(_WIN32) +# include +#endif + +namespace ut +{ + +// ------------------------------------------------ +// CaptureChannel implementation + +CaptureChannel::CaptureChannel () +{ + tl::info.add (this, false); + tl::error.add (this, false); + tl::warn.add (this, false); +} + +void CaptureChannel::puts (const char *s) +{ + m_text << s; +} + +void CaptureChannel::endl () +{ + m_text << "\n"; +} + +void CaptureChannel::end () +{ + // .. nothing yet .. +} + +void CaptureChannel::begin () +{ + // .. nothing yet .. +} + +// ------------------------------------------------ +// tl::Channel implementations for redirecting the log output + +class InfoChannel : public tl::Channel +{ +public: + InfoChannel (int verbosity) + : m_verbosity (verbosity) + { + // .. nothing yet .. + } + +protected: + virtual void puts (const char *s) + { + if (tl::verbosity () >= m_verbosity) { + TestConsole::instance ()->basic_write (s); + } + } + + virtual void endl () + { + if (tl::verbosity () >= m_verbosity) { + TestConsole::instance ()->basic_write ("\n"); + } + } + + virtual void end () + { + TestConsole::instance ()->flush (); + } + + virtual void begin () + { + // .. nothing yet .. + } + +private: + int m_verbosity; +}; + +class WarningChannel : public tl::Channel +{ +public: + WarningChannel () + { + // .. nothing yet .. + } + +protected: + virtual void puts (const char *s) + { + TestConsole::instance ()->basic_write (s); + } + + virtual void endl () + { + TestConsole::instance ()->basic_write ("\n"); + } + + virtual void end () + { + TestConsole::instance ()->end (); + TestConsole::instance ()->flush (); + } + + virtual void begin () + { + TestConsole::instance ()->begin_warn (); + } +}; + +class ErrorChannel : public tl::Channel +{ +public: + ErrorChannel () + { + // .. nothing yet .. + } + +protected: + virtual void puts (const char *s) + { + TestConsole::instance ()->basic_write (s); + } + + virtual void endl () + { + TestConsole::instance ()->basic_write ("\n"); + } + + virtual void end () + { + TestConsole::instance ()->end (); + TestConsole::instance ()->flush (); + } + + virtual void begin () + { + TestConsole::instance ()->begin_error (); + } +}; + +class CtrlChannel : public tl::Channel +{ +public: + CtrlChannel (bool with_xml) + : m_with_xml (with_xml) + { + // .. nothing yet .. + } + +protected: + virtual void puts (const char *s) + { + if (m_with_xml == TestConsole::instance ()->xml_format ()) { + TestConsole::instance ()->raw_write (s); + } + } + + virtual void endl () + { + if (m_with_xml == TestConsole::instance ()->xml_format ()) { + TestConsole::instance ()->raw_write ("\n"); + } + } + + virtual void end () + { + if (m_with_xml == TestConsole::instance ()->xml_format ()) { + TestConsole::instance ()->end (); + TestConsole::instance ()->flush (); + } + } + + virtual void begin () + { + if (m_with_xml == TestConsole::instance ()->xml_format ()) { + TestConsole::instance ()->begin_info (); + } + } + +private: + bool m_with_xml; +}; + +// ------------------------------------------------ +// TestConsole implementation + +const char *ANSI_RED = "\033[31;1m"; +const char *ANSI_BLUE = "\033[34m"; +const char *ANSI_GREEN = "\033[32m"; +const char *ANSI_RESET = "\033[0m"; + +TestConsole::TestConsole (FILE *file, bool xml_format) + : m_file (file), m_xml_format (xml_format), m_col (0), m_max_col (250), m_columns (50), m_rows (0), m_is_tty (false) +{ + ms_instance = this; + m_indent = 4; + + m_is_tty = isatty (fileno (file)) && ! xml_format; + +#if !defined(_WIN32) + if (m_is_tty) { + struct winsize ws; + ioctl (fileno (stdout), TIOCGWINSZ, &ws); + m_columns = std::max (0, (int) ws.ws_col); + m_rows = std::max (0, (int) ws.ws_row); + } +#endif + + redirect (); +} + +TestConsole::~TestConsole () +{ + restore (); + + if (ms_instance == this) { + ms_instance = 0; + } +} + +void +TestConsole::write_str (const char *text, output_stream os) +{ + if (os == OS_stderr) { + begin_error (); + basic_write (text); + end (); + } else { + basic_write (text); + } +} + +void +TestConsole::raw_write (const char *text) +{ + fputs (text, m_file); +} + +void +TestConsole::flush () +{ + fflush (m_file); +} + +void +TestConsole::begin_error () +{ + if (m_is_tty) { + fputs (ANSI_RED, m_file); + } +} + +void +TestConsole::begin_info () +{ + if (m_is_tty) { + fputs (ANSI_GREEN, m_file); + } +} + +void +TestConsole::begin_warn () +{ + if (m_is_tty) { + fputs (ANSI_BLUE, m_file); + } +} + +void +TestConsole::end () +{ + if (m_is_tty) { + fputs (ANSI_RESET, m_file); + } +} + +void +TestConsole::basic_write (const char *s) +{ + if (m_xml_format) { + + for (const char *cp = s; *cp; ++cp) { + if (*cp == '&') { + fputs ("&", m_file); + } else if (*cp == '<') { + fputs ("<", m_file); + } else if (*cp == '>') { + fputs (">", m_file); + } else { + fputc (*cp, m_file); + } + } + + } else { + + // line length limitation - this assumes we are always printing to the same terminal + // or we don't mix stderr/stdout. + const char *cp; + for (cp = s; *cp; ++cp) { + if (*cp == '\n' || *cp == '\r') { + m_col = 0; + fputc (*cp, m_file); + } else { + if (m_col == 0) { + for (int i = 0; i < m_indent; ++i) { + fputc (' ', m_file); + } + m_col = m_indent; + } + if (m_col > m_max_col) { + // ignore char + } else if (m_col == m_max_col) { + fputs (" ...", m_file); + ++m_col; + } else if (*cp == '\033') { + // skip ANSI escape sequences (no increment of s_col) + const char *cpend = cp + 1; + if (*cpend == '[') { + ++cpend; + while (*cpend && *cpend != 'm') { + ++cpend; + } + if (*cpend) { + ++cpend; + } + } + while (cp != cpend) { + fputc (*cp++, m_file); + } + --cp; + } else { + fputc (*cp, m_file); + ++m_col; + } + } + } + + } +} + +void +TestConsole::redirect () +{ + // redirect the log channels + tl::warn.clear (); + tl::warn.add (new ut::WarningChannel (), true); + tl::info.clear (); + tl::info.add (new ut::InfoChannel (0), true); + tl::log.clear (); + tl::log.add (new ut::InfoChannel (10), true); + tl::error.clear (); + tl::error.add (new ut::ErrorChannel (), true); + + ut::ruby_interpreter ()->push_console (this); + ut::python_interpreter ()->push_console (this); +} + +void +TestConsole::restore () +{ + // TODO: we should basically restore the original channels + tl::warn.clear (); + tl::info.clear (); + tl::log.clear (); + tl::error.clear (); + + ut::ruby_interpreter ()->remove_console (this); + ut::python_interpreter ()->remove_console (this); +} + +TestConsole *TestConsole::ms_instance = 0; + +tl::LogTee noctrl (new CtrlChannel (false), true); +tl::LogTee ctrl (new CtrlChannel (true), true); + + + +} diff --git a/src/ut/utTestConsole.h b/src/ut/utTestConsole.h new file mode 100644 index 000000000..a3d700ece --- /dev/null +++ b/src/ut/utTestConsole.h @@ -0,0 +1,141 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2017 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#ifndef HDR_utTestConsole +#define HDR_utTestConsole + +#include "utCommon.h" +#include "tlLog.h" +#include "gsiInterpreter.h" + +#include +#include + +namespace ut +{ + +/** + * @brief A utility class to capture the warning, error and info channels + * + * Instantiate this class inside a test. Then run the test and finally + * obtain the collected output with CaptureChannel::captured_text(). + */ +class UT_PUBLIC CaptureChannel : public tl::Channel +{ +public: + CaptureChannel (); + + std::string captured_text () const + { + return m_text.str (); + } + + void clear () + { + m_text.str (std::string ()); + } + +protected: + virtual void puts (const char *s); + virtual void endl (); + virtual void end (); + virtual void begin (); + +private: + std::ostringstream m_text; +}; + +/** + * @brief Redirects the interpreter output and serves as a general output device + */ +class TestConsole + : public gsi::Console +{ +public: + static TestConsole *instance () + { + tl_assert (ms_instance != 0); + return ms_instance; + } + + TestConsole (FILE *file, bool xml_format); + ~TestConsole (); + + int indent () const + { + return m_indent; + } + + bool xml_format () const + { + return m_xml_format; + } + + void write_str (const char *text, output_stream os); + void raw_write (const char *text); + virtual void flush (); + + virtual bool is_tty () + { + // NOTE: this assumes we are delivering to stdout + return m_is_tty; + } + + virtual int columns () + { + return std::max (m_columns - m_indent, 0); + } + + virtual int rows () + { + return m_rows; + } + + int real_columns () + { + return m_columns; + } + + void begin_error (); + void begin_info (); + void begin_warn (); + void end (); + void basic_write (const char *s); + +private: + FILE *m_file; + bool m_xml_format; + int m_col; + int m_max_col; + int m_columns, m_rows; + bool m_is_tty; + int m_indent; + static TestConsole *ms_instance; + + void redirect (); + void restore (); +}; + +} + +#endif diff --git a/testdata/drc/drcSimpleTests_1.drc b/testdata/drc/drcSimpleTests_1.drc new file mode 100644 index 000000000..75b575701 --- /dev/null +++ b/testdata/drc/drcSimpleTests_1.drc @@ -0,0 +1,21 @@ + +dbu 0.001 + +target($drc_test_target, "TOP") + +x = polygon_layer +x.is_empty? == true || raise("unexpected value") +x.is_box? == false || raise("unexpected value") +x.insert(box(4.0, 0, 4.7, 0.7)) +x.is_empty? == false || raise("unexpected value") +x.is_box? == true || raise("unexpected value") +x.insert(polygon([ p(0, 0), p(2.0, 0), p(1.0, 1.0) ])) +x.insert(polygon([ p(0, -5.0), p(2.0, -5.0), p(1.0, -6.0) ])) +x.insert(path([ p(0, -2), p(2.0, -2) ], 0.2)) +x.is_box? == false || raise("unexpected value") +x.output(10, 0) + +y = polygon_layer +y.insert(polygon([ p(0, 0), p(0, 1.0), p(2.0, 1.0), p(2.0, 2.0), p(1.0, 2.0), p(1.0, 0) ])) +y.output(11, 0) + diff --git a/testdata/drc/drcSimpleTests_2.drc b/testdata/drc/drcSimpleTests_2.drc new file mode 100644 index 000000000..5582c41dd --- /dev/null +++ b/testdata/drc/drcSimpleTests_2.drc @@ -0,0 +1,90 @@ + +target($drc_test_target, "TOP") +source($drc_test_source, "TOP") + +a1 = input(1) +b1 = input(2) +c1 = input(3) + +a1.output(1, 0) +b1.output(2, 0) +c1.output(3, 0) + +c1.rounded_corners(0.5, 0.5, 16).output(1010, 0) +c1.smoothed(1.5).output(1011, 0) + +a1.texts.output(1020, 0) +a1.texts("A*").output(1021, 0) +a1.texts(text("A*")).output(1022, 0) +a1.texts(pattern("A*")).output(1023, 0) +a1.texts(pattern("A*"), as_dots).extended(0.05, 0.05, 0.05, 0.05).output(1024, 0) +a1.texts(pattern("A*"), as_boxes).output(1025, 0) + +a1.middle.sized(0.05).output(1030, 0) +a1.middle(as_dots).extended(0.05, 0.05, 0.05, 0.05).output(1031, 0) +a1.middle(as_boxes).sized(0.05).output(1032, 0) +a1.extent_refs(0.5, 0.5).sized(0.05).output(1040, 0) +a1.extent_refs(:center).sized(0.05).output(1040, 1) +a1.extent_refs(:c).sized(0.05).output(1040, 2) +a1.extent_refs(:bottom).sized(0.05).output(1041, 0) +a1.extent_refs(:b).sized(0.05).output(1041, 1) +a1.extent_refs(:b, as_edges).extended(0.05, 0.05, 0.05, 0.05).output(1041, 2) +a1.extent_refs(:top).sized(0.05).output(1042, 0) +a1.extent_refs(:t).sized(0.05).output(1042, 1) +a1.extent_refs(:left).sized(0.05).output(1043, 0) +a1.extent_refs(:l).sized(0.05).output(1043, 1) +a1.extent_refs(:right).sized(0.05).output(1044, 0) +a1.extent_refs(:r).sized(0.05).output(1044, 1) +a1.extent_refs(:bottom_left).sized(0.05).output(1045, 0) +a1.extent_refs(:bl).sized(0.05).output(1045, 1) +a1.extent_refs(:bottom_center).sized(0.05).output(1046, 0) +a1.extent_refs(:bc).sized(0.05).output(1046, 1) +a1.extent_refs(:bottom_right).sized(0.05).output(1047, 0) +a1.extent_refs(:br).sized(0.05).output(1047, 1) +a1.extent_refs(:top_left).sized(0.05).output(1048, 0) +a1.extent_refs(:tl).sized(0.05).output(1048, 1) +a1.extent_refs(:top_center).sized(0.05).output(1049, 0) +a1.extent_refs(:tc).sized(0.05).output(1049, 1) +a1.extent_refs(:top_right).sized(0.05).output(1050, 0) +a1.extent_refs(:tr).sized(0.05).output(1050, 1) +a1.extent_refs(:left_center).sized(0.05).output(1051, 0) +a1.extent_refs(:lc).sized(0.05).output(1051, 1) +a1.extent_refs(:right_center).sized(0.05).output(1052, 0) +a1.extent_refs(:rc).sized(0.05).output(1052, 1) +a1.extent_refs(0.25, 0.5, 0.5, 0.75).output(1053, 0) + +a1.corners.sized(0.05).output(1060, 0) +a1.corners(-90.0, as_boxes).sized(0.05).output(1061, 0) +a1.corners(-90.0, as_dots).extended(0.05, 0.05, 0.05, 0.05).output(1062, 0) + +a1.select { |p| p.bbox.width < 0.8 }.output(1100, 0) +a1.collect { |p| p.is_box? && p.bbox.enlarged(0.1, 0.1) }.output(1101, 0) +a1.collect_to_region { |p| p.is_box? && p.bbox.enlarged(0.1, 0.1) }.output(1102, 0) +a1.collect_to_edges { |p| p.is_box? && p.bbox.enlarged(0.1, 0.1) }.output(1103, 0) +a1.collect { |p| p.is_box? && p.bbox.transformed(RBA::VCplxTrans::new(1000.0)).enlarged(120, 120) }.output(1104, 0) +a1.collect { |p| p.is_box? && [ p.bbox.transformed(RBA::VCplxTrans::new(1000.0)).enlarged(150, 150), p.bbox.transformed(RBA::VCplxTrans::new(1000.0)).enlarged(120, 120) ] }.output(1105, 0) +lx = polygon_layer +a1.each { |p| p.is_box? && lx.insert(p) } +lx.output(1106, 0) +a1.collect { |p| p.is_box? && RBA::Region::new(p.bbox.transformed(RBA::VCplxTrans::new(1000.0))).sized(120) }.output(1107, 0) +a1.collect { |p| p.is_box? && RBA::Polygon::new(p.bbox.transformed(RBA::VCplxTrans::new(1000.0))) }.output(1108, 0) +a1.collect { |p| p.is_box? && RBA::DPolygon::new(p.bbox) }.output(1109, 0) +a1.collect { |p| p.is_box? && RBA::SimplePolygon::new(p.bbox.transformed(RBA::VCplxTrans::new(1000.0))) }.output(1110, 0) +a1.collect { |p| p.is_box? && RBA::DSimplePolygon::new(p.bbox) }.output(1111, 0) +a1.collect_to_edges { |p| p.is_box? && p.bbox.transformed(RBA::VCplxTrans::new(1000.0)).enlarged(120, 120) }.output(1112, 0) +a1.collect_to_edges { |p| p.is_box? && [ p.bbox.transformed(RBA::VCplxTrans::new(1000.0)).enlarged(150, 150), p.bbox.transformed(RBA::VCplxTrans::new(1000.0)).enlarged(120, 120) ] }.output(1113, 0) +a1.collect_to_edges { |p| p.is_box? && RBA::Region::new(p.bbox.transformed(RBA::VCplxTrans::new(1000.0))).sized(120) }.output(1114, 0) +a1.collect_to_edges { |p| p.is_box? && RBA::Polygon::new(p.bbox.transformed(RBA::VCplxTrans::new(1000.0))) }.output(1115, 0) +a1.collect_to_edges { |p| p.is_box? && RBA::DPolygon::new(p.bbox) }.output(1116, 0) +a1.collect_to_edges { |p| p.is_box? && RBA::SimplePolygon::new(p.bbox.transformed(RBA::VCplxTrans::new(1000.0))) }.output(1117, 0) +a1.collect_to_edges { |p| p.is_box? && RBA::DSimplePolygon::new(p.bbox) }.output(1118, 0) + +a1.edges.select { |p| p.length < 0.8 }.output(1120, 0) +a1.edges.collect { |p| p.length < 0.8 && p.transformed(RBA::VCplxTrans::new(1000.0)) }.output(1121, 0) +a1.edges.collect_to_region { |p| p.length < 0.8 && p.bbox.enlarged(0.1, 0.1) }.output(1122, 0) +a1.edges.collect_to_region { |p| p.length < 0.8 && p.bbox.transformed(RBA::VCplxTrans::new(1000.0)).enlarged(100, 100) }.output(1123, 0) + +# edge pair collect +a1.width(1.5).collect { |p| p.transformed(RBA::VCplxTrans::new(1000.0)) }.output(1120, 0) +a1.width(1.5).collect_to_edge_pairs { |p| p.transformed(RBA::VCplxTrans::new(1000.0)) }.output(1121, 0) + diff --git a/testdata/drc/drcSimpleTests_3.drc b/testdata/drc/drcSimpleTests_3.drc new file mode 100644 index 000000000..510891813 --- /dev/null +++ b/testdata/drc/drcSimpleTests_3.drc @@ -0,0 +1,19 @@ + +# Foreign cell test + +source($drc_test_source, "TOPTOP") +target($drc_test_target) + +cell("TOP") + +a1 = input(1) +b1 = input(2) + +output_cell("NEW") + +a1.and(b1).output(1000, 0) + +cell("TOPTOP") + +a1.and(b1).output(1001, 0) + diff --git a/testdata/drc/drcSimpleTests_au1.gds b/testdata/drc/drcSimpleTests_au1.gds new file mode 100644 index 0000000000000000000000000000000000000000..6cd7e0683e5ef1582ae7cdc5a0414bb4698f6046 GIT binary patch literal 431 zcmY!lcJ=kt^>+;R4CduxWH!_@V0gjKfDB|rrGn#q9V6m{J>C6WUE)3cLR{TlgW|(I zT|zuKSY&u*Akv|J*c8Z!as|hS_y@#0yZZR>Fauf4AcB`c)S8Qdg{iBy;TPit{)e%m y7A#C1wSBb=3?dm9_)jptR1mqbhxLFRgQy`l1Iq>e1@ag8FY+@mHH>7yzyJV#FRG^i literal 0 HcmV?d00001 diff --git a/testdata/drc/drcSimpleTests_au2.gds b/testdata/drc/drcSimpleTests_au2.gds new file mode 100644 index 0000000000000000000000000000000000000000..7f15c2f3de6501626b2e3d5a808b4505d7b47e5e GIT binary patch literal 1849 zcmd^9ZA@EL7(VCTb9+y}S3I{;(hVJGNES)k{b|K?EUwuaSTPnNVLwWiE5G?!BqUU1 z!V;5qqiazY>N2K{Gq%~EIXkmzG%V>H-JG^a88Ej&qKSwG!OwGA7?lu1;!kgKPR{#0 z&--!S`{q35bscp-({j-3_G(p-x7J(0f(Xh#vsC5v9X;6Y^STdvT!(yaPpj*&>qXy7 z?nA9dFfwb+CA!VCoxm)qyuQ|!7ktmVnwvE|J@IVQ0;vH4gg)&avh_E8aSjGdA6bY$ zht6w5eA+2FIEbQ#gmO_#C@0{eL$X60Q{+$%L4t8EllT+?Lrzp7>{(wdke zxi{@3-5Hz`h8u~0L?vfCLLTB@5Mzq#%}e!UI>=``R@>A_PQU~_reizt#}p|cPN-)t zCFD`Qh2&}Yw37nummgoM4|yc(Pa>+mCq&_t;S%>FH($y2DFZ{kt1QFAFiDA=VU7~B zVK^s@!Wl!7TjCy6vVDdUqg#kDyhNF48fz5Ztdp$EBH{=7CiSnb!9ZVD4@lv6kz1C2 zt5HL_(MAUU5Y-^a?;@c;#oKxk{!3hzsHPvwcZ`KvdJ5p8nx0PJuHyqXHIVn~Na1xX z%K;ljhl(g5gFy_)2}l@DWOT9y*lp~%}CL3Kq*?^#+X{G5A38h@Jj&%ikUGHpcRJnmu_Q;Tw^w;~1& zyE?txkxnu=j@7~=XrGD{F5qGd0qM!$jcF%6H|TJGugfDkw6yES#hVxi0AAB1_Zlq_ z=65fM#ySAgn&C!f=XU22>6ye6iV=N4NH&t5EPhR#)L#{TphLfj-YUy*yG;GnCJygl z0>7r~8*FssFn}R#5GRXS-rExpjCKg)dxChF)&XEpOA$wYTh%+&8GPeOrjJ##I?6ol z01oM{3XCnqpwZ}QSeL<6=a||y-qx(&QdLqJQ>2~7ItZ^@2Kl1i3<^v%;D goPcZy=}U8pqtlwpA=YWqckdAeHbAcb|NH^)H<^}dQ~&?~ literal 0 HcmV?d00001 diff --git a/testdata/drc/drcSimpleTests_au3.gds b/testdata/drc/drcSimpleTests_au3.gds new file mode 100644 index 0000000000000000000000000000000000000000..7f496f52f3da5fd028c71bb6cb4ee75ddd3ce88b GIT binary patch literal 437 zcmY!lcJ=kt^>+;R4CduxWH!_@V0gjKfDB|rrGn#q9V6m{J>C6WUE)3cLR{TlgW|(I zT|zuKSY&u*Akv|J*c8Z!as|hS_y@#0yZZR>FaudKf;`NAuHit64afr`W*7|;W(FzY iW#VNNsRp`Z4bvHp6O1n{c^O2iUn00n4I>#aFaQ8BM5);T literal 0 HcmV?d00001 diff --git a/testdata/drc/drcSuiteTests.drc b/testdata/drc/drcSuiteTests.drc new file mode 100644 index 000000000..3602a6ca4 --- /dev/null +++ b/testdata/drc/drcSuiteTests.drc @@ -0,0 +1,494 @@ + +def message(m) + $stdout.puts(m) + $stdout.flush +end + +def run_testsuite(dm, ic, tiled = false) + + lb = 100 + + a = input(RBA::LayerInfo::new(1, 0)) + b = input(2) + c = input(3) + x = input(10) + y = input(11) + + h = {} + layers.each { |l| h[l] = true } + h[RBA::LayerInfo::new(1, 0)] || raise("missing layer 1/0 in layers list") + + c.is_merged? == false || raise("unexpected value") + + message "--- general #{lb}" + + l1 = a&b + l1.output(lb, dm) + l1.is_empty? && raise("must not be empty") + + a.and(b).xor(l1).is_empty? || raise("xor not empty") + tiled || (a.and(b).is_merged? == true || raise("unexpected value")) + + a.xor(b).output(RBA::LayerInfo::new(lb + 1, dm)) + a.xor(b).xor(a ^ b).is_empty? || raise("xor not empty") + a.not(b).output(lb + 2, dm) + a.not(b).xor(a - b).is_empty? || raise("xor not empty") + a.or(b).output(lb + 3, dm) + a.or(b).xor(a | b).is_empty? || raise("xor not empty") + + a.join(b).output(lb + 4, dm) + a.join(b).xor(a + b).is_empty? || raise("xor not empty") + + if !tiled + a.join(b).data.size == 16 * ic || raise("unexpected shape count") + end + + c.raw.is_clean? == false || raise("unexpected value") + # c.raw switched the semantics + c.is_clean? == false || raise("unexpected value") + (c.area / ic).to_s == "10.0" || raise("unexpected value") + (c.edges.length / ic).to_s == "18.0" || raise("unexpected value") + (c.perimeter / ic).to_s == "18.0" || raise("unexpected value") + c.clean + c.is_clean? == true || raise("unexpected value") + (c.area / ic).to_s == "9.0" || raise("unexpected value") + (c.edges.length / ic).to_s == "14.0" || raise("unexpected value") + (c.perimeter / ic).to_s == "14.0" || raise("unexpected value") + (c.dup.area / ic).to_s == "9.0" || raise("unexpected value") + (c.raw.clean.area / ic).to_s == "9.0" || raise("unexpected value") + (c.area / ic).to_s == "9.0" || raise("unexpected value") + (c.raw.area / ic).to_s == "10.0" || raise("unexpected value") + c.clean + + if ic == 1 + c.bbox.to_s == "(-4,-5;0,-2)" || raise("unexpected value") + end + + lb += 10 + message "--- centers, start_segments, end_segments #{lb}" + + a.edges.centers(0.1).extended_out(0.01).output(lb, dm) + a.edges.centers(0, 0.5).extended_out(0.01).output(lb + 1, dm) + a.edges.centers(0.5, 0.5).extended_out(0.01).output(lb + 2, dm) + a.edges.start_segments(0.1).extended_out(0.01).output(lb + 3, dm) + a.edges.start_segments(0, 0.5).extended_out(0.01).output(lb + 4, dm) + a.edges.start_segments(0.5, 0.5).extended_out(0.01).output(lb + 5, dm) + a.edges.end_segments(0.1).extended_out(0.01).output(lb + 6, dm) + a.edges.end_segments(0, 0.5).extended_out(0.01).output(lb + 7, dm) + a.edges.end_segments(0.5, 0.5).extended_out(0.01).output(lb + 8, dm) + + a.polygons? == true || raise("unexpected value") + a.edges? == false || raise("unexpected value") + a.edge_pairs? == false || raise("unexpected value") + a.edges.edge_pairs? == false || raise("unexpected value") + a.edges.polygons? == false || raise("unexpected value") + a.edges.edges? == true || raise("unexpected value") + a.space(0.5).edge_pairs? == true || raise("unexpected value") + a.space(0.5).polygons? == false || raise("unexpected value") + a.space(0.5).edges? == false || raise("unexpected value") + + lb += 10 + message "--- extended #{lb}" + + a.edges.with_length(0.6).extended(begin: 0.1, end: 0.15, out: 0.1, in: -0.05).output(lb, dm) + a.edges.with_length(0.6).extended(out: 0.1, in: -0.05).output(lb + 1, dm) + a.edges.with_length(0..0.7).extended(out: 0.1, in: -0.05).output(lb + 2, dm) + a.edges.with_length(0..0.7).extended(out: 0.1, in: -0.05, joined: true).output(lb + 3, dm) + a.edges.with_length(0..0.7).extended(begin: 0.1, end: 0.15, out: 0.1, in: 0.05, joined: true).output(lb + 4, dm) + a.edges.with_length(0..0.7).extended(0.1, 0.15, 0.1, 0.05).output(lb + 5, dm) + a.edges.with_length(0..0.7).extended(0.1, 0.15, 0.1, 0.05, true).output(lb + 6, dm) + a.edges.with_length(0..0.7).extended_out(0.1).output(lb + 7, dm) + a.edges.with_length(0..0.7).extended_in(0.1).output(lb + 8, dm) + + lb += 10 + message "--- extents #{lb}" + + b.extents.output(lb, dm) + + lb += 10 + message "--- first_edges, second_edges, edges #{lb}" + + a.enclosing(b, 0.5, whole_edges).first_edges.extended_out(0.01).output(lb, dm) + a.enclosing(b, 0.5, whole_edges).second_edges.extended_out(0.01).output(lb + 1, dm) + a.enclosing(b, 0.5, whole_edges).edges.extended_out(0.01).output(lb + 2, dm) + + lb += 10 + message "--- hulls, holes #{lb}" + + c.xor(b).holes.output(lb, dm) + c.xor(b).hulls.output(lb + 1, dm) + c.xor(b).xor(c.xor(b).hulls - c.xor(b).holes).is_empty? || raise("xor not empty") + + lb += 10 + message "--- interacting, in, not_in #{lb}" + + b.interacting(a).output(lb, dm) + b.interacting(a).in(b).output(lb + 1, dm) + (b|a).not_in(b).output(lb + 2, dm) + x.in(b).output(lb + 3, dm) + b.sized(0.1).in(b).is_empty? == true || raise("unexpected value") + + lb += 10 + message "--- inside, outside, overlapping, interacting #{lb}" + + b.inside(c).output(lb, dm) + b.outside(c).output(lb + 1, dm) + b.overlapping(c).output(lb + 2, dm) + b.interacting(c).output(lb + 3, dm) + bdup = b.dup + bdup.select_inside(c) + bdup.xor(b.inside(c)).output(lb + 4, dm) + bdup = b.dup + bdup.select_outside(c) + bdup.xor(b.outside(c)).output(lb + 5, dm) + bdup = b.dup + bdup.select_overlapping(c) + bdup.xor(b.overlapping(c)).output(lb + 6, dm) + bdup = b.dup + bdup.select_interacting(c) + bdup.xor(b.interacting(c)).output(lb + 7, dm) + + lb += 10 + message "--- merge #{lb}" + + c.raw + if !tiled + c.merged.is_merged? == true || raise("unexpected value") + end + c.merged.output(lb, dm) + c.merged(2).output(lb + 1, dm) + c.merged(3).output(lb + 2, dm) + c.clean + cdup = c.dup + cdup.merge.xor(c.merged).output(lb + 3, dm) + cdup.xor(c.merged).output(lb + 4, dm) + cdup = c.dup + cdup.merge(2).xor(c.merged(2)).output(lb + 5, dm) + cdup.xor(c.merged(2)).output(lb + 6, dm) + + lb += 10 + message "--- move, transform, rotate, scale #{lb}" + + c.moved(0.2, -0.1).output(lb, dm) + cdup = c.dup + cdup.move(0.2, -0.1) + cdup.xor(c.moved(0.2, -0.1)).is_empty? == true || raise("xor not empty") + t = RBA::DCplxTrans::new(0.5) + c.transformed(t).output(lb + 1, dm) + cdup = c.dup + cdup.transform(t) + cdup.xor(c.transformed(t)).is_empty? == true || raise("xor not empty") + c.scaled(0.5).output(lb + 2, dm) + cdup = c.dup + cdup.scale(0.5) + cdup.xor(c.scaled(0.5)).is_empty? == true || raise("xor not empty") + c.rotated(45).output(lb + 3, dm) + cdup = c.dup + cdup.rotate(45) + cdup.xor(c.rotated(45)).is_empty? == true || raise("xor not empty") + + lb += 10 + message "--- rectangles, rectilinear #{lb}" + + a.rectangles.output(lb, dm) + a.non_rectangles.output(lb + 1, dm) + a.rectilinear.output(lb + 2, dm) + a.non_rectilinear.output(lb + 3, dm) + c.raw + c.non_rectangles.output(lb + 4, dm) + c.clean + c.rectangles.output(lb + 5, dm) + + lb += 10 + message "--- enclosing #{lb}" + + a.enclosing(b, 0.5).polygons.output(lb, dm) + a.enclosing(b, 0.5).polygons.xor(a.enc(b, 0.5).polygons).is_empty? == true || raise("xor not empty") + a.enclosing(b, 0.5, euclidian).polygons.output(lb + 1, dm) + a.enclosing(b, 0.5, square).polygons.output(lb + 2, dm) + a.enclosing(b, 0.5, projection).polygons.output(lb + 3, dm) + a.enclosing(b, 0.5, euclidian, whole_edges).polygons.output(lb + 4, dm) + a.enclosing(b, 0.5, euclidian, projection_limits(0.4, nil)).polygons.output(lb + 5, dm) + a.enclosing(b, 0.5, euclidian, projection_limits(nil, 0.4)).polygons.output(lb + 6, dm) + a.enclosing(b, 0.5, euclidian, projection_limits(0..0.4)).polygons.output(lb + 7, dm) + + lb += 10 + message "--- enclosing (edges) #{lb}" + + ae = a.edges + be = b.edges + ae.enclosing(be, 0.5).polygons.output(lb, dm) + ae.enclosing(be, 0.5).polygons.xor(ae.enc(be, 0.5).polygons).is_empty? == true || raise("xor not empty") + ae.enclosing(be, 0.5, euclidian).polygons.output(lb + 1, dm) + ae.enclosing(be, 0.5, square).polygons.output(lb + 2, dm) + ae.enclosing(be, 0.5, projection).polygons.output(lb + 3, dm) + ae.enclosing(be, 0.5, euclidian, whole_edges).polygons.output(lb + 4, dm) + ae.enclosing(be, 0.5, euclidian, projection_limits(0.4, nil)).polygons.output(lb + 5, dm) + ae.enclosing(be, 0.5, euclidian, projection_limits(nil, 0.4)).polygons.output(lb + 6, dm) + ae.enclosing(be, 0.5, euclidian, projection_limits(0..0.4)).polygons.output(lb + 7, dm) + + lb += 10 + message "--- isolated #{lb}" + + b.isolated(0.4).polygons.output(lb, dm) + b.isolated(0.4).polygons.xor(b.iso(0.4).polygons).is_empty? == true || raise("xor not empty") + b.isolated(0.4, euclidian).polygons.output(lb + 1, dm) + b.isolated(0.4, square).polygons.output(lb + 2, dm) + b.isolated(0.4, projection).polygons.output(lb + 3, dm) + b.isolated(0.4, euclidian, whole_edges).polygons.output(lb + 4, dm) + b.isolated(0.4, euclidian, projection_limits(0.4, nil)).polygons.output(lb + 5, dm) + b.isolated(0.4, euclidian, projection_limits(nil, 0.4)).polygons.output(lb + 6, dm) + b.isolated(0.4, euclidian, projection_limits(0..0.4)).polygons.output(lb + 7, dm) + + lb += 10 + message "--- notch #{lb}" + + b.notch(0.4).polygons.output(lb, dm) + b.notch(0.4, euclidian).polygons.output(lb + 1, dm) + b.notch(0.4, square).polygons.output(lb + 2, dm) + b.notch(0.4, projection).polygons.output(lb + 3, dm) + b.notch(0.4, euclidian, whole_edges).polygons.output(lb + 4, dm) + b.notch(0.4, euclidian, projection_limits(0.4, nil)).polygons.output(lb + 5, dm) + b.notch(0.4, euclidian, projection_limits(nil, 0.4)).polygons.output(lb + 6, dm) + b.notch(0.4, euclidian, projection_limits(0..0.4)).polygons.output(lb + 7, dm) + + lb += 10 + message "--- overlap #{lb}" + + a.overlap(b, 0.5).polygons.output(lb, dm) + a.overlap(b, 0.5, euclidian).polygons.output(lb + 1, dm) + a.overlap(b, 0.5, square).polygons.output(lb + 2, dm) + a.overlap(b, 0.5, projection).polygons.output(lb + 3, dm) + a.overlap(b, 0.5, euclidian, whole_edges).polygons.output(lb + 4, dm) + a.overlap(b, 0.5, euclidian, projection_limits(0.4, nil)).polygons.output(lb + 5, dm) + a.overlap(b, 0.5, euclidian, projection_limits(nil, 0.4)).polygons.output(lb + 6, dm) + a.overlap(b, 0.5, euclidian, projection_limits(0..0.4)).polygons.output(lb + 7, dm) + + lb += 10 + message "--- overlap (edges) #{lb}" + + ae = a.edges + be = b.edges + ae.overlap(be, 0.5).polygons.output(lb, dm) + ae.overlap(be, 0.5, euclidian).polygons.output(lb + 1, dm) + ae.overlap(be, 0.5, square).polygons.output(lb + 2, dm) + ae.overlap(be, 0.5, projection).polygons.output(lb + 3, dm) + ae.overlap(be, 0.5, euclidian, whole_edges).polygons.output(lb + 4, dm) + ae.overlap(be, 0.5, euclidian, projection_limits(0.4, nil)).polygons.output(lb + 5, dm) + ae.overlap(be, 0.5, euclidian, projection_limits(nil, 0.4)).polygons.output(lb + 6, dm) + ae.overlap(be, 0.5, euclidian, projection_limits(0..0.4)).polygons.output(lb + 7, dm) + + lb += 10 + message "--- separation #{lb}" + + a.separation(b, 0.4).polygons.output(lb, dm) + a.separation(b, 0.4).polygons.xor(a.sep(b, 0.4).polygons).is_empty? == true || raise("xor not empty") + a.separation(b, 0.4, euclidian).polygons.output(lb + 1, dm) + a.separation(b, 0.4, square).polygons.output(lb + 2, dm) + a.separation(b, 0.4, projection).polygons.output(lb + 3, dm) + a.separation(b, 0.4, euclidian, whole_edges).polygons.output(lb + 4, dm) + a.separation(b, 0.4, euclidian, projection_limits(0.4, nil)).polygons.output(lb + 5, dm) + a.separation(b, 0.4, euclidian, projection_limits(nil, 0.4)).polygons.output(lb + 6, dm) + a.separation(b, 0.4, euclidian, projection_limits(0..0.4)).polygons.output(lb + 7, dm) + + lb += 10 + message "--- sized #{lb}" + + x.sized(0.1).output(lb, dm) + x.sized(0.1, 0).output(lb + 1, dm) + x.sized(0.1, diamond_limit).output(lb + 2, dm) + x.sized(0.1, octagon_limit).output(lb + 3, dm) + x.sized(0.1, square_limit).output(lb + 4, dm) + x.sized(0.1, acute_limit).output(lb + 5, dm) + x.sized(0.1, no_limit).output(lb + 6, dm) + xdup = x.dup + xdup.size(0.1, 0) + xdup.xor(x.sized(0.1, 0)).is_empty? == true || raise("xor not empty") + a.sized(4.0, no_limit).output(lb + 7, dm) + + lb += 10 + message "--- space #{lb}" + + b.space(0.4).polygons.output(lb, dm) + b.space(0.4, euclidian).polygons.output(lb + 1, dm) + b.space(0.4, square).polygons.output(lb + 2, dm) + b.space(0.4, projection).polygons.output(lb + 3, dm) + b.space(0.4, euclidian, whole_edges).polygons.output(lb + 4, dm) + b.space(0.4, euclidian, projection_limits(0.4, nil)).polygons.output(lb + 5, dm) + b.space(0.4, euclidian, projection_limits(nil, 0.4)).polygons.output(lb + 6, dm) + b.space(0.4, euclidian, projection_limits(0..0.4)).polygons.output(lb + 7, dm) + + lb += 10 + message "--- space (edges) #{lb}" + + be = b.edges + be.space(0.4).polygons.output(lb, dm) + be.space(0.4, euclidian).polygons.output(lb + 1, dm) + be.space(0.4, square).polygons.output(lb + 2, dm) + be.space(0.4, projection).polygons.output(lb + 3, dm) + be.space(0.4, euclidian, whole_edges).polygons.output(lb + 4, dm) + be.space(0.4, euclidian, projection_limits(0.4, nil)).polygons.output(lb + 5, dm) + be.space(0.4, euclidian, projection_limits(nil, 0.4)).polygons.output(lb + 6, dm) + be.space(0.4, euclidian, projection_limits(0..0.4)).polygons.output(lb + 7, dm) + + lb += 10 + message "--- width #{lb}" + + b.width(0.4).polygons.output(lb, dm) + b.width(0.4, euclidian).polygons.output(lb + 1, dm) + b.width(0.4, square).polygons.output(lb + 2, dm) + b.width(0.4, projection).polygons.output(lb + 3, dm) + b.width(0.4, euclidian, whole_edges).polygons.output(lb + 4, dm) + b.width(0.4, euclidian, projection_limits(0.4, nil)).polygons.output(lb + 5, dm) + b.width(0.4, euclidian, projection_limits(nil, 0.4)).polygons.output(lb + 6, dm) + b.width(0.4, euclidian, projection_limits(0..0.4)).polygons.output(lb + 7, dm) + + lb += 10 + message "--- width (edges) #{lb}" + + be = b.edges + be.width(0.4).polygons.output(lb, dm) + be.width(0.4, euclidian).polygons.output(lb + 1, dm) + be.width(0.4, square).polygons.output(lb + 2, dm) + be.width(0.4, projection).polygons.output(lb + 3, dm) + be.width(0.4, euclidian, whole_edges).polygons.output(lb + 4, dm) + be.width(0.4, euclidian, projection_limits(0.4, nil)).polygons.output(lb + 5, dm) + be.width(0.4, euclidian, projection_limits(nil, 0.4)).polygons.output(lb + 6, dm) + be.width(0.4, euclidian, projection_limits(0..0.4)).polygons.output(lb + 7, dm) + + lb += 10 + message "--- with_angle #{lb}" + + a.edges.with_angle(90.degree).extended_out(0.01).output(lb, dm) + a.edges.with_angle(45 .. 90.1).extended_out(0.01).output(lb + 1, dm) + a.edges.with_angle(0, 45.1).extended_out(0.01).output(lb + 2, dm) + a.edges.without_angle(90.degree).extended_out(0.01).output(lb + 3, dm) + a.edges.without_angle(45 .. 90.1).extended_out(0.01).output(lb + 4, dm) + a.edges.without_angle(0, 45.1).extended_out(0.01).output(lb + 5, dm) + a.with_angle(135).polygons.output(lb + 6, dm) + + lb += 10 + message "--- with_area #{lb}" + + b.with_area(0.49.um2).output(lb, dm) + b.with_area(0 .. 0.5).output(lb + 1, dm) + b.with_area(0.5, nil).output(lb + 2, dm) + b.without_area(0.49.um2).output(lb + 3, dm) + b.without_area(nil, 0.5).output(lb + 4, dm) + b.without_area(0.5, nil).output(lb + 5, dm) + + lb += 10 + message "--- with_perimeter #{lb}" + + b.with_perimeter(2.8.um).output(lb, dm) + b.with_perimeter(0 .. 3.um).output(lb + 1, dm) + b.with_perimeter(3.um, nil).output(lb + 2, dm) + b.without_perimeter(2.8.um).output(lb + 3, dm) + b.without_perimeter(nil, 3.um).output(lb + 4, dm) + b.without_perimeter(3.um, nil).output(lb + 5, dm) + + lb += 10 + message "--- with_bbox_height #{lb}" + + b.with_bbox_height(0.7.um).output(lb, dm) + b.with_bbox_height(0 .. 0.7.um+1.dbu).output(lb + 1, dm) + b.with_bbox_height(0.7.um, nil).output(lb + 2, dm) + b.without_bbox_height(0.7.um).output(lb + 3, dm) + b.without_bbox_height(nil, 0.7.um+1.dbu).output(lb + 4, dm) + b.without_bbox_height(0.7.um, nil).output(lb + 5, dm) + + lb += 10 + message "--- with_bbox_width #{lb}" + + b.with_bbox_width(0.7.um).output(lb, dm) + b.with_bbox_width(0 .. 0.7.um+1.dbu).output(lb + 1, dm) + b.with_bbox_width(0.7.um, nil).output(lb + 2, dm) + b.without_bbox_width(0.7.um).output(lb + 3, dm) + b.without_bbox_width(nil, 0.7.um+1.dbu).output(lb + 4, dm) + b.without_bbox_width(0.7.um, nil).output(lb + 5, dm) + + lb += 10 + message "--- with_bbox_min #{lb}" + + b.with_bbox_min(0.7.um).output(lb, dm) + b.with_bbox_min(0 .. 0.7.um+1.dbu).output(lb + 1, dm) + b.with_bbox_min(0.7.um, nil).output(lb + 2, dm) + b.without_bbox_min(0.7.um).output(lb + 3, dm) + b.without_bbox_min(nil, 0.7.um+1.dbu).output(lb + 4, dm) + b.without_bbox_min(0.7.um, nil).output(lb + 5, dm) + + lb += 10 + message "--- with_bbox_max #{lb}" + + b.with_bbox_max(0.7.um).output(lb, dm) + b.with_bbox_max(0 .. 0.7.um+1.dbu).output(lb + 1, dm) + b.with_bbox_max(0.7.um, nil).output(lb + 2, dm) + b.without_bbox_max(0.7.um).output(lb + 3, dm) + b.without_bbox_max(nil, 0.7.um+1.dbu).output(lb + 4, dm) + b.without_bbox_max(0.7.um, nil).output(lb + 5, dm) + + lb += 10 + message "--- with_length #{lb}" + + a.edges.with_length(0.6.um).extended_out(0.01).output(lb, dm) + a.edges.with_length(0 .. 0.6.um+1.dbu).extended_out(0.01).output(lb + 1, dm) + a.edges.with_length(0.6, nil).extended_out(0.01).output(lb + 2, dm) + a.edges.without_length(0.6.um).extended_out(0.01).output(lb + 3, dm) + a.edges.without_length(0 .. 0.6.um+1.dbu).extended_out(0.01).output(lb + 4, dm) + a.edges.without_length(0.6, nil).extended_out(0.01).output(lb + 5, dm) + + lb += 10 + message "--- ongrid, snap #{lb}" + + a.ongrid(0.2).polygons(1.dbu).output(lb, dm) + a.ongrid(0, 0.1).polygons(1.dbu).output(lb + 1, dm) + a.snapped(0.2).output(lb + 2, dm) + a.snapped(0, 0.1).output(lb + 3, dm) + adup = a.dup + adup.snap(0.2) + adup.xor(a.snapped(0.2)).output(lb + 4, dm) + + lb += 10 + message "--- odd polygon check #{lb}" + + y.odd_polygons.output(lb, dm) + +end + +if $drc_test_mode == 1 + + source($drc_test_source, "TOP") + target($drc_test_target, "TOP") + run_testsuite(0, 1) + +elsif $drc_test_mode == 2 + + target($drc_test_target, "TOPTOP") + source($drc_test_source, "TOPTOP") + run_testsuite(0, 900) + +elsif $drc_test_mode == 3 + + target($drc_test_target, "TOPTOP") + source($drc_test_source, "TOPTOP") + + tiles(4.1, 5.2) + # must be that big ... + tile_borders(1.um, 1.um) + threads(4) + + run_testsuite(0, 900, true) + +elsif $drc_test_mode == 4 + + target($drc_test_target, "TOPTOP") + source($drc_test_source, "TOPTOP") + + tiles(10000.0, 10000.0) + tile_borders(0, 0) + threads(4) + + run_testsuite(0, 900, true) + +end + diff --git a/testdata/drc/drcSuiteTests_au1.gds b/testdata/drc/drcSuiteTests_au1.gds new file mode 100644 index 0000000000000000000000000000000000000000..5cb92178edc8c60f44f6533d278a4abf5ec15e91 GIT binary patch literal 9653 zcmd^FeQ;FO72o^b+n3$9n*`E(!RMreq)iJ2nPe>`ueBMhBBmf=8W<3rGSjZD{l{Xf zon1}nZm^h8KC<#r=;*i$L=8pVHBgPlnq4&RVx$QI?#_Th>}Vo&G=f5`V9&kpBiWAx zLK5sunb{2c?mg%H&hMUk?s?~Kan-!rDz2-%v8tjfTS9#4tP)dCmTB<6d~$8o%G+*S zzOt%f{-Sv|udG-Ue06x~qD&L=iBz?6N#){|H_cnP(9XmE zc={*XgdcR8_HrGTDC$*+au>?^gv5m%6XU}6i6LuT2wD?DTPs;k_FHmq9D#kdZtp{;mK4 zcEyF=*^DOT9)W+L$FlnQNgov8YRTEidmT1T>dbmlbZ+IR;cBPkJX#J<-sAV7Q~XY} znRgQ9w;)s}uWp&#I~6ZK%WoiWmSC3@;kU}?X5t$Ti;eupb8Mz=VFE5&XAW7Poq?ru0SsRuru z>7{=4n>7#aS3T$>fdb0DS2jG@Kb?BOG?}Dxh6jLw^3QZ3$*pW&LO9=;tGsxVO=5cq>ao#T-3x_r72=ll5j zxhA4KD3}il@!9^xPl&JHHZWo{y_l0nlxo4eCp%j1`Q`h0yYGm}fBH7AJ}hoS%!3BQn^$XnCi`_dp06Xgh1y)5Ca;dGvVRo#FD!MAix#$S)p5Z|LD`UVCpSYP zvIm`+{K~f|QdkS*tc8d8-dc3U8Ojy3s(BPI@5%DY{Y&+ng=rk+eIz zk;i~WT_V#n*?HK#0ZAPeh$aPt7AcA@DEQX6&@~OaHyR^lR1kC+Gw!7P(>R?n{pWiS zQMMr@swr7b%U!uN!LnF`{Oa1v(VR^m=}DDL{|iD>X*Q3dKtc4{uH1ephR%cixKN+d zX0OX>;Qa-?S*P;)rOw>2J!m~I1z@(nppW>)eyJa3CIp)MlXKiadhcl9Q&KvS{fOm3 z6adIO(dFi2We<3lex9hV5an-ZSO_Lkx))5PA6&zqQpVwAP^PabQJ&7IkCBm%Q_I`K z>DveXb>?>E`V0I8^l#rRSgP25)RP5kp7;mq%nkhrPM_s3Og=RuQHc|;z(}BgW`*On zmdW+s#fi3*CqxM&f&Vaw8h^5-@L~|HpVYjZl&~i|W^SCcW9FtwChVq*t)CAulyVGW z`5gNbJQcjFo!#hkvPgbNj1eHb;4wPMj z>%m-3e;1OT&Lr%VlT2A%PGnWcx?^G!zARNbuctuR;8p1!sb|W2BbIm{BsSQi$m7^0 z#?UL3v4s?H#2);`CVLEZt$?Btf_@Xs2v!7OMoR@9rxL*mI^*H8*!8M}$FYG)s2UEt z+KStkg{-Hm=;)J{xX|#!MoU7dpBuX#m$jO`GHlH127WKsQjw^%nK~>Ji1NYL0R~cg zDjYa^djQG-dtC4=2*X~bsHq=egQLUCv_IcH45_o<{Y%JA|iVvOG4WHcgY(T}I%CG=&$ zW{M)%)E|N$)SxdvhSZhu3rFV4}tc zdtYZF1Tt#Obgj^0X28o#eyB@adYa+Uql>8Sv&cz}n@mbzVg8*lMg|9{N_t9&f=`2k zkSHIGi5{Hz1QrLp7xq9hPI97($BupLx84%mHspa3?r;)CF% zW7ySdjv{8R@DVWAY(UV}bmq1nNmf&{t3Xvll+7R#86HvTmQ*j$R$%0D4i4UCvF>fo zvr%kcXALjwzX!Xv-}J8qT{n7bpiTm*tDfUBZskrID@57#bpT3Bx+bi_*$r^6ey$q| z51!q4h1pK`=Lzsf&v|-wx*Az8%_B8Ot1Op3$xvNZ=1WrqdGD!=F*DMP%b7PNS=L5iI)K@W_l|3^_8JMkF+cm+EV4mVbH$h52o z6i}i^Q-vgjA7xR)hi5;Vx-+59P#L~NA)>E95FEV3)}+qB>yW4BRG#k4*cT{iE+z#^ zX^6ob`CesY5#_ndjI%4>z0KogLEF!N2mRf10^&aN`*T7?7uX9?t$cDZEjhe$-+ZG< zL6qk&HT4?Wt0u||FfURbfSy}>d8h~)<`AW|GJO};RI+&(wQ#hm4HZFY0cU6w-kkg9 zlfoB4x>okooFP7*Njd}Kd1&!Ldk$?Er}TfklB+yGznd$*bK2~EJp?_-UW@Kv%C0XxHyPw_>H zRsTXu*f%>{DtrB0-)#DQog66+i13Q8dAAD|Qhm}lD^RBTgVBLZi>JH_)@vQAcJ&=- z7j$_d%K~E9-Zx7vSoOta%T3Y1==O|^IXN!yh;nKy^=g-xNxXKCrGbG)wEMjwsKU4R zKxdRD9lY2N{YZoF+k5rr#%ZIGJfKxu2%>yAKBx`-r$?jTz@Hw`&|VppW+Q^#AEok} z_*BmcC(dx{ZS|n|JUbe*L*DCVSeehp#Zacm{d`<34Y+jToI#`eONdSS(}47-y)c5P zy*LJljR+V3YDDc7!3eE2*}s+D`nH=hnzCmDu3bWUmDeZ9vMpo`WIn(|4v)koZGp4PDJQgRrEStQLsRJ^PV2E*KAk+op2$K*2j$;S`k=S4!A>tYF3W7LffA@~Q zPqHN*At7CR#W7d&&Ew9UJ9qBAGvCa71rI+kdBW(aKYn<^!#P&P^p|7)!Vu0hB>uCj zLmqx&@{j-eiH9dVIOTz#JTYO)BM&_Iz_cf(Px#3rKg%-PbL=wt(J84i*j4MpPdqa9 zp(pNtVA3R;1tI*IV!t^C@w*v@8e?--cv{q|*zKMvu92$aNu_T@4Vf4A51A|a*JX#y z{_K6`koj1qKf5k_c3;mFpT(dSDp@{D2+{k@9uxg<&5af=?2iPqx#<_KvCfHGmngpR z)q=8G0=AvGQESJ5%{HGUG6v3TG0^h-wgR9#dWR!v0xy zL&+I)({$klQ-s%<2N0R70J@eD^u12d^O_N`IzTY6lc0Zx2{713u;UcL){o7A;V41p zoD9IoGoZrjnorR83PF#LDygm`7-%BsZ^!}+zC*BMKf%@z2=#`K5p;e=Fmm1s=ql|4 z=zEc%r`!fuy@X(3EkXZku-Y4JB-rsT!Paek0mB~T z=MfAnBj{f=05JF(!HxjI)(y7-hIbHjwiAr(5rD2RLEj~Uo^#-+w|dqIk;JIv6nAK+xGjF!DBW+qI9N?>Irvks*N97YGJQZwK@{?SR2@f*nf;wpQi? zhF24GzCkcje+QszJ3-%v1U>H$1*|?wFmREe|I9GJV99X6j(G%IpC17jUPRF8Cm30I zC!lL1L0>aqkKG;gpb^-QLbe6*cippA8 zK+&)rwokMjg}oDU=uvwzggOMv5YG9Q_TM+h0bRK~X z5|uMSyxl!MI7G8K_Q-=Flqi~EUG_t-&|}x#*LpW{&YI?ibuxIS)aD3V>}9g1AlC7rwN{HfY)K`oC99j-1`Vtt^mhuH5UjrYysbF z&E?>mt?dAuILE)CyIgrshrb`aQ)|>?=S|*1FW6aRZ8EvX9|s!?cUd|opSAVOek`1m z5qWS;p4Pmg8l&~(k6Q26p6v|}_0-P$1v;;K)YC2F_m9w86YD(f(Z1}5ETHG7^vpIw z2G2tV>j!BK>P3>MI{qE~7tMOwH=Z2dLa0ibFhT8LG9~PR={_hY=pENc);rNh-IQAW zQyQ9>!G)o;?16jHy{h9s>i?@ifkB|9GLzI)HBj$pX$*PiWWA+S3$-+cI6RFpR@~1R|4X*C zbG#dK4PfkAduS@>bwObO2Mc2mw&beS6#r(IW$clOA!v4ca8ff&ialDm?%M|O=zoXI zfqOME1_wiCwip}w3R`#@^l{Wjw!-xZ!HP{}F{+;5zNF%w`vu$BG54)y~h><2nOq*3izN#ohaexRTIzySM! zt?UPOupbyx{lE_P10(PQMRAED*XiP`Ea~;S^m^UbvtC!^&z39Cu&-G99go#xeGN|(1#E47 zd_&0}_?26+F3ahh3_}|-v$5`pb1G5%ykc$6+8tkM7hnLqtRt|(lR{<>rk}-SS{Aqx z)MRRYJlA8$)l(gIdw8119#e>_V}YVrY(+ESll7bPS-k0PxL1=HMiL zH=r%{8r0D*D#|c=Q4mzBh6ac1%)MmYrbMklC1jr6H(+!1^_YA^k7XVlaLL-5yV>T? zj#{gb+c)%#>a$$3UP4Y<7B=9$MAwzHgjW&G9#i`51BPSz?ZZ{xKB)OVCGn+MMwJ5p zXNhv-?+qNQFa}b*7n6bxWUne|`Gf?ft?kWNTXkFwWHI>-t)_ufzO{MZmAw3tt>TOc6yP;FQCczE`0}Xd2d*6a>Cdcxv-*u}B!U7E-0^V^W7esbae#V2 zBOh6{Ab#<5B6v5NDZ^W8@*AH@%F%jy+TxZmjZYzuRup*<3#e6#pjKEWNVG|~v`iWz zS?`V1aIUN%iOwV%m>d}^TYhmt2Db=LlFy(hkuBgKG4~In$_9eF5QFF)d-_{{E(@c? zo&#o-h?G9?Uj0BuB%in*1mzPTsME8`c$vhE({t$ojt17mtO$c?5{sTu29mHK+Ozl` zz@!w3G?^S3pDOzb?{^!Y(rH29v)#jWDr7BMy8LBy68ORO4N23@!M1b&JJn zTXklX*1!5_0mit9pE~g^JTnuuVS?u_f{w0O=*~Q{Fk4ZS#-|#eQtfX~o!f3SsJ8QO z_A{t<_o<8}F1+uw&58_C6obs-+t>%GS_#_+@7j@b|4Q5XJ-NtP(U_0Nvlsg(7TJ3_ z^OaY#6r)jMcou49he3 zls{IRReL04Vzm75x-Xj$C>IuFsP?K|Cdwus4j(oW0{Jj&MfT~JM_3hf&ifyV!&1M* zh5#v;?; z01f*UNX5#xGPItsf3eKkP-!!$BK#2?E_>(N%(8t0s6P($?UaK^ zzp~qSc}@b@Hk`wQKbvVoOSIvHmQp_iYq=jr z%61`P{Recih@LMgC80=)F1f6m&OL8}`_Q6`N4W>aG(q`RD-9V# zY+$}I+g!H6j4=rDR1C4PhGGeMY!FsG$-CA_qmqiBfdKFz*Kk$O%u2vQgIqDhRK(Fw zvM?BQESER+h0yD%@9oWo%D^Lfl=^GtA55$m?9G>jJ~UAVV?z$p`Ny(oXc~ApAvZLG ziN~qOV+JM}KGbXl78$Ou$wezn+|yfXjL=tB^UBITyBaJz>j#z|9CGEHrS{AyK;jk* z`ttj{!xxTvVc^?SOV4NOS`2zGX~~MgaTL)z0&iFu1xXF5<++_?Eb2bD0=Mi`v=fms z3|)Psevl0Nj}BOWAzy~U*Yk((kYV%&YL{(2S7t>8MguF`J%@8qxqKL0x^)mRB{cAg zU-`IHS@%c9I#O3;GO%<{UpQ_zq$jlznUwiUD*(3Ovi`b5nb73wC|v?c)u1P=Dk$@| zWXQftP}?#(i3UA)lID>qvNlaGRL?e&q+`sVU2e3F(fwBB1vjKJVj{IR>-tG|ijwL< z1Uob`0R^SoBPmTv)OQxni9bQ>tq-9W9fuB^cm{ysVJExiZOs00ieUfHIE z1dxg%IyqV&zYHdULx(+Cth`Oqr7GV|&JJ`x)TFMOo5E&jN(@c!@;=f{t3KCH#w=gL zZlA#zQ`7G~$?Dz|dvxw4R|tq=&%5RkV(1*Wk84Qhy!RyZuGGPeZ&{$gW%y7d`C>8* z9H7ty0#&=Gg#0xQOQkHkdBM-AmopPwSTI@n*8L1u^y#zs?2$Z6E~0Xez^Y3xt! z_s0F=7&1x6Ld;31^Wi4!e!gFpL2IB`|3x-jo;7WK0Bir0117?afeuFClS`!fb^EY+ zJ*i)0TlH(*guqJyIuq=HYKK>Ya(Z^(Y60~vlU9exqUh?FJSi|Frg!1dkfJ38$*LP( zja0O7yr2eSxj+nQTDF)#r)yDHLqF2G91jnUG%8!nRllZa4x$3mtsDk&QpOyHy9GsV zHP&ssui?_zG4aH@SARkT4>0^D#%Ve!F>c;=)kpQnKcswdaVS^Zt?YBN@)Gi4GOJaf7eqj- zK*q^=F;9^bYt+MRfAlAmkp&P)N=eMM)_0e-nvhrq;R6RoYcwVn09xp2tx?FAnE3JX z@1!atvuIQP!~&AYR4IC{B=Wy%!@4$EucvF1^-5$5yEZv7@#<{ST9uglhB%R207P7! z4I7UROXxzqJzb+EPBbG`|7enySg{tT;{y#WrHPq1A~DA&tyvl^2|;b3!CGt8Yf-?e z1-n>r%t^@XzvY9BR_mgrrw6+HC*rs$pO(OEQD{{kh{*Kt`DaS4>S_caP z)3@00u2i1A!d9yS=fsu*dZ)_s@TT2LOTpIJv9bm4B^2TGYi4)sM)qswxQi%o&Ui1E zR@VRZeT9yU;pl;=3{Q(~Y>8Thn>Mz%d1H&4H@3KWV~d+Nwzzp?i<>vLxOroXn>V(& zd1H&4H@3KWV~d+Nwzzp?i<>vLxOroXn>V(&d1H&4H@3KWV~d+Nwzzp?i<>vLxOroX zn>V(&d1H&4H@3KWV~d+Mwy2}`=xvU_mWD)iWu$EP0%e{Z4Oa{HqKuXSkD+UE*|9Gn zHD$Ls+K^*yw$DKEm-z5mT+cMK^=~kL<<90V*@*GTq;H% zz)qo_EfbCoW`e74(=X`oVZza}8)StpC&{RLDPp^1PIXlWP>!WOI&vo(sjW z?D|chVC(eAgU?}U9qB8wupQ+G%d_@j7X=-5D{RQ{jpq$39#f4moOuZgZ}#RaI^V&k zH3gs06nrXE@JUR;r!NJcxDQL}WLqVqtRmaCkOw=?sbLAUg zB{shF7G{RQ%q_N%**S4D>=#K&0y8)3q8ijhm7^wZf!sMYLEJ*QCQ4x?`K4Z%tw>qq zcg|8)H|w&Bg)Rv13_?OLiF*w<#jlRR^futwN{olJ8NTO_IPq9eNe~R217PJr=ca-R zHksd#y{gsawEw;S0KwpD+65o_j9_>h?U#+dNc&}7M+sJ}qh0XTCA9y&zLj8b5$%5u zog^6EK>KB*F4`~a+DouvIYh)(eU4!LX4(ZGe1Z1AhuR5-U#0!B(MtqfLE1H2v4Hl! zS05o*zlL_f2c6JQ3s7#~hM}1-X3Omujm6L8 zZR0-U(YSU*P=VhUAEB@?O|->%9tvX*2aVWg@}L8X%i&0|wVQ?nczH>I9pBb#hudr|WpC$D8u6Md1GdhhOyK%Nr44cO>YNf=pvZUUERk3gwL z3&~PEdj|UD*|P-B5jaoaQvw%hd*5YLy1f9lhg@{%T{Yt~L}zp&gX43!zw$hG{uL#b z`UR!V!VZRvX9<)-Vdekc7|Z1{#&XR@nxo8UHXXBK=jwM)Sj$Z($7(*<^t$Z1<~_fu zpFq`N%&f&;Ck!J~xhYymdB(lat)Lu6*DIFcL8W9zkTB(Kg< z4uiv~q6qgZD6-muxu`Zyi;k-`ckAwMxQdq!jp0%q^H7SaymphkNlnU_Pdr*|#B_PB z#kJSyEVFE#gE`Z}@4&iO+<#Jsz#`DUEq-pEohZaI6!hO|GN_J`q$-vY!SirfOcYfnKp|i%=>2<6go8)lr>fVKp&>hjud_ZA@6<5Uor?&8FsNkXEvbtU7A0 zfpQS77qZ{T&gD11>d5nLCAR9wH^m9Q9Zv9#aDs1v6MXZV;M?8=-|!~*RyV;nxe30# zP4JCvf^TUPd^4Ni+t>u(z$WhFE458GpjcD0FLN z46Qg8)fj`=`+|zqf(^h?lIJWwr_v)4HUm2)tfe#LCBiykB4Z=15H?(wI@Pi12J!?_ zqp$mZ^pZ~L2U+>^G7{;e`%PF_<%m4$jE*4U-!X&L1of$bx) z)`^TQSLs~Go1mj)tgI#W9(O5D4>wJ(vf$c=5wR6-t^1`!5MfBK=wCXcqCe$;Dv!%V ztQShgR#~u#g$NVB-$*^8c)O8rM!_bm9(k2!q1Q@xy~UKM z_};8cH!b7u0QY|D8mnD^Coe+jO)boCHp=_2{w17grQ7UqvrD&YuzKdbjB5Tn+%m04 zCxtV6Q}e{*E!fyZcMEC_>@6K9ir?*(s-)FuHqyrkc(`Ff(MaDWpnD(NcVlMC*TswC z_l^2D2+|k-q(3`w?avOhNwLu!f5#qUw`+Q?@o5a@OJ0tmI`)1|*mVI#HBqwmru{-Z~TLq zs^erg$J1GcoqzY&%+SN}XcSH~CZKf;|6>6ee*jvI9tbycJ}jPXhIm9I$yrhE$- zujY%dSWSAap8kB#O?|${;WTyKR`S*7>N8Ec$D3XzO)ryP+vjXAHFeqNB-(#(O3&5P zbM@;?Rl3jBUEQ?UEuX8;zJ(R0=j!Rn(Df!m-Cb7IG3ORaq+T2LGe4Ks&uQ_zo_H2} zO!$Oy?>K7WC^yBA328G#d#`Alczmv@>p7F69(~S>zT4e9I;O~)^2l2LJRFtvk1z=B zKv&Q1uaxKVM6nk_X{FOoIHcR7a0=FYQ;S~QagwNY};!a%3UpD3FFpwTH6X~_biS(9I9OL$$UP7do zy+Necen_OZTqM#@&%;4kZ<(J+uWcsMTf#*8=~=k&)>~FZq}OgD(p&Zs>8CFc>1A^v z2j1FRBE1FQB0~I8BE9UHJc?gTq_=D&(ogRt(#y^h>9wAr6kkK6pAHh~WgijgwKMO+ z$$Y47}fZ`~|m@#%7VT_Q0y0R@2 zVH|Sb_gic2y=O*}?ff-v9pcgcU3>j}KkxUgwZ8A-&wcXRPyH{S{lw=!^|>ht<$UO} z%R|Feq2T}0^%s2Zi`RbQufF)XPhGv}lUIH5Q;V+uZ4UYG$S9R|7UE$&DR3emH zgj3gT-Zo85H783Nk3>;(5WT+#8&vb?Ho$K%!m5ApS;S2gzeViV?ZY-e^ zQvx^iaRW-c4h6~wAN!2|JsdCTQ~hx?WwNUKwSE6{{>wPV%PSRL_8YdOaZ43y-?kPl zZMnCkr8xCp=1D_GWcWI?O{&!q@o(Z7C2y%h$$mkBeyIQ0U#r;u#dt)0rs$;bL|g&$iRD@OQ)|FLXGB>U79C&otRV@^^U8Tdpo^4E`6^AjU? zSK})fn#j6XUkW{3*E3Yrwy6f6#VF{{;*;`>ink?-k*A`?NN;qw>Z$1As*#t|`Qd6l z+f?N}+cuP}|FDWTp&hK!L*c!VF8s?aO3u)YuNbpwb5TU9h=<#2_eL5~Ilm>thcA-x zgMc(P&7ZcvWpe7<$b$XF z?#?*_Q^y+Gz7;uzAPCSa=~g&Z(`)R|Om?o<9@h2}713S3H=>6eJ@{^7EYV!Ukpuf{ zCKr*_R5e)lgSd#hfnCY*#odQhV#jn!i6W!~zi;KhL_z@LFrEOp2Rs20gAv9!fUPJR zi(skdnuo&6YryHbRUlC5z|Pp<=Rx-9K%sm-opXXu_Y9vWPm_M$O*FCyX&R-hNaGa_ zIhjoZaiD1+H5}Wne@^9`LaOJ%3oFUhBj`~_2)g-X6eXeuhN~=pe4}c44aXdnU{~UM zUEvbeFx}oCUtTkoXs>0r4~)h|77J6yz|=Q`mNbvXH%uu;eh@E$Gl#2w5Ep5r+ktV< zIa*Fpb}z5N7dh@FY$Jo0k13VRuU6z7bg)uY`Xt@G`a;|b2k(Wwdqs4Sa)S39doQDLI&KI zS~=$(2uzV$FQ>KCDxvRUM9Vir-y%6QFWh(+|^>NK;Lo@ll9(U*FBdlMkIa!-?wAMdd zRgCz|MQIH(H`;|{qqRey8hP#fiu$Pl^Au7)h(D9UcReAnHb(G5(35*8Nuq)%DfMJ05DW=x184>lOcWch)WATOXITFe2%aE#h(2VS2-db?bK3QMR;g9(ss-RNs| z29tG#pHunRa8>u=I^9*iUi5@B6eIbh3G@+^{kcV8pb7beBkWVtJI?YN))o}S+Fs7P zKEkwjp?f%3h1BwzrmFT@b_L|M?ux+Y3#o^~LSVwc7_9|@05DAq?11Nq{7ONCg{ut=b zkAdqk2B=goXvU{wqxHzx930WIZ-m)jWm21379zD=%a&OfmU=Df8tIz{-4n992lZ5; zn9*k=O*OdZ={nw1_TIqrkWIcm<2}dTgK!O~Gv12WTitJh5}_iqM+Ld^UO1*tJk2m% zRXkMZX@;QE$^p@e%GO2|ddf$i0MbOOB1B4{K&2cLG^WLjNN!3=yw?@B@=t^h#(g5; zQ_~u-+58?Iu6h)Jox)h)FBF}}UTlt3?Qqo?;2}j+?&$3Ds=;OzGP@XLmOn77oXi9a zXq`(f6EqY7o>3U7Cm-umMc93#&T!T68+C+K4W$@SSD1F-a|%!zV^C)Ep?Q-Mvu<7r zfMf#rZ`6Mw^=Nfd6;%bq-DR4^16otG49OdI_V5CT;ss3~g$O+bb4N6~5fLawiX_iK zao)*3BMn$(gV49rm1lr)f>N;{!~dp<)5N+1@<$>44u9ZeLm@+T`E9) zj+hy+F~mK=D)mM+7z6Qo3UYbPD5#1s0$M?{`2^hN34;LYl>rc*7FOSVu~BJt7fS4p?uoL>mgavHrJ8@yTk6YO*#6RoYSH-y-aE;S)G zf}sTfLmI^doLkbLf)TZN5_>zp5YkCS;81}X%?N;&36{^WH3p>65F zLo4U1uzo0wvRoB``_C(CTCgZ{LyEEwu_*5$7Nu`MLXIbzjkTZ&1t3HBf^{;X7c>Bb zQ!=P|_2V+KjWV#=e~y7SV4hkZOLw(3Pjibv2NU8X^zQ=kCS8U8)yxe3^H5(o|B1z} zk&~?>P?G_v1hm?O(|8(yR+E!B3`mr4KWWAnm#C3#f?`yx?*c7mj90K+_QRdAW4eCA z$PSH=v>wYnav>3tNTOgzl}iT!RwX)EL>isGC8oRX;1V`ygu}>V0;89$E1?m<_ZPFr z9*TyY+@gB7eQS~gRPT;8#ABiC!RjoChVb`bUEjqW3t{q$<)%boP^bViOHXNOl#fAn z;UPLb8mv)D!xyT0Qocg#eCM_BhHl3Mj$F^sCQAdaADWquz2kUB9oRBJP{qhMfFUSl zV42GZpgC|v4t*XXOb7sm=rSdi=$#6tsIV_RsUEB~WMVrlhfg(%`$aoffO1MOlc9Vc z2p3WV>k|eD7gC3-+5lImsQ_0v9+#IotT|q!V-NbEFDH=K*8D?nO~@$pq0;#z9@F;a z2F_V;zi3tr;Y6A>U_K02JrowCXgpW8I~rj!9-IXlA{G1odnp+r&ZwrGV^tHl%c!RQ zc!`~*EkAmV={~zTzbZio6Oe}p=*-Z~repizV+tTX5u=(UjTtvC^H?A%{u|9aFs5>j z@4wN#3DISkfA@?&#bE-B(TD=ZI%%R${QwoGK?JC$&C4UAn@siq|BDe`4(QcN1-JYYS*SHeI(3sFmiKOfa!0F~31{ z6jV>Zb{q)K9&k2_Gf^^$7vNR;ro|HRcvV|4P`WT+$g6a}Efk@56=WX8`XvhPCLh+) z!&V|lbIv!a4p)gFeL3w7mXGU60zvx;9it%^iq&7I!vvq#C4;s~xL*bWmHJz$)Xk`r zVfR96ZH#kHA~7FI8dlWeS+|ptmJ=A_3k`S+JyTJC5xSd#5HNV-JmX`dl-0Zr=B!uZkWEgvBVu`CT!kpsyX>Z^W$cR231Uhc3`f9&Fd$6yKlp)tMmj2RZjnV$d^@8f2Nd zQGCYuN%tp9@QIcnAY9M82AT_Z#yVoVUzeH956cuCW;)v%lIM>%k&FpiPEq2+=nM*7 zPBG}tz*jje#<gV_YS@gSo-5?fxw*Gbf{%2?=!ODV(3KC9y=9(vH&+87=e zJNcocbd17;CglyZ*Dq#6-b{+`g8o$!kw^|rJm?GGZvp8M=nE(?&5IL&6HfqY5i4E+ zG6vMCRuSX4fIYEHfqO~N$DpSw$#uptpxx?1(dGIccxiljZu=6YPt=_l+8xt9_XR$N z`S1yyGUZrbH0zJ1YCo6=59uL|VBp)h8IA`^DLc>o7j~Xo=0V?qy-SZcWQi7>B1@={ zA+R*p0$-H~EUlLUf?v<`!xf_i{{*te)51nT`))cBQU-3kT~i?kSPOBGmmn~)5MYfB zT?I&OW^(o?OCBK&VBmaAJTP_O6M)30U_}`V>T(c`NL(>AVuN+!fG}$W>_im$O%pBX z-V-{yEuQ`a1R@3o$I_`s{(wR<`gnh`#22Ki>@9}~`Y1Tb11(%R{Fv|12=T{IZsd5V zC8>d!t6b{vdHA|b7ZHRt0m-s`Fa?Z8A$l=mu16PYi#}>W1V$Hig-dE`zi_h{#4_(} zd=OlLF?ba{N~;pI$_S?EWXj-@1`QA%k5^vLtr6*ZaRc0A$TeLfebeC!*V89 zO$3n#EDXaynlz;Aj56dqR*T>{ejTQ2&v_opmkSfa(|Yn(cv=q>(6}CI%1f^Cz)%|; z=BsWFoOnicD`8Z!-BKUj87M_VO6xyD>&(FPMoTPvvK}BBWj(Qv09=`c29PR|30mBO z)k+vp^MSw-fi@yI%}`lOj!F_90a$a|u!Xwy_=p1{lDDK%m7N6?Oi^9B)&h+(+$(cV znW4{v9yJ#j>K3VINTC-i1p@01jN~K#*-2>dp6)(e@z9IR2XJ{=-^I7Bq#L3f4wOMJ zt6)80sI{4x%Y;2pvMwhu8A_@a zEvqTHM9XrPAxO>OG$W?TI0Q6eXuT2UyF$C(cPfX&TMh!<4a-AY=>crQwQ+iiF}Db{ zoJcEjIn@G4jRj5jCd`y9$HN+yMjqM`vroLQy`h8)v1SA#ooriwgtf zH=%Qc55xNL!H@*+RY2No8fTAlk^UK0Dt92o~O+V6a z8(2@P6&Hk4aDLir2&&>P>8@@Zxl26?I*V<15bG@6)Zo8FRcN|CSu$MJ5ov#zG9Zk# z<1S`Fh96GRqwTtdEy;K&l-)5s+c67gCuWC3pe{E(wlvAd!#r`vUYZ$p7T~+7_yKXTOj`HCA{0}d}3-02a1GPtp8_DEH zBDb$rEe`%}pWU5YF?EaME};bKIXq{eU*?Kk&?6T?yG!!1mMC22?^mf98Zq=0mu`6# zF~_9;5Ub7`#h1=gj(zACvqPxuy)!TnP6&6?J$s$P`>1*6_v7yQg^O_=tMYrJ?VT54 zy+9~AawGc>LxI=&hJZnF?NA;|mF>ms>+$aQWH~Fkxz3r2JBynrM&!}qy@5vuUk+D1 z+7%w`0JuS^qj?!B{i)8ik#`dqzx)OknCcn86Sevzoquh5c`-|J&Ps)jY#xfB-G|mp z-Ey6qezdx8yQ5U7KQ5aEKoYpuUMnZLN5cM{?iZYp>khmT!SBej7=Cm4D*Sf$#(BM1 z&FiD+0XMfI<(JX@5SC@w(g&Wa!Tsi+O~rll!Q9eXT4R$a#6dR63p4p|Mm8-|dm{MzTJ%lz*u`&cR0Apt+@GE4{a1Qic1H5d zpn>Q*CzJsvb;L$~UWIyrLe_oNO&7kM9N7js<1tp$jcHyTxpk_UemZu8n=ZaV*@DKtdi73q`W(|4U$KC_I;+|Oqr0+%SJy706_s~c1PIOpT znQOLQaJW}0^t9|-K~IyUdZQhYzH1=CWNc4d7nKE@K!yta{X34uyXwu^PLb5 z(gXJ^5yYeKB_NB2szi$N1a&wPMIE5zlF`gv=Wm*Kq$ayRyze7<=dL6U+$Ezr)}mKpnEp zh%^&pOIo7E)wL|I3gu8I*36yi#gByV z{UDy)?vR`Z_l1vMGoWrH55v*%aMegXCgNqwQVPleqEe2jscTm`d2CvR9mafTvyl~ULrK%-+;=S^X#a&@MVaad(UFt}UsmXhP{}_kGUD5<30bh~+-V>MbdpYMo7>cNU zGXMt_(Sa}(5s8ngLTJzEAs$;b#N#v9Lh_bTLOd?jK~jE+3X*5eKm$qnMghsYMokBp z1k@c4?f(uw-UqJ_JV*j&POib)OABW7Njld72o0Un^b@(K(GQkk%Eh*W{zVYmfiQ?o zOROg@p~T9^G6Sn;*_Mqpuw{gUY=DJ>JoexqPb?gihb$cQ)HL8=t_{H0z(F*GWDZYB z=LK4}J#h)8KHMBmVM8tKx)3&X_C}#z8=&o)Z&&zj-^<_52g3O6d56D+os^b+B3fXh z_%A&KHc|RIR&B}k{&~>J>w&*g-ADOb_%2XbYT%ndVX47={2kc{ESAcS@i+Hu62JMj z6n?u8^0&~9y{4&w_xU^64^);KSp{jI${yx#?mN@)oBsx&S-Nv*&RTpEdvrJ~@_pF; zI8I{#*tE!SLF9{6+0`d$G`su7+AvxJg>`t26(M-WaVdj}k&Xy-WKak-9!r6msaQgH z^n^jd`Ph*tdK#iE8P0%6H~rBrlB~PG5dsrh8PZ(PaH|FZ`UJA2B|7kcD5zv6|B!-? zQg3|?I_hW2oNfJ{GF#!jBv+Tr7qT01Oc0&9ow zR$4oF0rEK?g9NWIX1c-;CIN?F1|~yWh6#s?k(MaXB)SYV0W?WV1!&T0bOYGwyxSW&w*+QzXR zMb87bp*-jUBDU-g)Zi!))+-?Cm`@VsCO0VM3bH9iPACwGt{8}f zV+A7d#x1vqw`g)mm95DkC)VW9cZ|uQ@6hCsqf?t4Bqy32Ly_CRj2;+uGP#YE$H`Qt z?%td4h?lhP7~tvhFYi>gb?Y46yx!;u1>DL0u(*>Qk^RC8U)w^+dGz-Z?tVJ(AdXb?*oc`PJot_uaWp#~v5LsPa*OxZRuW!uD*Z4*^Sq1fjn+F=WjZ zv18lBjdZ4)!LP0ZLfF=N}rjBOJ$*4B!|XWu35wTThiCPu8S14++5L&|Ft z6L$5Z)sWE&{wJ^5*u3I3+gv}xW}6bE*`|a*v(1%2v+Xmd+iY7aHru{i-fUY3HrqZ^ z(roG@RU2q`ZJ^!e`Wd#{lpyUkB?Q`St_0d`pE=!j+gh>R_TBP!+d8n__L-7)Q`ssv z&~7)-Zgc$%+igmacAF9c?KW2e?Y7UHZo6%**lzo7dAn^L*lzob-);b^{y3~%izdv@ zathUBDd?>LcIZ>UfgK5$6v^qC?49b${Om$Y_{tZqho$t8y8V+ig_{bOTshdJvK!Qu zcW2c=&AYYR6fEXV0K~?EW*`k94HRGv(Gt6?X0QP-2d%Po$rU#|>@54@HX1&#g%0qq zhU)oPBS2`SDYnf45T;76$>g))d`mbx=kXTxw94Ot0K%mCKW*1I5M`t$LHJ5}B7bvteU-h1AF zI{r^15&6hqB7ZeLdim2!V_8?l)72f7xtV~rmhBtlsX=O}( zxB==m%|qMwg^wlCbeO|WYpXJktvZ;XLDB#p6eiXSQQQPD|U;b!fwKA{R*#bcvj0=aH}_xZ*bp3g<& z5pkWv9XGA8Yd&q^Vx;+Sx*QOow_#6oB(!5-918AmED6&&aOJ&!cOr$JeeZAfsK=+m zb)cOM-YI^;Bg$9r|&$^UZ$7Tdm4^aBIOLB zb1%K6(wmzPr${uqtFo)+Rdh{P7%R07+pS}s%k0uERa{FJ&g}ZoWB+5<2LOS0#b67d zoMdQMOwbTEhrD5THhS-&lVCKgi};NHmByARI4U_M$WHLy2HB|v@PF{8d;Soldd+Rl zt4qZK=nBIhahP*Mz+3RX%qZL*DcrQ3Qw?;(ELGjfdq22^Zk886`y@&5u^LqK!6d2~ zK;&KF7P{kDpF{>Os!gBXW4%^yt>G_*t*I?f8B<%1X=>wurZzynHnkVatOM&8%M|VkzjzhucUKtAL){L9_ub%GL^)@h z1LV)PrF=6Aa>6sC?vB=Nx=VrAX-Pe;p3s(5pL(AnI)G%0kpp3vdO;`RC<+I-W#QT> ziE^z|a%uEfQiDJD(q3)$Lo)hg)pwuv6ZC8d_zCn$y721k3X4?7SS7?h>e;TVtP z{Xtlj;<|-45vK=F&M$sv9!ho>4_CqRl*@Xat%%1@HP6P!%PddMuJKhp)do+c6e)GU zR2%M;&bIMxrTvM=I5XH1h5RDToQC`=Mz%SyFEEyn4zMroxLE895P%cn@yKPE51-Ts z1#!?zr81QC%fEbw<1WTNhz6EOKMe};{4_lF{4_kVej0hm`f0px!+X%(o$~xNxbE)n zjrLswN0W8;w{q4jqG!I{)y>6P+>07tu-uE5SCYBxinlquU&`iA-oMDq%x-@|Q+oXWrq^lYhMk3@a_5m_=GFjf%AJxV|fl zB|5L$Reaub5o|p~?0SO%iISlw{}F;bA6v-5Xow0v}_IBmJD)u-S^VMenANko9iz#EM)wwjx)E%Xl~C z4J&fxDI;>_m?9Sk6uBTa;{IB=H|_3XW}=MMp*I_whuv~GdP~lM8|<|?r7agNbUtLPI5qiqg^5qtAAgi&uagNY2XN_D30g{b%zFhfPB0zCz5 zNYY1tkJLYkxo|kz39phRzIIHvT%A{Rx1UJS-Tu>vb+_j*S&7~$_pmf%;5`5;hDtcN zEV{HM3eV^eA(zbdVl(x(^t8s2Kli<2Jho0T9*e0~GP&OQD4lx!ak&ee#)%Ogk2ldS z&lvZZJX12w&MRWJd9&>D49&LFoMe~sX|~xq&}=K4VwY!Vw%KQBw&@A>G;oBT@rDOc zJnaW(p=~(_$M7I}UUj(7^kZDg@KKHC;sF?FZfdO63fPXQXP$%uQz%mirTWca0ZOA?6bQf z6!{RqjZiq{Jde*~68pZXT!&LdmN|}^a+d0fp_;t9b>5Sx2|+PI8;aFNkkL#gkrU zqfbD-6@ar(!Tj4$tD9o%q@-=@RH=_8;7<5$?)g4b@nBFwcP=%RIw>7)HvJGIx5 z;lg|tTG*5G2>zeQDW~>E3Na=YXNOrOw4*03dyLUCvqP3Mi*Iv;F3nTw<{>F zPr+7P7a^$8vo|G^wfWcyHP(=YBc6ztvn+OIlE)t6<%vbS@{mQmo;M#OnLh~NVP+-) zya<2Zk3-C1(*kD+PADup!Dk!1GEBTfmvMuox->Ns&L0w+*|UoEma5_A;_9L zav>Rg^&h)Q z$7Go}GTEs@elYk8Vyj`ppIxVo1>QPHh2G&u(8yiBlLkXZAqcjNLTtDqY+5VdKUA_? zLN;b_Q*AI*o$Rmhqbf0$M=0*11JkVy$=L>Tu3x|E=DIi;cL94vYF?oN@ay1PXqS%JDm#tPZ6zVI zv0)>)RRZ(T2)m|arCz;W0c(zBHrih2WVF4GxoCifdaRxWgizw=H*cC_dDC9!oAx^1 zgvrTGmk5~UZ=DhH@Ed_`rWhO>tw9IVk!{u!4{muN71?$qw9G(8HqZ`ed7MDl=rFyr zp^t&O%C>r=YIX-VNMG)2hNxKmZ=xFPjo>*B9tR8Inb6FMVnpKs>{cuZw1kg&{Qw%& zWa|Y>FvDv%b?J?1rO~1>y1~H2xka#1K)LcMZl1Keyo3r^u*cSw+buxtUS(vbNZDqk z59=190SG`f>2_Eh^D7vAN(Q1lnkl+aM*=_>a?>N&MsKvxl|)n6A!UKWy+QR_LXJa= zC5Cq(uFoZ?z61l}E;sDIpLC0n=|{VxOvXFw`?*E_ZZ${x~5S%y7} z0D=^EHcBw{Sa5GCbuGr!h8W}<6^R4Zm4=C7@&QKiLy#zFeH&kaQKTeB0A*HiN4wOa zB)%oWKIlk(5^L8Jd<=Jq;Wh!yY@h*&bglIPg->8Rv+nSc@aQO42SEqnX>kl@)H~*l zB9xHSp4`=Y2hpV-!)mP|><@yJ>$0yx^ z>%8#XCHpoFe-*}$+)Be8`i&d>CKHBSZ<_fPHz>h-GVZ=2c$v!g`b@ODhFfXi&0u9Y zsXC2bRoMdr_YUR|&9)b8D}h4@?qqBkyz#Cn$F99_m%7nykj?CUcO~&4r|>`FB6|&H z{q?Kfj)hTfPZ+GjWGJ4?t7CLpp7~OHa1TqMD@H2W6=pDnU1p1-*-({&hLCJNsa)Er zgC&gFk|R;Pij~0j)ry7DT&a*w9zl6h4<$;q3O(j?~sLEXZ=nlU&L z2WHbui5Y}Xa7k1KRLTJ~RJwGEQ0cgHKb*i}m8>j~85VDlIJ*K#p)w_tC~!Y(mJdvy zfNcD(<*yrZcj%ji5!7VB%PG8e{ss1fgll zr42I~h#3d$5t->u2UQC2QSse~wGaqJW_&@X@D!XzHYxg4>Hz3|^|VpwFB@fHxGHC3 zQ^pt_(9#c@L->U#b;NDo~cXY^V!H%RrtrZ9Pk9k6&wsCgXbW`a;|<4476DZ%Ir6M%gWikmS2+$}%&FW#O|S z#27iHgn&daiU$&tjPNS{#?vo>+_mSF_40_U!X#IxmxKwWl>`<#oit0HPwsjW!8}paS78>p`a$(Z^D{r&q;a6O8GA4vGu5x z#GL_SmgkzIXWa@CC`@fRmKj+T9~BvmN@zkNw~Sjl%|cC2pe0R=P>EJ#c;Su2LgaVW zmS->$&ts<>$e4yN`z+R74PPtv_DwGOPA{DiWG(Zmc_d~uG9!jzGW--tj?B0K3F)}9 zbTIgC3jl_ogA2I8m*C#_GzUYP(cdC|Cvj-ATgFRSWc39?>1eEBG_KdF+DnI4SzS>y zBY>?{dQ7mB*Ekk8OzAoAG=j$dW^*5b=Eq4%1hfA1UAEG*y8jrGjk*z7!jp}`i1|h| z>OuE{V(CE-#GUk#5?S}c1o?;XWkebAvyMqvJS=IUKjhb&O$aQZqu2$K^njkmkF_e? zrg_jK3_>f!Vf0B*-(fEaTbJmHaoCz@qAYa};#7$*iFD#V_8Z8FX%a9P$ zuVJxZc^E-_z*iswXpI&x5q5$6JO-DR1ZEZ`?rAmcBt}a#d6BFzaf=}#6-m)ApAze?q0`r!z^ugBb-z3Z(|uEQ}ctPm&qEtySP}E-4Nu&Ke58 zxuiGXu3)HSx;#Cr(uGnPOrqO;dP#BuD&8x`U?D$Ihft{*?hR9z9$5?(D>8zM zf1r!TpDiWA^fopwdHBk_awfnpI@Xp=jX0KyDoL(gYd!KrbWp#IyLA znKT24q*FbB&k6`u76K zx0J!mXLFaL21hMsZD&Hn%V-`P^7eGY%fMIjFVqE`3%(HIH_i!dOBSy zQK%lHM7479kgLH@70G5`<>_t%N-}D!B6$pGk2xoTQM|R%7Pivg17j8=0R#xs+ki#N zoRX|B_$0#;2a~hV9<0m*qe)(%h9P+>ZVL+W1!LOeP2Au!PVIt*X+DREYsU3gOVr|< z@dzDvceN>V{PZn;UJlPlppyeEOiGqOy&HdRNm`l^Qtu%@^({UUNYcV?EP)szK*{@l zAZVd-E#)AjDqysx|FT>o0>5;dpo>dvFg}k32PLUd*l?!qU0wrP)WSNLEJZp-v#p7> zdnSuEjIBFSo)qD8D-*<9&9MWZ^hb%Jno5d0Dkj6YC~EUGW)tv?b@nfc>bt-fP-fz! zQn5&&VUtzGFc5jRB(VzmsncaYf!okroU%3U6_1X=9Q004tr0e-m5A}<;s;vpsGa8s`W5rGh$6O=GN2ptB`%T+ zWLgnZ%laOLlq*dw)Yn5~22%KL0TJhBJlJ1PMI{fMg@Rn4IZ1943>b4*B(!cG@#u2Ju&k39b_UjIofG|%N7bW!B1-ie>%~n%2?`OzCQcrX`{`txbstG-X{e+6O>ji5+-eijcD^j|gN*$*ioFC4e|g z>?t}=8)`D29mo%2Fr_74N)Xv+GBL}D=@InpgOr4dDPqbqjfi;g0H+aqzg&_L5b$_e zK_CDtAUzpF0J*QE*)UYJQqWgq6$yBz#RxG{*>}qb4%NH|P)XO8$UX{c%zx2)6zomV z-+zjPAq@{EnTE>Sqm9M5=MsouE44{kf`ysq5{Q_*B?EqZlXD3~&Lt3;C`m~A*ONe` z;aCcS31ZmBMn9KOgyFT%B^2>;jhss;0%UY9p-5Ret8)oOLa}oRMf?COCJV{Agd)FO zLJ>q24kQ%$fy(}YIkzcA?*4!dDr|z`p1o;BbOLY(+tVar9gv4Qmtv$eilR>caRy-& zb@C7sM^V2$P9R17l!>IMpYlzOb16o!x*x!=JgXu68`$Ib>aUh!BJhiCnYmGOf}S{++hpHI{=?-q$vFtQw{~h^1ySR;LBh^uJK3(7 zxcPT3+etBUuo}uffDzBe{3i&`h(^FhJ(maN`*HXD!o^s$`Ag4!qCb!TuWjj;xxB-|S)j=Dsrx zzxi*}hL9lgTxt~f&{Lfcgz?++4u1%8{ z^obItU@7|BHQm)uMRQKy{fH`bXiui^HDA7F>Tz-Zj;zVLS<$~Hi~ zhd`E(k6{xP;_y^%sVNKi%}sqJ)#lprGmO44ZbXg1z6*boFt&D-h9|fCZ-18qhK#c* zbG+>W=t#Sj)Z68c75~4?PT}&<0B5j^W%A59HmZgfv#(;u$waYn{J;j%IQXL_Hf(Ss zsch&f*$q@leaxCs8j4XkfU>+I0*R(1@SMfVnir-(|tr_}E;#py?;G%pRfr2xB>h zt;F0r)%`|TS5c?w2t=z#OeqPqdNKAjn%&0!zy&tbn(beK&}07QDL>=|3iG4ub1#Vw zS;g?~I>0`6h>RE1u6)xNV`r4?yd&R(54C*gu5R8o4e}w(;v}BBrZ{tAY@#2Y? zV2$H!dq>5_*<_55mF-ED7#&_{P4={hbJzRxoRt%StJ33o$SPmc(>2m>Sl+PtxF%ISF9i-CJd`F5e-|!v`k|d=Yk=q~l zHV1oQUn&Dov)hC6T7RZ|W2AI*$?j)8#HO1CM3OaRblCBtRCRsFi{~*;v?c_5;$~y3 zMk_lb(1|k8rbGJUC4|eyL%a0HOS)Fqc*L<#(!{Y6W9D!b!~gIq#tYCQQjxx{ zG(8_iU8_cEqd;dZ%76(1X#p7ziYl=EjVXpC+mY#r5)4t7dhSm#<9U5bC!QrGy5DB=YyI^nisew3S_@liIUOsh3gsWqRBo+`SG z|3MiTkqM|6(=ArSlNdz|M5)-(*=7%}4#(7|5X%c`FIk6@xQDC*#D=8yk;G?nbh;hp z|FMXQ8F&n$8fTL}I+vSb{TO?o<}U2DrlBIc-ACgzJvj1z(U*ck`aD8dG8((EKO&Of z%rtON9wCN{6x4-GNzfh=jVu@h0g(g(=FzCbF%X^0rW#0PbQs3?bcK=za+T4jD#DTk zu#G2i8SMxgasAER67>y(s#RZ!*skO`L+OIsG^LwNb4W5G$>td3)1%u;ss>;F~ zp(Pld;aa&_SSS>IHUWi}g>(+QD3VThb}(p@h$Uf)l8})IJabjw#T|c(YE$cDsLK+E zSy+IWIr@Q$oCAKHr6uOmWw|8Ck7ennsKGo*AarK2jP$A&&B5>#T709aYs&+5gM!*? zJrp#SP@yR%Ks-qP1fLzOEXE ze0fbH<3Na`h0sA1R4@PvNw++(y_OG+jOPQ3>)3R`9Ac#UiAc*NJ7YYKtx1$eqSXRV zXDQ4tgoK?`@>3YvNV|}}A(5ApC3A$_wQ>l;7~dazC`!#denJIbs|c@CgkmHDQR1X8 zN@Jc%w{oate9efLhbx0H2PBCyLrO*x?zpiTp)c#VNBnlC3P`;U};3G0U1Zl`C=YS5Eb3)S}k#Nk6cLCj`+I!F1}6D zKz6=S;|OHxbe(BT0?wwC#{ScIbFfYls7j(*tsr#5DB->eaije#oOywtMS7FG=HZIU z=e_O^P|(Ad4E?$K(drpFZczeJtsI4H@Pn8P46A5KHNISD?by$Z++xKcH5 zP<`8-WFj;8sw!NKpVyYEXJhtc*%dAE!JGEYd^YyPm-0?Y?#yuJ#tWZL-N>aMCsbjp zg0c-O2piYU?>;~5=%ZZJOa~mh!JSsvHQ!y*A8(1a*8=n^2TdCKit84i zvO}`8ekC8<=8Qo-V1(|t7=6DrsUqfhEbHpN=Z)e^=gFYRL%*1fH5P;S&HxKIAzVrK z>~*@kQ(@-tiCjqTg1NRHb&BzQ6s=}Y)C;#!|OI6;<9*gYTq&l3zhQX`zi_M+F zP5YKA5aGV?TVEK7AeZjlb$ir}o3eNwCmZLHD*M!d@L)%xxFV9Ap2^;+uFTIaw1lsG z;d(stkh=YoHHDiBms~m6q_P{-m3L>=K+U_g+Z2|BZMx+E|Lpq+OBqGZ+MYEr=G07N zve#ulg}ohvqw3yosaI6?y5(OUioErO0hL$Jd?E3|`~mFi=&n8xhWmKzZLI{|lF2W( zWb(`9;Wbe zMl+M-C!`o|+~$D6NH*9DlMUxv!r3{Gx2UI8{-*3ImHor~%PKo(>u2khU4apU^mtlf zFU--8a4!t9@#Z$p!W!LVDpPmw&3D90I{H|I9lcZ8j$Y^Jp8gF6A(AYvjF279?E27S z|6>-u|sR7@?y^b2%}b?r1NvS-;?}tex$Ufs>be)Dnx~XJiA9E5j23mMY1BL^CeQ zIzOiA9Q1RhRK7L2PUHxZEp`xGCaW`#slrW=9$Oc*P>4K!(@>2_3V$W>|H%~3WC8^1 z*cjgQ>8DkpGkjydU)>0aRkvv#Xn$Y$SQ4avD2mn#6;_FbD;Gxyr-e{zhdH(grP(b=7Sl4nfC4&upH-Lgd8Fz8{Y}Arz6_ z{Uw*mS84@)3k?+sM|p65{)ZRg1&Z4(RZxSlT$7=edy&W+?A{1sD#uSt8j;ZX{;A*! zbTWn542^)8MH^PrjcW}Zmp)qAyj)V`^C#}F{Mp*Pxd?_4bc1cT`D zg=)pMNK)exeeJiqm3MJ}94pPFVg>@4no%}IlBL&Pnx4nL3M0OH9>Q$43Ma(}Y%h+z z?gSs%F+U$eCikUBBTH}n95uDhfsWpD$J5juj#br!09W4ocPCQdy7&HOk9r)P>8a|j zaDTl0ut}8Vrkgj|2{RAcrg6Ld`Iu8nlIC_xs9kEXbf%z=-|(cwnPCehpHhFolwS2!$2;e>byL5jraxC6NL*!LhY#@jDbjBKd_0R@CcSGX~E zs8VR0(Z2AesuK!ApMW!#Dclu)@v2u8{y=*SMI^4%*0m}zNR`?>rhokxu!e9k4HqWll@r>t9+ESEVAg0$%n7}@Jq$V9fE!vA zLlOlc7OEhMdy~ zX(t$W!}3G?j^C;yC~yJM-5bp|$N53218 zpHAVA`<2F)D9jncINz3m$rQ9@TGX3r@TPnIPzPOmbt#M}!Xh}e@=Ck_`$>&&L3ir5KpXWE)+KK7r1Je`P~0ZIl@qW7=;0 z^p@C_gPq|kZ~vw$#Ip7IZE7%)zdFBN9sj3S6wb2O;ds1_Pi=|D+)Hz$FO6!~_JTfv z=D;mDR<$#Px72iVtNR59_pJYejwa>Ud$|?x%d1H6y@!IQctQQ)j1_-I40bL&II$hoxf=)%o`!xL zUV|tSqUV8ksFqdccx0ev#nho{3apkW>{|+?l(t#-M6AuaYy(ByzVOj&2GotWUwfNF zAB>o+ed=H}sV#JURG!;eDfA3x(i*qcpMx0tHfjPId*n*gSl+8k$#ZDT$G3 zjVU?Fndk#Llx9Wv0e}_+A?WUfv~~xI0E_O(DExjdv?V^j_a|EA#Ll$pJ7%g?-{D*=9`LB9?#9!3Yfm0uJC~{?WQ{Hv$SijflvZelI`P+v+pc!)iT zhwWOBiyXRF-T}hMeOJ30W(%kXK}Z(3WBPQM$rdJNvpO$S%p!pr;Sz7;?`GkqP_O z*oAlhPuRx+n~`?%Yi*nuJgDCL6WrS$cK2i1#v8SE#XzfBF|fZ8lqkj{bQ>*IZ9M7| zRHc{rQutt0?i=RlT&w4_@*4tkTKXiNe{FgNkMzN@3`eq<#PGQIDqf#J#c#v*B6NZ` z`|K?u;8e7=O61sDB{D~C77_5cXsr@?%2*|GOsfP3v`RRUAXW(+_=rEXDgoCSWzT`| z?O$$*XP*H|2Ch30e(#o_>d7sJ^^h!j!aoPv1N6#nC3Ek2aBBY3NF=Bbj{g%ZRL_aHh7)i1T)ED%Iszal9~<0Q>ANq2e_?HxJ2;_* zTB5m*DCJjB3qgjJXrVWi{la^Hf_u4_OC>hg0Fe$RG3_7;qWP6)o!u zBkl9yS!ThgH{KW-__VGeXeCs}G@Umo$74GwC)drSoLuLm91b`s2XH4q4~w6bOH~B|_8n37`~~U98b7VlcbI`^H?mZ>p@a>e(Pmuk{8m-mZ~k@y4v-N3$fq?)8j2zWN4?KwtjX% zExTf+BhyTHEum+g$l+Me-(ftl;M%(v-VqbSco;LuSg{74^49#JIIdX3qQx^`3zZ;k zU>^6?Dl?Cp^5${-)Dc2_YLqC}Gi;i7L@$fVJ9i~TU>7408H)(b$rUhBe0e7)mJIYA zLk9W|$$&>o8Z+oUt+Z_KL6GfK*^S}#pAO@9)2HDxHS3kf=}IUsGn2Qq1+U$DMflaL zcXDAfT&yyQ$M(VAJj@NnaD`*_>5Rhdk-|;eDYUo@bt!}vEa=A+mTF)T>2~ME&pt^5 z8t&)C`Iw2r8p*%_zyn(xSOA;;=t+e?MDLAQ;aDz3_V-5nt^vhG%oJHGDK1hAmk4Nb zb#t*6_oBgjQF{-#Un_he-&P^t)t!6NLcVZz;e9DARI5)`civTvd&;agmV3(H8y;4; z7Yp8t*?Z-2lpEppA39I80n8FunmKqbpkS!Qrv?-Z<`7C0j7?Fse(`_;bsoRwHph5n zMzJ&q^8;emVY`zzUn<97WvoWuHVuATy$I~o%V~e@5fk$~sK}IeHBXoH_9I)0yLW`E!eSi@YeSTdYWy6U&SGj^RaphrB39rxwXr z2?d!*4&xjK82~WijXJ2~Wa!C%1l`aT3WvN9?5~<5uZ7DbNrA=z2;vh5Am*;<0NkS& z&Y;7vG<$>i%u04PJnUGpvFr-?$gr&MVdsf25tVd>0n=W;2!(q1y8K+LyoQQgC}-IR^G6_4|&S? zKIE9b4;;|~{b{|j!m#9!WLq6E>lUOU)*JK`^;-*$#kEJwS z>$p&-cZ9$FPct&%{#H0CQg6S)-;V8zf?H$Ia-Th6R>N%Omhuqaq;X`ikUe5IFf2WSuSHN%L zu6`JP6Sw#I8NAM1AnwVf@SC`+8FSU`-FhLfzntdv?KmV)6Qq{a=t>#pIwG{`vIIkz^S2 zMTlzm(J=IW{pHxCsD=}Rs_)f0%DA2>U++(qO1;9pB3~D;Dd(~XLC&EVxVqOz_6&G2kI{a{YmDA&>1V=@YV*X>q1?ZM_Ko7Flwc z?)iz5Pd*Z2Kll=mf8r5!^4iC?RQYWjx%#p9Oyye_=HAzb4gVZF zdF{b`wfESi`<(6;h<@~g_slk@b>WHcCsbO4snnb2sWe8BU3o$!Mohg9Jk2(w3+u`% z^4-YEYj^Dm^y0|XyS^pb&23m8J$Y^J3BQsJpJ`qA$|h6GlhE>!v$ zX8sdrqw+x2k6@Eg>MX4Ll5I|6b5YaXx^Ix~mLyuDBUc}MJ*m=|t-*!VV>4B{Mia+M zm1zC3Mk_E#NTfg3aAoD$r}^5}B!u0L2=^_%ihU8@2uFe?EJW%Ko>{UZk@6g?T|elz#i=iv}*C+O7ZC-_w?vKcsdZ zn6>69qnr<1B+8l5rz{=-R?ucoVn{taScfP0_J{Fu=cQjia$>e}w60H~5ak#^u=C{f z0~e`S;7-imS%@13D#$AaPXKk`lk>)d3aCwI&5!~jd36>HF@C%GysxjGvH9n-b!?Ef zE5b93an@NssPOSS{>=cKoeKrv=+fg~$HIf=-=7F|Oa?VZt`6dhrXq!n!53*k8w3>< zI3qo}2pk(dD{YI>yWF!44Dqm zyG!q0fA-4R-QPHSwqcI?w26LUr2rndbWWS%(dK<1Lfnb=B~eg@qohMH#=^(nFJ z&~xW3q<o37-feSFqa73VANvOY zrLp+e^9#E#vd+i`_*%J@9Bz(I%7elUd3ZNrP&fa+I4;Njl8QIo`QJoZzOWm z93QLy*gvAU_VWi-{^az*BL?H-EyfAo@^#;7GaiP66DPlLq}O-ipcv1IBUQ9c9J!(4 z8=+p$lp&QJtP|)bSgM#D2dTM>5wEOxo)(0(KHa{tu&0gP<|}=LI~-8BZu6s9YG9o% z!<3b7=kfUlre#iE3lE5O2KDbWUY-6Asy782R58zx$*KYdT`$sTA;J)2Te|zgj z`rEs{M{iaCtMs?O{c~=&XkA5rd)F5F+xvIX-~RRyxcc4J+v#ubdVv1+{_XU)zx@;X z+gnd!wW8bAPJes!Y{dG+v++@NmI-WczOaYIA+1D%`MPujvHevj-17 zl$7+{;C<}7wCFHBYEXA}07V0u0nEeEgb5<~#HD?zaJN?rGeM&zEhZ!NWDa@j&8ccKpS4f-)MvYKj6$lPkWIk74oJH>>QdQmA$ zJq;8p?VZLISGd7A1M5Hbe@`qB#*+ZB*$|8bvdCI(lm^yn1JmPcT5UnukO~M8 z$bxqF>s`}fig5%aQloE;9aLjc-{hhdi7T_m5gcM*_Wa(E!XAE zurwCPVIWl+t{I`^4KhEF9%-t<<*;}8Hh^<{8GdJ6cI@Rb;$B>a7{td)1!219d4NUD z6p)E+u09;;n`il>@RG6UA$%s5m7pz<9mLd6s#!s9sH9dY6QN_oYKGS)7M5qV>=H?@ z`MgBf2LyG%vwga9Ki$3$$_E{q{q|f>-2_#^r3b>0{y>7;pkaOq=_$ZqJb8(4=GXh* zoJd0Yau+z`-$hsszM?6x?=_*o?tPj9ONAZ@)1$!Cl7C-`0QGG6cy4puw zu!k246dU_X%{2c_px8ed$vg1#+1Wx$%f4>wiL#X>-7zNY@t~9tFy?ZTA~7qaX-zSQyeT4tWUweRR5L@Kz)^>H5YOYI@?0VD@djYV0l3%h=<6%AInB#J)D>bcMQkKJ6x%Eh@u z>g0i0{nk0%s%MS`MwrQJIH!}LKK0F-4Q_k2b?xvrtY zP4+KV4XOQub!d{|w zz$FZ{g-b4A5+tCEY!DWq=+lBlAc!C=5;6fS!sQ?=lFNWaxEzEZI}^j`%y#-_$4hCcpkDu-#@cWxX2*(ZTvlmw8B4$MWlwB0 z4I}VB%G1Fc-6m{Hp^=y#{>6OL3xG!0qHDW&_}g9=BO;vbz>Y}Mx4nVF)3yT#)_Vih zr`lihhP&@IZ@3{#Y$+$yp@SaL;AvY*-$9RPdZVx%ZCwyZ)4qCy*V)|b8?nhaA*l!q6|}V)-|Q@akRvfCBkKT@yuD*67)Vs zL6*v%b#>1^Un{cSEUC=D<1eX91H=_0dXeP1Hxx})I0@uwOk-1KL${0M!s*%8@WpUX z=b%sa~Fsv`@=Nhny#W zQrGcsT;LTx=nOh2Wb;waqK09+w7Qty8QJ|!V|X@QEX%`?Gjd&&H+p3~7K+2EgVPAG z4?J=7yx~n;Ig~SRwp~K66g^>`vu~^Js&D&=yb9~~`AE-Yw^hIL<5|z%Zl3+AcKpk| z_77&OPrN&Oi?yv^dCxTEl|Qx%yV#U$HEy6GdR4o*hgv)zY0m77pk&X|$8(;gkAgjy z+be(kQ*T|GD-Rh=!!gi0W(xL4`-6pe@gZm2ZNfRE~bqgGc8Z z*W#WNEYq=NJ~?n6?wOta`g;+3Pn4%qEB+jP%c;^Q?khVl((_VA2TG6Tqv*SES8T(Y z1bg&y_Ok9z^z!O@>Gq3Fx6j%9l49pzkD3P^4AnKw ze|y1(T?&g$H8{!s_o2EC|NGl@8l1Q#qIRwaQ7CBpQrZ9hY-&RTlv^2y31Asfg&)uV z@lf54|Ml$!JKE;*LjWlcPHD~z)(*vs_as(tO=+ELo~naNrEJ#9;G?EO3)%;(^t(~M zOXb=$f9yrYx1oI=oYMFIv-dW@ab4G$AYT9fp@9ZplLXi#Kr)JH%kioZXG702DW}F$ zvnksek4h;!wenP|2AZ15R%N}rGcL{!lZ7Zq1{qpvN-Y|sD7h(7G$=t*Q$s1V1t~3t zrkfHZLlh-7C5VP-iUebr0VOa2DVpBzoOADc_r2E*f=MQ|oN>9_V&lDj@7{CI&v$SeDf2neBS!<%V9QYqR!lxz>Bn2EUG?9-{IELL`M@s{ApkKvDe_2u^6Nwb zDzDH1ghO1`LRa}c(LH&L+y54j~*8sX&|RA_>h z@{LgrJhz{Ax+q=Kj#b?0f?Ngg0i3lGV_u*Ib?E|l_q7??AJBdtNer}M7 ziwo+AgwP&-S)(4!G(ZNja-jF*ZHF@hZ!S@wmFOnyhBfn3hhjMS@*nSs?f&0Do6#!s z1Ftf=v<&>gf$=3SkE{SEEuQXxtrq(of&IpMjI4 z^75R>$;9N>>1|l!frIq6Fs<8kVPImp;?K>AgQ=-S)vlrU`sTJ~*X+6mv1*0aMPf9%VI>(DVUV6g z|4$vxD5pW>t&`{kj-mPBFOV0a+b0KxQcA8sy4ygyKbY>AeSL|nBe)h+_&l{k=#GL?hkin`nYOqXhU#HJ(aniRqUAhB2O%T+f&Ues;7Owz;7;ps z8%wafAM5R(5twlp{5~B%^C2y;1@wo4`&qkRJw|21-X`cLg3asX0ellmojpeT|C7<1RmmGuh6jm+jxMI zJ+O+BJurqLdsuFekJN!I#*L?5TOtd@%RcpKc@zjMgTb9;5sDjkZ_B$&Daxcn0bQoV z-!--HY(&BhD+Bf}T#km}Hm)d3_gRXu&(TjXHU*2Y&>9`c7!@B!0G~|%& zuWXKDSoYUYYF{R=&&GK;y-1tWKyN)7JM~4i0MLrIjZ}BYLNPV|wJ{qC@xPGR4~6Mp z?S~GpMC+wKybBLbx6r#TN9|)Y!?ZQfGZ`v|>|3{4Xm5}X4fGU4U%~ec2XVe7YrDE$~T%#eLc?i1!b73n{j9zrqcA*J&|54>&QW#VjreR)1Tn+@B(0RNN#PB1&}X0 zxBLV+0gKb%+J>XCzEfYsGp>z5>uXjo&V?^2n`V}WIV(J*zibird0$(&E|+m53|BtU_wlg=!Nv6}prS~&7Mn={%a90dw;2VQ{5 zD>7l^#H&P#Tiuak_FqBK?jv3H*HGNGD^K*n+T&p(`2n}UWB5laDQ3p~6>U`AAY?6H zRLzapL9W=aA|2{d?MrxqQ;x%RIF_<}zB`BS%<{|BUTE+m_|l5Y#s(?_v8)dHQb1h_ zZgr~rAgQ=DWZoLmw@Oh?*t{0j*HFKM$2j#n5bh?#&wuVuBjJNx7b7o*s$J0ikHZ!s z6?I?qg;Y8ERO{uHVlV*^U&xeWWG;}9Aq~wq>W+?$$){T1A>a^cry?i81`$K`62m%<;Oepql_UZWD1X$Bci#I!A9*UFl7xiDF@xd+{Ndsuj*JP2fY(LPz zNc3>mNc*-2Qz8l+@!KsqS-_7SiTk3JJZK?!5bqOefT(`Vgu8zj zKNTjtw@p&Dc;m?&jr}q$4EYqe=Y!VS*x|14#9s~Ht#LaeGvz~IzK^%cJrKp$L1HGFG182%`9#WDdH z7+bs(Rt*0QLFD?rUa&B|2FZW+tGM#7;>y2@EB`94{HwU~|B!Lz ztGYkGiYosqs!Wl^UqzK0U` zsv8l)t8Uorn<5dqiK;3vQUn)62Q$?SUT<_oK#Y#|W?a$XVeE;( zDDmCbFN8vqmlA`$R}$s5n6tB8g8IPe#t0aTvt7b$2xaq4YP$sAH1Bo^3}9-}WDet` zP7MevViMSkayWwqbLeSDn@2$Y{OO#(6O<68eKLpl`I_HRsuclo{HMEiO8rfMx?nST!`Hnh|*(EhEOj5 zJN*`R0_Ltzif8|?w~$pB?7WiL*^cg1uv}t?(Bz8qE#u#pqzr6K?S|92=|!+YD$8{# zQOlgp-Sd9O(Vwfjxkq%}TnIHuQ7C`ZF6kN~JYcZ%bZ#VGd59BNFl+}iwG6!%i6k-+ z_<94reiFZv@){To6vk!tM4$S}a=e2EdNM~p!4F3MQYG%De~q^*(j;Kmc777J3BQ&} zpdFT^kIHL}v>lrmnhYL$Qs@8F&}o#t7-bJSQOO`+r)UV6=0VP=vIWI&A58gw3)@`F zu!X}fjcL=E*%YF_&^5uYeZ1lkbhJ=eYCOy7(D>S4q;W;(ujxue_0@msbGQ&Qexh49 zKJ|-5jj!-r2RkQocpXS6WWn9CxKGJeQ4IW2Vlsz6V~Vo7(z!wu$EsjWcIuvuQXQ!_ zGY%ratlEk20fD@kv6{h2rMJSkRrzGJaXuMsl21mP;giw&-=z)|#l6(^q0A?vAv{j^ z^T}xF8HRtOy%;{xL<^tjfQ8TE4(dIDv*+;1CrM_id@|ZNpNuxiC!@{q$!Iv_knaC; z>OE2BlhJU5AO2nClhMZcWVA^>8EuA6Mx*W-)U5aV8ZE%t$9U(vc$9-NL{a zW9+<{@wyv3j4{T}#`wA$JB%@SE4Z1ltj@dISQBHc4%%R2Y+fT{tgbcK0%+bT#+cQJ zV~jzz7G%G!H>!y-=Bc>G*qp0rj1_hMbfYzv7>y3mF}bQw<%UwKZ(njBv>XnQFQcAw z27R3;is609?K#^f~!+`ow9R_?()f&;HF&zNMKvp#n>T56~x$|HNmpOfMPUN1xS$^jR5&A}-Zl zrO(L|^f_L~Kuc3+>9hZ1`ka|XbD+}T-DnF`Du0JQi(Anas5JZteOCV~eO8`?f-cow zq|eD8)93gRDC^SHJM`HF5DsF_bE)CyLpVi&;iF0J}S$mp3 zC#&>1{sZ*7EKR*apZ)LACypM)Cysr`XZa$17C!<-OTz=`lwGPmK%bR+(J#AHE7RxX zKKdMg0<{gLsZsjue~mt8eniwBJWikGzoyUPDeMy|4PT(o>fPwCU8?k>zjmp%l|CmQ zq0jN1=(Sy%dXhf-|B^mu4xsOLY4BzGEWb;i#W&HAv^0F0KC6F6pOy0{=PlJXuEgi$ z{q#Bh?N#`kdYC@@EA%_}jTx!HL@}AVU6n_#w=_FI9hBI1$ZUuD_}8 zZl#kU5=lN6;HS6YeB0~k9A3oWxM4DI4T>_ZH|kS=CTIb6Qm(tP&%9k=4ThG8*WcLh zPh#v=%k?;R;CPHZc3qAghr*jjMr}x)Lz8CiCk<8m;%DqX{k8?mj<$`b=-At_L%eIK zSl}oc8ipUc<-mSX#0Ou&0k>$ed*InNzIEyfoKKuF@L%&q>YF3&&o-pS62|~H5-C=0 zAAS|b>z)H3wM*1@(%w%UC+%a@Yto+m32=v9qW+The(Ee~AKOodp3IH|Fxn;RBWdra z4wCjU>K$p%zC?$fl&D{%z5fz`o_%Z^pqxGX3>|t>`Y|1Pvj1mv=*h8f0h-yfkJ6zh zr79hIvVVdOJvlZ6LT z%wE8$w}qY06KR0(hn|xeD}+C|Kf(ZbQ0;FI!ayF4 zX{h$_|4mtvRPAR=cv-mG&qmizH%u^fi`(<4HsSU>vQcU3I(*#h(;mG0*Aypgjvl*3 z+v~%tX-ilNeMR{l?6nF1>_0>UytN!dZ&8f5e~? ziIBskA3j27j)9DL#U*vnz*BjX&2Q_~e0jLc=aUs@m$T}`*XvGo4tyA6${Z}C^*<h}KO_-5xSiIj0odIWNlhy~bM8T2&snve_|(dxAsW)9=6pW-brI1ZK8y$a^P;yhk|XJ<1{PF%Ef8 zaL9Z56$*J@-VW24*}@_3ozGFzcN9P*wXrI7dK%`lmnEgbUR$sz9% z4tbAq$a{=K-V+@1K6F7(tpGYGn{;bEf+>S2QYMc5fvh&KN2Jd!<3Wq-YEh=X)ydKe z#sW`z40k~S7~H1$x??szSt3Rqrmwr9Pu+qQ(Ps!3kV|EWK&RvDZzg{g#Z;V_KC47( z{*4J)E!X3O_2hWyOl&;}QA)Tx$Hx@_k9!WsFe1^t7>Jefoy+w(;NFav+*s(rC}GbV$tO zqy~A~5erR_GR3Et`JcGfir|Um*j%dkvp61?$GKB_5R}6~Q|bPrG5V|XVjdaYy3XRU zsvrBGtgeoP=2A0vb|XC@>Fb%lR9XG;-!roM7zVu+0dN`zz45c=IB2d{nJE8$Bs3Mr zi19LPvHBqY@AGSnaaS`kXlH(CnT_Y}OJbirS+6edV@J%(%Z`mOQ@LY;YGB(BXH!(91r(pA&^KB|#Kh=RpC<>QjU`)?L=~c{J^iZ|zXp2HW z+l8H#MA`DAnt}0@Bgm}s(EI~mL+?0ZjYg-K1sv|BK{6})e?Z*pJWaTl{y=vhczhX6 zNi(h^0E%&W;^S@D?7As7A7@dpeh)Rx9(gU14)yk1t2c-Hp{@E5S{Tlwjv<^TX)edY zB-NA--IMbF6_40v;Sm(MX=y2jpSuOEW$)~sd^SoVVcX-sM+F_WO`<`~Vm9>EBL}9I z$Yh}@MEIpw%+#3y()JFyUW9(BZD3ZW9zwkO|@hgxQToUq%6&MwDu(4AvAj~9jUB6?2w zq#PAnz;kHaEn~k7so6Srp(dFwQQI5mxp-Q-rIdO)(vF9oi%xcokG1lE*6oSl4H$VC zBd0f1uec)x9}(S8gg`V*M(Cae*^z<;XQnYZOtywV2Xm%1v=j*tZ(`yl6rXfXcYV#SW0ZPMsX47mu?yU zQ3A87NA^UpD8?F;W2lndk+lj@#^ITnyj9?GTcUnFQu`7{oRh7bT*pl-fx+9L74%J= zI?vcs_jId5UYy(WCDKbdE*z4|?~wX8{{kljIlqv$bXT$B( zyXBH~;52$+fxtCP9rPn*8R{*ULT_{y6aSa}rIC=sKUh&*^{lAkkAvaKj_GIJ6@ygS zR4T9siv#kv+h~xCUpM$EdfwnJ8j;mk+-%Jt97TywY!jxs0XhGU>VvG7w74-?G+c=o zS0d^PCre|-g_yc<;QNdDVY2a5Jx#t8hVJQVdJQ~*YvGIlc>Y~DEnn=RnbjUq2ljDB&O?WIg5IhWdnLY4l`nf@M( z2ESTLg)22{S9h&zS8R9dO#XKN&E@X%`Eu-H>LT8IJc8ZhXi@>hVEpj)HbkVl8~(6n zUBnydhuGu}!~pXtubW8EeCElJ`&{g1A0CRQVI_OM@r@of=9{wB%wV<{#&}P4j@*wf zeHMgPk9KPqwF1tZaEe&>w~b+9v-;rAQ+Q#!{>9Fv)y%62DzxbD{5gu%^n~!@sqtis zmlPLMyIX5r4QFKh@}4dt1z^xvf~blbjkUx=IESzYjmvxZ<0kqq9-i3!>(z`&NMe;k zVO()Y;*M}A_t4TK)CmP6!yPF-hmp|mZ(In|%#TMP1#?dli0|s7#WRw>TH}B^xihygz5A0mb)uS znjZRNT}*{xqVt&pFG;yE7Qs6{y|A^ev++&Yi>blv%sU~}6@_8m@)?EvjA^@s8E=Ca z3sDwNq|G_&XEtK@N^<7jWm|qhvw^kH474d*O3kHSO}I=%OL#u-nc?sz60OLVd^Pg| zu=p%#$Jad9&dIOI}#P3rWm~=gfwQp=(_@Gqru8s?VUsIIlL z{!xf-p_yO=Z!Z-E16?Duo6e4}`FVD07?zwkV6A>LwA!+A?7g(8nZ9!OFHkM#$l3F0 zs94=Cs69L$>ZFHR+-g^z1;7alW z$_(o}b11CFUjyqmk)GdgxQ8s1N;eg^0S$9!~z&n9RN% ze89FwmZ(@b8>exPx7`(gDKi>0H#SB`y`)fsSfI`9I~JK(;c884sj7Vhh6%uN?laOzWyDLIpe{uEA1nL;I+i}f(7LWx<- ztL``}No<)AZIJ5Q6)ShoIgcCRY?p0c+91?!gLo4bVG4ZR0K?!W8#b&kjxSA za;?iW+yLK7SC$}YXYm|liyBB6_>)^52YBBn}3|W0*iJ;hka6x%? zy$~I5D?YTc|F5w3$nq&(S1D@6JG3#1bxI?i(9`o3{IBOL_*HBmPUcJQ#(}YL+X>e; z)ICA0(%<>Q3f6DJ<;q-aEli{*Z#2!t+&Z!rH(4=Fw25?Om8`lClg)3}u%nnT*-e)3 zSM%$y@r^i<#(a162Bx8mUp^u*? zw0{S>cRbq;UOd@}ZBlf_9BUsUeyafe@b{G0_Es}{?UyohI|^cN5d&#tZ@0pi9dGlZ zUs&xP$GH#-~k^m3M8bktn28rC7I%5ge10vK-RJ3bK;ckkGR!Z)CemYyd;f1I>OE9k3O?Ahv zZr8X>c09C{1wkB9jQfan8J-S}d+>=K8Fzc%Ub_erswG34Y7Lbxg@iZ2-bH}CN^9%(v~{7OjBjlx3*#{g`;r6LzC`xN zKUn>2kQL|$ziOgPcEyPP0ktWy2G~*xlK^EkmOR&2c!@lKGD`>k+7Jnq_7+DmAD`Z zWkQYv3D>&xRKO526&1X!FYkYS33*lPT$3}FDE^k_R#E{k#|Tb>W;KL3kP2Qllrtkd z2wV%O!F38KR+2_*UcX< zGVQMWYbRcWEt?T8h9CM*7}?T5Ow~b_4Z`Wjbv~LBb>6X%s3erIh!1p6C8-BC{+LC& z#}FOY zvfz{LIRLh}>6U91h@hch5k-it1!N0)9YU-V)z-ua&QEo~950LcT)h6yvj}FrO|H7NwFynE|U`pW# zY!=3v+nAOyD+=7vxf7h*F!l3%T0uAsBLS|-os{sf11@od9yz$8FJmPzhnCs??`0(J ztKPM%+iUJTxdfqhVl3eeZy;JER0O;`f=%Y|N1o^rZ^A$ZyYSv(*z2!(Bm2nAsi>jp zYca)`tO^I|siLVjqQDF&WMYcvk93KntO7H?Tp_}*QWwk;l)8ZZlE4bvYmr`rgxS?} z;(}VPs6vNxU9mom*~;CBg^3n))@chInHQqu;`sF4^JzgQ0QQ@*!#0`OMv=E-xN;$@ zG?^Q>7raN)0p=4!tIYoU(AJezrr8IG7PF5SUnBJCN-4NnA-)pwS4dKF#2ffg_yO@R zKVlMHX1hMpRh?I%8AsyCNKAEZ<8?b{00r|F5ZM%oUMf|`AV|U;>@j+ zwQ<1OM0$M9dueu%gq8*uUrY_e2?ovMYTVN$u7+aA_dDQUuhA+P>*Ha^N^+=}6LxeE zJNe^}wE`@j%A&~(7}b#DsUGC-h25cliCNu9G`@A2(E~)H)dCRuJ&SQqZL@{ZjTyb( zHh@HXa7LcQx6DYv&zXF6C{LQ*vGnsAq+kSNsmnsIw+-8kA{V3?JR^{qH1WX9C$Opz z&dyGtE^^o=o)1df_|q|AucoMg(o7e^{$H}zu06&Il7{9NPk+DjRPnH`8_@;e6X{}nqa4)2TZRB}leg%Yq%{9bHklhBJt zZ;FO?lr6xFF3yd$HRnsu;|gbASV(3=P1alb@+ib;{VC zcaBDfZHfhhzLRCr5zZADza@{g@@PbR1Tyrflt2-N=4)28f^U3r*m`8I&7mq~9JS$< zOr%FTg_8{Ltr$kd0(o!i%sLw{Zg8C#FAhN2n-^&|KxdUz7vOqQ&Ith$8NCmryDLVS zW&-KJqcW31z&V;fd3zU^=FYD6xCpzty^f}bHOaWI@urZ15oD^pa(^WxPP->H$|)c7kKs9`9UQNm2+B z_E2*#96+t&JgG!P75@&ZX2o!u zl-qbS(_VS-1QzAFhCaKPLTVpjL~Q`|C;~=E^qG>%O#B$ zlJ@25Bji9S(>JuAipSK%j$py4w9Ijr1qV5J0^3ATsmC|~?Jw~=U;c6ag+R>;DJ9s< zQ=P$@zPU4t=z(mGn5-whd?}z)NwaI*FagO~XxxQR5D%a}tfA{ILfX-|=*hH3%#t#CpK7>j zyj~dDQJJ7_*JG`maupZMxq|w4o2q!>wuv(Jtwbfc*JjsEXr|l*F6R{$VT4ZR4+u!S0wsp#|4fND3g5e0-2@DTtC#kVUsm19%^gWydFJeT_S{701H1hq* z-FkYc({l{B_}B2QHV;~AAU?Jqm z_66f>TB7G#MWPkN-kw92w#S6n@E=sj$Xg2S=CFt#X=#wdQtzA2dgb5s6HqVvWKIlP z;@Dye8@lOt;A9n0GQ;=cYjF~VjDu5lysJ@+Jt)F`rpy-02N9 zD5jy}@X9I;ioh{GP=qpwj>lRV6p4g4H9bki4?Gc=*Vh7?D)~WQ7pplNbm?WiI zk|1yLWF$_jFi~#er0d#N**fQC>n2+|V71RbB#B+vet=@{<`6-++1&5<#n< zTA{#`6#yhbT^SDuLf9)thO(2-I3!%Q5d~n;3*ZBmwJllV^=cqhMTU1;Ui>f5V$r_7< z0Np!P5dut_gyh_S7q&Kv&(NeYK$NIA)2-cR);ls<_(sllB!OKbB=c+V2e2Iv(N#36 zP(mUGkOMnnkbH4z2d;`BCT0tRSyO``hAu*@A{_uY%PPqxk&d}_66oO2Bjwr2mqO@K z!a+u*uA5B_h7URXNo$QPgEOHE zjh#}?(PY=3yQQr7klGs4(Kv(`fQDjBeWg!95Nx8a;@^cuu#D|D?g*;iUMZ1KUjAt? zGDoJK=yB@xrsB7A9s1BFPDzqi4gCwd0UsJ$@}Ys1PLT*@y|##S%7Uuj8SMr+J24Up zL?K9Sq4~ueb1@^SezLF^P(6k1Qpz@B9gHn`ZW)Af`jz|H` zx_tf^LSJxHnDZ1*Qn9{SSv7PXOSwWaSl1 z3xX~U(@<3}FwK$2k!Agzo_a&LGrT7isd*>7q?D$Hj^f=pTb>21A;$s*8`#Jx!Mw(b z-V;?-=IPL);10&&vnmE)_^Jy=31XJjd|%2;XNv_>|6uT@LkK}RLJrNU8Q|-BP=lMr#&FCP#SGPo3gTolD4ebe zKAI;<5S14Caa!4v*E_phpokMeMw{zNx1pQ3ligG2)O)F~nY*=af;GCW3}YD}hW7 zqXZoa9X*h5IK$>N=;DWclHo(6k|D zH|uT>EO~O7Y{g*HX$aLt+MEpN7Yx^!xoFgiLk!cc(R=l0Ez7bly7AKF*iv2 zRFwFcr4k30e9-A+UsRa{MPljw03M=kv704O5FtRLLmcy~`u5_Cqxej!kCZxx$8zDQ3x$TBwt2Si^02lp|=X;n) zpHid9yQ6T~iDV$rZtg}s(lxSdD&E9bMV|}82J0*Y0|LExf=-|crf^_cf+^g5qpzB| zol&v?euMq@C3m;>cQ#0rdAJ{aCwMoUvXER2ssM^H~R(x!W@oh=@i!rfjo?p9w>wwux)L!9Guvb`JTi^!t>Ji$- zHQC|)m9zPmEVU^NCYE3hBR3$x9Zo7Y$Q?%b+zWSIItL7rSZt>x)a;Puk2{k_;BH;0 zxoPO0E8Tf)u~>hYeU&i{%#zWVcsWgNFl}!L*bK)6i>+vq9@`qG{VPFp>tSb1}*H}yx92@^VCm=o*r;0K(kf0<5jFvYt=00AV40Kn4$?&B^0^whQtCWs# z>dcfDZ@hWPEn7s&pO^@V?Z$VyBO;aty2Gu=OE}p95~=B|H!x^2KT4H6F*2>>-7q>8 zzf&p`S!+?SypzH#gaX?t06iLYs#!(LG>PSU=BGS2>TY0;tURpJKfxWg!61P9!p zl^M8uQ8HIZA*#-4}YREgcS_>Xx$s~9v+!Yes(JAhe ziXd_`l}Mi>+-5SL!v@gshA+<@a78gG$)KGi_2nMPIKNbFw_J!#ksOTxd~R@r*r7KR zjPS20N;s-caGD&cbyh>)M(@y(sHAowIGPxA06|@E9SZ#lwJ0)2k8GpnV|f4{1@K+N zMgwluKgA1p57Uv5_<|s=>p^2fjOb8i9m;l-Qld(4ZoQMaoNJszE{+rw67d2G*Zb9+ zj6Rw2Ug&QxrbN1j-tClbHik5g-dz`rQyA3pV)9WmCHK0}4kj1ZS%o0DFj-bsi8if< zOCHnpKvX+80$&+aFeK|5w{kafutIFq-b!Q`ZAJ=kO^kYEwe!wPRoCaF7Ra=kLpdR; z13?9mRgynk#5qYRYgWqbYJ?Tt#;IT0hfa}3AYzI%I!-Nn(Qs*S;h*B=D{?iHBQR2#erZ zZ!QcM${D-S8?(4(Jd{a@sqn3}MwYIQbk~}Jy%H=sy8}m<)388QL2{{ybgTRL!nhS6?GeyVgXH71$r(WVicE2fb*{@l)MDS3RANDi{CP9!4$FE;n8 zvGH2hlgl`MFM&qoZ(h}|21V*BAc{?wP(UichH@x2kfyy2J$uvXMg+Iu1jz+&`9O70 zH@mBf%-v{4!5Nnecg6O%&*X!ysFxE_m?iEg;J9!MHg8a9CE)%$4Mu|QzoRz@8(u&H z$WR=zffx;kvAkBp8d(LWxNHvU=k2Tz4P80BhzuELT5~kjlsJtH7j<%o)A#|vsZOtx zLtIXd4jWb{4KuQACU1gWs=|}v1IldBZ;+u-xL@AM z5^0O{Nh9CN@Df=dkWseTqpd*BRJJIP7iHU|CPT#9n^w~31dHnKVNu?}l$$P^x1-{w zERxtB;(B}yA+FLyW{Qsiuw;gtY*uvLQN#&Ee>PBtlmsDr&s}3l6&MSLV$a{wDH1Fk zkg&0E(0|1+>X^`QQ0YI3_kjAUS*hPIgcm8i5*c835P)~V?`zV8 zC6Enn7(aKHn08`u{QS*D0=KokFg9gRc6>X}`7@8-q$SSvGb!lH?X=!lq0GCfpwThb z!>%qsfLa!IRqw{>Y%7Lsvj7GOnm_>zXKu*JX_^IcM}{Kupc5dLYqFyr5fN{))7zyL zh0VB-1JRj4nVFa*r)Z7#Y7%YH1x9TZqHZIu856?<^ew-O^f|e984Mcv2N4kfzc8|r z>#f);-ruWLq%hOxZdYTXy!9oBZy;cGt#Qe_&mZfL2V={&2@f_RsWZRHKJGNNPC8&O{U2JcY@&TT{ zkYBlf?tUrX86Gf^%B~$+y*2J0p!pps z8^LuRBj9f%2EJDc)J)Wvbrcj8;o6OtJ3Qmms@7FSgge;wDh?uAd?7WV+Oj-5_c)_cyIY`abEQ8y_9baQ<$m81v zL8GB*2S4!w3XO3ngVMC@hA{Y1ZU|vB-T#t?jUJdXY)8p$$q(D+b`21VxS9vz={+7C zh$8m(+-P+06PnnNX&H>$6F}l-IB!C*IO?Um!VWYS72%_C&0eX6OF-!BXfc-MFkVr>3`)|c3Cgk+JO%aBRS!+OX18d`JtRCN#IN{t^!zBH8>uRNQ%k>2Iu@z zF*v>*SWayNY0^OG!|5B=AoL-iU~6GIJ9(ptbmrCx&`||lY-v?N7x~S7&{t zPPs#vm~#ZPJPoHMxCFw=!OjTHN^_fQi1KohTa2*JqtU2I9BzU#u2N&oE*_1CHQWpx zczBiCe!{_HDSl9(9EV*iBqREC-c21UTv{1SVM(`!ZQI>xYY2vq&ZjW`F)SP|lV&Q#Hv6HaXsicLYfW^%_K& z_il*@XK}cOzxAAw)bYPvDw2x2ZAMohQxG!}0MNXHRFjfevQ)G)1C~nEl2bQKZ6Od| zkp;-?#BnTs6w2Uvq~L)wW*C-^E?^|C;)2vgiEuq2ko0R&+QHu((G%nq>+VyQ!FmRl z5zrBDV_uY{ix9=DMCp-0MD=3V^+TN5%p1V@SKbtR4>Nf*JXZlUQ%$)AY(KP=qgyH*v+%qHCon~znnbreo~RH>E#HwYI?AY# zRYB#X@Z7ZtDVHsakQ$;COrUy1X=74}64h{!h)@I*pwrnrBcUFDd(zLFw_7w(d}yVJ2pS?dCAQ=R@&Lv&qr{1J0Dw)mk`063)CcTM{llQ{)uFiz#YYUDFtb zvx!zUr(1CD@>LF{lt_Y)Ai)@%tFAcLD+F;;KkgAT{hc3lI-p$f3w3Bwn}LOF2p)LD z?g+Vy(SWb$1rzW91w;KkI@YrZ6JA%IsnhsI7Ag>JkXjM%ehh>HgqlsNwMDfzUJI_0 z7k5bEg*!UO5;LE1>JOez0~NWFXBw6ROWs`Upb9Z6$fr+s=-&ct%5Nza9zpI7Won9* z#DzG%98J@HQ{RqosN8P2HfFSeK;nTV-Y_%|Uqa6wETkfOpo~JieNF^V8J))Z)FaMH zhss#b=eJ<9Sfh49s2fz1=QKbic{`W!NE$%t)${qwxnj7HUd69PuVNRXH~&Z~5jjZD zDBMgsj+`l&xh{l>#l?d&7_8Inf7((N!ZMp(-E`K;d#GeVR8Lj?&>|@zpnI|5%sG5a zB_HpM!q)rB2#m#|C^Svd^_T*HxR#SaI*^D?u)`i*6$co@A*QMWvB;F#*u|S+#sBcN z`gC=uE_*>0Jbp_@J~QpUgRx`#rBqm7Ym;-;Fs9Cvn4?#RRYH4zM0QZD>W4x zQ7(y#wY@6VRqZ~PP zH^8y2R4xSVSXB)nRiP184_{>=WD&U)7yXtz6%_7>T2yM<^#^ev;&P9AMP)Q^uTrrS z-fAigqv%IRs#32bCmAGyiB@;iF_Df3V&6jIn}*=HCN7xM<2loN-EH@=(G@d!b7UyU zBL1Rt70F_t8cRSYDlr?{6S|p>Bv-gv=FlW%<)*ko|8+5jAgz#FQOZxTf?x?(72QVEJcnMb99 zu#c!%a8~Q_WwcuBEKkc}>QG0AZW@((!Qi&fCk>ijS9!@kTPBjB%;}^yr%#XYjLMo_ zL^TJ+N8KO;oMs;gYJ_jTJNN9nb#1v>Mf(WaNVb>adkhBDzj)l%^)2)Z>?mRAG# zK^|@wQ8Rw=dYid!4hswn-~i>&yRu|nWzs0y5VXK0_T3zN{S7P zEwVDXt65NSYL|6Oke0=tAM*&3PLoT%id4V{Tm{*3y46*-z(F%yRDQa zRR)i%J5>hH-DBx;R`p1$ctuTgpnIp<58>B=T=wL6SiZ8SSf9Ib1^ar0%Y96FRnsC& ziKEY{2Zd`Apdb}JI5Le+BfL)t(P`c%)a2lp`2#Gwx>++wLckY&zp5Hj%1YRc8}kyo zp+JuMi78luufBMb(*P7}uULMsO?3f|5x)=;^NHS|)r^@U2L(ijDW2Jv+_{vFCvs_o z?na^qjqPjPT}a>|{1iIQm}B~K#$^+G!>2X~9(VY2B0ck&C(+c<(05|jJP`|R6N?Y6 z^rUazM=2S#yAiQQW?IDBQ!?fcgyI&N4fgm{GiKR2C?W4|($wD*85R`=S}RC}=!X6{sZIEIMUywr4o6)GrlTs@Sb? z9iGydwxB*erRmYp3zFUKIf{`O;Tv;-8uc3Ic}iC!#eEWn_t%erkP0#QL1kl8#wbqh z9usg*k-n)O&1(R?;)*JX+Mc64n#t98qi}UY-RyNz8J|kH)X@eL>HbdhdC&&*yrvc6 zrd#A!^J}d@p1Se0InN-|cAzhH)p!-Y8BCb+98oxeiS(3zEOeW2K_;irmVCeN%b{+*KDUL|}DPh1#n!U=6t4ef8z0H$x{b z2{hD!BiGnddpeI&6(bi=%u-_lN8|J&RbO(d(-A!}nK(GyOtJ^OKJk$e{wap3k2|)Y zNC?Mgp`6`c3O(sDOFZ2PcFD@BQdzfXSz|}7D!|l|8SrJ-tC=4z;!a*FyWV);v==mU zg_@xhz141}FJb24u)#uX29Qp_O7@*??Uo;v)vkwKyY1n^cLKB97@i#2YL_lu{nzA(fkoAIedM^+n_dAVi`&o1n%8 zsSB}y9HI|rLjnv6SM50k3Eva>*^@NwZ^!oXw1}Sxs8u3n`Js(a<1)&e6&Og$b z{vKHZWMvxdXBI-LgSj{eE72+C+m&O~(E;cEh;O%mB3zEe%Ts+3rTL<`Qp_S@H@~s< zR7Y`~g-E)_S^x{yBQ$x`C1cxJ%%rr`C8N=BHDdzWYdAM&m&@Cs5?mupdIg49cuql~izto9W^kl9v=svQ5@>~Y zxxdBlga$5gg+Tppt`Ooj!O^s9a4_r5;X?Vj+ga6?e`=pWL3;GCQvrTW+AVoHiJijf z{hg+dhbV-gP6WZ~5mMgB(#UxZO?(}f%_^&e3WkL=!SLBQo#5r|6X$p-0*L<}=U+0M z7FDR+S?UEt``sL*Ag-RWBtl?pJ{E!~O;gR#-Xol@Aeu(4(D{A` zPOu|Iu!d6i@)uRwMJKco6Tn1>R)hXK*am4OE5opKcJWlEpRUnvql1SBHWC%q&6q13*7i^%T`gJrsXi9l*vuOEK&A7X$ ziD16bg1CIRJNOHC1-KwuCJ~S$m)PtV2$5mb z+>Spaoj9;k{k|{Q!9w}gmC=>rTWht3$jVIK4DBQLuc978gF8B@-H<7KpqU6?W(%4z zG$}V%Q4qg{W}$J?zoHS0HQSV>K99k2FOSw0u4%G=Fen@@iGlxmoEj$)Cr5W;!5Ztn6TxFV_{zj9UA0a z$6!)g);ZL*VV1)mV1~0wIW+R#I1D&tNf@xe2UwfTGrv-xDS&k<@2FETl+orw3^50g zU5Ah&zfgTm)AT|?ABxu~#bqp4FLB64iY7gDpDz??Wd6KT<)ChOvuMcTlmRKzB}#p3 z;WCaQZ=`qT)`{BjsWO*Cs7#bp*^t`KthD03Q&nKn}uFxxmw!$dXDHwyd zWD;{W&5XFGKA~VJm>Myx$G#*m2DGWGBLbKMy}K9%Yx8IH7NX)Vu3eT<8;6d~Uwh_+ zHC~R1>&;w=d=mD&AP6*NNnNLJ=d-ibLE)>$AtIAoSiy*|*WTB7+n+M>#TOTg^79n~ z&Buf69Xa6Mg_mg3Bj6sJ6uf#KKBCBip}4sDu_!K;&y(`UKsNdOQVD(sY>f`ezl0U4 z+#eMc%@p0abSh3U7*JHjV7BDvUm4m)!5H2nRl3E0P@7>3gc~(deBxL70>!unOr?HQ zSG?HV)|gPLK@Ogy+>#K0c5xcIdB7%4Y~i-Ol9dNKmQalw4iiDZ(Pt74Yf@k75f@<> zwG;vh{Xs6I|5cDnj+)?O6lGy0{?BKtsRHoE<|DwRLUl(Ix&A*lAMn7-UC<6Qc{4Er z07|@pOg-=F<0E=armSFfR^6KvYVlG0Al@iwnt<3YB7j`q48i6W{1 zei2Vwy-#$M!_WM#z4mYl3KYU=uj{Kk+_5hiE)0IcUOyUrE{=-n^0~OZp%|`52G15y zIU7#l8v09yTk+owecP;|;Z^-(iEztvx7dZj-4;e@wX}@B6n$sCZO?qr)?%Y4KW+$h z6>qi+v@87CC|-T!E5*deU;f*Oy}p{kYwle}uUXfNMoi%0G=eGBH9u4%>!Cg|oJ3ET`y+UrXxTz+#kF54T*v4dfRL$J8%NbkqF7p?x6 z!uR)E8>7$X#;p&R(#?A!)PLY;%-%2(+Ge3ROo%q}A`|Vv86p}`s8H(8Ke@a0!B6f8 z+v|6?9t*rS56qiJoAvSTpJfI@_WDZlL&wH7n7s~$QE!U% zz_4Eb-^Z;tZhX<2{CDqmp0wVG&qP2>jJ%fuhkq4L$7tY>f}`=D^Q(_$y<^`n8r>7Y zbU}4z1#8xG@yaSXkUJRo`qF375c1)GSPIl|enQ?!hp+I3wSg zPWHZzCHXM@({Fs3B#xhaH*{b2kDlnqLZ6Qd)9-Et`Co}M)1x18|FG@XC0K*abo44o z--bSj{F$Wzkw>{nZ0M)mlRL6uv}dw2Gx>0i7NYN56q<#`zkVciG6_O4y(iMW<=3b% zg_Om?FirPcP!=b?%*tZ$`4rc9&8sZhp)Bn6#qe`+N)wYRoQn5NO`)YUD@jlRsj3e` z0ebcAi9j)epP-w*L@Ka6m2^}fkMwV-;R-25 z=*SPhY8TMAA6KDxChv~WZ^Edh#g`u~rQgVx{v*_gINdVdmg^$bg^*1#xG}KWFCbJ;Uh7X3Z!vt*~(qZuYBMF~K z7sI{fUG2j!rNS*6KWP`ReR241Ebq~OZ5JTFgQp|mmPh^{yRa__^5zM+@=6#wbNoyMpBO$qF=Bke_ou(tll1r56#ac`#(f5&9_};XhT!_( z-?ibodSP*64u67SHSYSsB6`+aHa+X3JYBwb2|mZ4SW18YQ71l!FLb%jsPq?%U+-K_ zcO6)P&*Gci#z@Zh7!roj*iV1|uyqS5LFgtNatlbH%qo5aNA$r} z0TCa}Ub3ht4f+Zi8mu>|qvS5@p?(brWP1)20RS0|;>x-fJ2Uv_7Cy1}6raQ23*i&F zH+)uKBZG-MNtHm489#e6itBKUtrkmNFFzWW>tieJOyxZIB2)cK@I_|)+u)1L7S#|;^8zt%cTrHD<|o*T4}}S_!at`90Q+ZW_G~TXZp|5XYnuE@manL%u)bu z`tM*CSc}RVxkO7#^*djy-t!;rb+ED6Fvck0jT;3748)&{7mv3J`UA6oP1A8=7Glcd zz2n3%kN)Pl$os#4)MDPmcQUG*qhL=q6~%BL#FWkQ?nlWigR2gPVFFye@lvjsIREQx zh&J>A;(b76Q?R1~DS&}tAW*m&n=4y>zT41;*u-OPek}*s1Cs+Uln`Idx@19T5qiq? z_TOuLzPzHkIrRBgZY_t8wpQ1CesVbU`FC%cOg{S6aqF4P+4wuw>;JMG-u`(KfeCD) zJ08|=-J%UzF$^&(r&)|jsXjj!CgXeH~| zvBV_{$FY(2jPy?a+dIKFlP9c?|M>32E%sUf*SDAQZS@F%)x(P!nZew?g=HR%&V~Uk z{k9cKy%+eMOgPHu$Gd-G4}`|MNtapdPK+sMCdU>f-zWf}P%!!Sa|#LK$)hj+JbZG)EuA%TWCOI=_=_h)YS z={Meq-2o5inKYcZzlQGVyOIFbB*_GBI~N6R8y{=6zc!Z|N0Ura2h-W!$(NTfnft~w zX9|On@eCQm6Co%%hDDACc`^wPlI-SMpzF7n5;}%%+mV27V~jo<28{e}rh0`R07vzB zD~8)b&PLq4k*i{S zYSur6?STunD`vHzil4^CZ*J#*7Z7ftsYRx$964Y}Y0H-arVU*mLVU(9pe{yIRt!7C zHlDMglBeKm2Ed;O`=wbwo{|2z1m0u{!v-hf@I96s?1=2%UmH+!T8mOB|&J zrsO!mUBx}+G~ENe!*05Squ%%raL!MJzQ1N;^rycsc&Hrf8{fHHyR&_0%P_nOTSvpl z$zgJ_a0dSc+JV8jKgtGYHVij?Y6*6xaU^1Ml)bW-ev3j8BcT%^Xby=)0Sa&^%86%@ z?z4LD{F8IZ2S51?hCC2InWO{ZDH>IdT}i<5F(=g{2#uV`k>%^@ty+rXs{i@HK~g~2 zbq+;t6NE$>irEx&Fc*m9SWY`M!2ZE%LH#Kk{6m)zP$D^bmCjzR+yo`$?)4c}Z% zOavPsy&QM9t>NK5cDh`&5sdOd?KBya{u%ISPM* zINm}WVsn)I345Z5)<+O`YH)$Em6(-K#6|yRPEvnoMk;Ysp#-9~;3`EGN?>(-a#W$j zQH2sm71R?rsGy#}Y*gZyg8B=G6iOUY(Dx7%l{lhM;)p_t;eUxE3MGyxlsKZGM@2lO ze@iS>;&?)d;|Y2U#6kKVVxSU76L#)PLjPGv`#IB27}HjNzs6~+C-Ah@6O3uAzZlcj z_bhbUdQ>!R{agRE^%!W{`X0};N!QaRe`nt9#=O`7|JFZmJqDV$zQ;3fGGrFEKRffbjd`oTU*o*h6L{Y03C6tDUyOO{dlouxJt~^F z{;hxBdJHsgeUCeDM8y&CdL_5msB@cQ{3xn#gvKw?S>9f{1bfHETAE4O0O%TS_52^yX^9 zv~UZcmbbRES{_T_XwK6K|NR@#tsnuz83ppZ%~80OLLoxbSfeucfFm~TBSN!|;K7p*$`aWQf(~Ec+iiij! zw2P{{bfWjeG}##fqev+s&2}h!=l^-q+Dci6=Wh~lgSPHC7Fs7Xr?c>`U+!T2J{m>R z^PLbcMG3)9U@uq+!U#ytmD3WTzxao5#kRJ+jMb_~0CXQ-%#*raXh#AzO28uftG`R;8@Ytzt%hs2zGi_1S1~f&-HN2a7@a2zYR~-KdZSZN zCrV?67YBH2cWWG>C#kKu&I|*a?umfd?9z^eU|3JB;3cd_ z;OyN%DZOrvzdh0MqE)p%`szu`!E+{hGymNX*>M^W@Nq{10!rutsf9k+VhIR7&ah!1 zjfYl8g8X9)uj< zcQbx02Ok6u{{2yQ@JB+IC~ZzlvdD-~P(lOI2?WYi&_bdUSe$o5cm50f`K_?5jm}N7 zz?Nh1qRJHey3-u{E81Qdk{KW*FO6 z)$e#rd2Eo8*B8UIX&+C;o-qAX%yWTHb!KX|tHWXDx??KMlqn{=6G3eCHlmN31MXS) z+o=9h)q~S&I7L*tXLA_PFRxU)UWc>T-Z{0+R%4NQG8LTlbXJB|&v_=_gZ?U>XMWa# z&*6;SYH8){Mm~f45NVMwg{fQSQdY#g6w#N4f4sQqH5C8gTBx=EJZekik)ecqDb&7) zqmdh5BZKT2BhSED1OE*8szXKpRjd|T0P5{MibAqlOb?X}M2<=!z2W4qJq2A2nQg4yQ2I=WdcZ>Wanmj1yjg(GwE*z$LjBH(wGn* zqMfJ)f$C!rxtQ=?FG3WUtE5LHf3B||Mr5RM4GUhfjY3zX`~(;Bdw6}oIp7q_z7?wGdu?f<7N~)I`q=`#6AX@VOKesc{HecJ zMAlx5QpPhmek9icN}^L<$gw#Llz3S<0CPMS1&JJ{y0uPAhD2C7hQ&M0(kr)eF%n%(^#Jjp8_|AEFaLoth8bsBN(My4Fq$j%C^@)FyM&=>TTE`1Hs^J3;bPk1cP_`5DezIz^8f!+_Uhv83=|}!$BKtO3X6wEHHo?ow5{aECzyc*3+@rw0h1n*<$;j z=|?bGg)e`q081qs@Us4FB;WdR|vI@b_BSUd7f@yS(5eyt)Ms753o`JIl{u%I9 z2XOeWGX8Mrp(rG)#q=O5UJHVuCm0CE5f2B!5Yzl4AQ<8+9=iiR)SESeQOrr8heQ>i zhsNTF1v8Lvj00%sCp=dNz6dju`1AdWnGNjXC#Qj3oSPl&;=9>i7YOTNHA(R^ashPG zX4FA0m?K?>Ip;jCzV0FyOhKyYk$vHZDda-%;vWOKs3|baqUQs-xKP7IE<8>_E-tWX zj$FWG4swxcdy&gE8EHl?fpMOTIxo@4h2G>vE;1Dciw<%b_^C!Nbafu&g2%C5TL6+! z$c0EV4{{Moc#sR`;2;+aXQA&?!8|{601h*9p{Hu(Lem9@F>>(}Q6m>3gde#;xctb) z|GEu`_yW1$P9JjFy&rWj6ifti!Bvf16el!CF7!@~Txd-H2*`yWsgMh=Hv>zJTzJ74 zyK3a(FqI#=f*P{@U7zowV}ppXmwMUaaoAf-~3W?*Nj;Bf%SApjY|}0Wn26Z!)=H92Mj>rjyct+2-BG-`y}hTWr(HhP zzo7HboI~G>4V?E8EG11?O3~S8o3emmD0`>-sHemQ;MDCobn!c$n>oiyxs;mhcp0?N-U4an*9leozX+<9*Mg`ZbMa=-)9$~LKt+hye|z$1 z#tN$o<;#oQn+_I?o3`~>)YlrnF5p?V6=^pf)owmYUEoLQo9I!_P4plC#Ya)@8Uk7*JfIrb-nT4j}1`ATTG$Rpe(n; z6U30b&Wdr5p(!HWq!@Sh#|b^H$*-UOW_d@7rbTc4!XP$Voc<;=gN3s>G|Nq)H2I|r zWCP73XhTt+ifT|?Lo8@Oe>DI)M*LN5DX+X6lm6{sN};}HO02@MD{*1qB3dtC11WpW z@)v50!f#V+CydJy#*AyBr4KbrVCJSbU^S;Yz{nk?b)_e=>7^v;Eavhb{)E)ifAI#; zY%u#|$e9zb6;Hks&-gmZ7HQql1-91ZiYKig1jj#rh|I=q7BK^42HX(PM0jooi2B9T zUns8ytE87S8XGI6c0#t)fs)8kycuhdUMwohn1$Tg-lNwKZ1YVhKx6%?8CgFt4YhUC zyrIUgs7`A}F*Z1zP~AimI!a65OO7WC5&kN{S^70GFB3&G1(lARjYAjtiGs&bM>6Aa zq}g~tqCn50bB+3~=q4-)b=`I}mLop;U1%9`e^Lg8X}N#h2OX#8q%}S?vnWy!^S}~P zc*UE+HqG@vxQR}#XSro=GC98fDMJ$1Z4;Ys|IM(^;W8vX9p$qpg{$SEoe z_qi$z3!>&ks)SBn0n-W3va#0#B(WibL;99r08X`V4Kp6ibAmOAu{EKiRe5D#e!c+n z^L3aeSxZJ9^iZY1dCf($U`4-xWg#o%b&TPVc>@1zcc9#F9C$FL><(+DUi^MX%CTak z8J(q+DPiG=L_A5>$awRO1insx9jb?PB}@m~ux=PE(|~2P0mB^sHP7+um?LXfv8t2b zg!x$u?KQ7n{4^_PQley;S}@U=m?lT`8VKk9c31g<)WY0Y1032$Yv3%nV@GM`@>jKz zr#h*5wYy+oLI+7XoUDY6xQNcn10N_3qT}JHEhYG$xt(BP?S?&T0V0IhP*0BOqo)9~ z48(_Wtgr~4nHZLFsW7ZE5h7SD>Hi;l?*klVmFA7+?e26solZhR2Z%O*U3Qs)&zj9$ zYn@%3`gZQPb7$RBzbR_my~Xa%y_K1oxnI?kwcoejQuk_A8K0#tLu0@A>nb=l?n9InR+n zDa*N0sfiDDOsNRj(U!rhfz-%49xluU`=tt)bBG(8M1(8QZ}N)nm921jbbxdLS_kF z6X7|+VZ9LI{;9GHUluGSzAS_n{Bph52_}O<0hbLmD0D;NeA8nxa&iP&v^XDvvw}II zVCx^C9{(5ABPHuwP%hVs3x+PBUZ{=MOcZT1U0E33g=PaXPLS~~)cUxwI@xv;1tZ%k z03vWI0IKYw=ne#F3qahz3Js3aQATSc2P^{`3_Iakjk~pUrf)X>uPCgrc=tL>M@w;e}HOKJ*KE|Oy{uC9|@Wo7%az*Gzp@ii49SE!vGtmK&k7AMH!;0 z=Q~>#baZhl&}R^5ygC?^*%Gg6nGsN2S{;zfx&#xRTYm?Jg&6);&~%CCPHRT0*kuhA zR8AMs)ap`@0tT5;0d*zmN|Ai~M?*urh1`Z&L)1)KYANdIYdlOS`LkPJg+B-9mM zAKkfMoih)42M6Q42N=R>JcXTOhP7fCl#Zz#G>Ga3U4}CmG#4cLrp`mO)N8|Lk!B5| z*eptqHda9Q8=+JHr$a$UDOKpc69bGs8#@)%g-X7x0brHrKr2qW$pA%{cSXe(Kr@Lh zgfaUE7_)0R5K$s_E`Sirm$+F<6F>(NMEZy9g%FF=5(zL7;dXL}Q9Ad``uxu8p#~B2 z#AOW5SC+?znirsQ-~SR-dK0aWYB^Ep#qZIeEsqBrSa=|I1t!3#;*5c2%%ZVAlD%-? zuG5Yn`FA+be=vphRW9FdsLQQ7ha{-lGQjO@+1@yq!Y(ntwq-6Y{d)~g_GB)-_jiab z&iuC8i`pN#hevr4`y+F0Yv9W@e-y4_g>g3CqkY8eB#6<5qp-&Ei5!jM5h*N3l#k@EN&QU32Vpoaj*TA zSWhU6^h}pym8$C;hC32!;AuV22MKi9=v&obtig|gm#!xmJJNnEfer(xV^C>kH5Z!e zsy6%xy$&GiIeQ_ioMy#{7~D_?6#5JWCPHZZs&o977*vfS{(u-4LS?Z%2DImk-UAnJ z2tR#wK8i;+IASF9%#%F#L}gsHxR?CFWJ~ZU*htmx_wWXb^O;M9bMe#XT=`)+SHBp} z)i2~+K9O@prjQE>?UEc_4UQRmXaaYepMk3ALs7 zd5>M#QICI!q~0*P)>@Bsc+JAL1a;&f7RIdbVg+hq1)+rriy3bs>ddw>4aG zwUUbb5vUU65f?oZvVwMR0|x{gucl|_YHp}Q%>cjgq&aZmx5Ypk)KTO-aB=fP7gJan zasv{J*z*K4&=KG!o&|vBpiHXfm^Dz7z3}v`&)|0S6Oc_bZ1Vi&#YtP70QQaYk(0;D%84(h0fDH9{VDL3uvNIMsXba@1IFJGt zfn$eWUClVeD$RKTcY?0jMH@U1ncVw4=J-SwGI-hH8nb1BbI6=Q91lO&IEZaSmdHHq z@nk3Vx3ISFZ%=ZQl8*Fo%&i7-pP3YWX96ARxmYV5#0ArQRXQKh@|@gr7RDDifraMS zNj|X%$;NqU22zb~95VtK`Gf@n3h(uCLrb75A5hG%R#mfQ^ws_(Vv&KcH$ym*p{uf}qv7R)$v%pxCB5gP@Ob2?8sE7^Y(T^|&V; z?H`X3Wdg7TLhzt)fV37nf^X6mv!R}`j<&;T1j;*BGqBG^7<_)9$4?&M24Kqha46O-Vtd8QuMy=ptQ=O?jw?@Y#Tz5R?%CZ;UQ6nQTWtZUM>HwOQ z>H+Q&95~D)k4r3{ASp}d6DWv5KEd10mNoGukjOQEA9`WBkvR?lPFhaJO;YnF8-fhU zG+_7*2^a=^2LHO+&&~#-8VwSC(z8XXK*$vh?%-h|+pu9i!E2)WBdms&SPhJR9JggtP}x3_ zD@vh^5X0F?7{5C-7#CT7XMzzBax|Oajd;S8C|5T0p)G-Ji9sXPL%#xbb8(4LfF(?; zzIqU%0#PH=6@>g47orm+5R#RFrnxo#com;gh)6*phs#AsjhT%Ksy2=xo|Yizx7Cy+ zjo_(FgoeV8(Fp;_p&&sh#0$JLTcdiDcj=l&bCowVl*l@7I_zu^jy^V8SsIL(2@5;l zIhh3IPY%cMkgs_~HjQj7i3e4`7LBwkBB6e$Tfp(S)|B?4bUyi=hCw*?&dH9CnPZn1 z?uo9{;g(@;=x{Lx-pgWf1O@BZsZ0k9tp`N<8chfaN}Y-3x zZ|&HxsBm7N!kfIZvIZkEvO0?E+!3H7npj6=wW;*Y4SF=d;?Y{U8Q-^v8n^a4sh`JZcyjrUepGqM7VKvlwrbb3p}Ih4sJNj z^pD3J)h%A7|DjYzAAZk*0JbJVW$Ot%Bh>R%v9xSpJB~{>0$NsOA=0i431)OA^+RN# z?`$9vpgRo`XvlF-W?Y8*&ID~T(ylF{n5%5%ms1Xeayn=%F>>MI)Ejs%m=o(KZ3?y!8s-^6JjTnFS;RY8yP zNB;|By|69x339oninor*>;;MNbzFWbt__Vz3UxfG87Ttn3L^K@kv$W%{uo2R?!%Xfk845<1l9()pP8 zei|#Gv_>NXR1E73K%I?DVe19;$nm=*bi>6B`1`=$aD}OyU&w2RTknm2^rHshmMq+D zTxc!`W7H&auZld%a|*=3mq^0@5~*EJ>8987PwT!xXRAuY`oW9NIB1IU9G}9*B|Q&5v8yp`PU{`fKrf;VS~eWJGcSlLw zL)#%I7Hp8AK8z5l4>df?s-GR>L;*eDx+M(CLTzF4y>GlQCbmy}3E*=I*I*5`NBt2H z+rCYK2`tXxQcL=`6uv9TTE4vzD>RHo`$?`A`_7DjgxAcyZ@j=FKHV$sK$@dfYOQ4t z*_`|}9tZ;i7)jK(C#C+9)WWO`2R${cQYyWN8&Ob1Vi&kw>9)&FGmHX%50|Q=XH!Ny zmv-1WW*gd-oG3$AauVXx&-I&O{6r4(5g;_gopA2f<1i??Kl6rI?>*dSjKlH<$jzhN zK+Na~G69(T=4i2ZuuZI`c;TA^s9*xAT>!9 z6jmAt=%B&bf*|#9DBtA(K!-z5(AgXS{AA}1sUbN4%DxTB0l;rVPte)?0Kso%C+Hk1 zTr3cZ>iVRnuv0{hc!G{~fhbB2S%|DDc;kTs#D6e0$8xHWz_A5a!z4h2r^GB*3LT7? zQ1(o4my~()^)%;|urb&vGKw*M}6cDaLLG)M#60ZCypjjx4_!=oY#Zfno zTSn%9G$UIQcOu}xc4~uQqf<(^8Jc#AbsNEj8M;cg2$sVa7ji@&5OBkbs01lOr88tj z0IV`A<0WyN1W0E?$UegHJPex-&yYMs*h$5Ky@S+v$(jFYb1shq20(JzvoA}PusHS^ zQzeYNVgLK3O*fnM0;uLyl) z+BkU&JYzrIa@-{|!qzBKKAAK|kNv$QSYG^BKMu5Kzjb(M&;Iy(w$~pCUgM;D{p309 zcxlFDY}TCW_Kr%9R9=`t9)%LZZ;zC&3iw$kvWhlxhia$g8wM_jlbRr znl;rj?wv@tN>+W&U$KEs5g3+HmeC`fMcyi2SGDatiN(;w5)AdIB>zs1f+SE|EN7Y* zUk+xD80vx(ta4`o=Yq5QnW2M$BYN8QMC3DRcQnalVw{-mVX#^y;jZKvt|Z)bscO$M z@vKdD%RJpS*)8vE()`NiO(5~CEpw)cXH9vzu*udPnt0a2P)WGEq~Isq#cGGW#}La_ za%;{*f720@2Z?1|=h2U{u1<#sSIhqRd$?Ly`52asKpgQQDejRHd|e}=3$f0^U6)o6}wa(JTyUGx?Jw*#WM;qw|jpQfHMl>U0rs z0DxYk!^)wEVg;_=a%TMX74uc?xCUTdBb=AIs+Io(_6$ovMI;@Udym#NIy;BVVG_4S zqc{^LaP2wxgkqBG$z(%YJR=>s)i9n-A1by7gJY7W0Z1H`FNt5kDRKC&o`#w`X z*YaFnWof9u9)V=OSO&%oWB`~K`vK8_LS}bokW>RfVwEf5%C3Y{BR^1fu_5whVS2+1 ztF^)D&9J1mttW6*MOmt15DJNf*EX@P6PXjru|{NEj@z=fhvv9_xpgt5I*{wNY>38 zlj(WOMctnFg-4V|v$&SNKf;h4tQWr7jC&-xe)$K8GsQM@`%4iXYQx2TjfKBrV*JxY zQUi)WAg>Cr;g4;PMIVdg+}<$Q83pZd4{T3|e9-Y?;Rq9SkQ& zpwSnZ!!;O?ykOLb#Q(udVOF?5Sdc4VVl-d?CS=Vfq#D%7KwXK;VU!C-Y0Fv?V59Ky z@TZVqgpk?eLO~)cpD5fOM}l;Ynka1V^RSr*rAQbYt#R4ho8uKX0IU*jD`AX6q~9~# zR%D4{i>tgcFGPv-Qw88!))rbQ#e4z@84ZE_ctnZaQi%iEDzUns5~~aCX0?dX55qNx zjpju#JGQM!FIz<}blo>zm2b={8WmJgN2+Wr2O=Jp{)qNnN}=+`X&Qwnl@Wpe@Ct7vc>hh z=x2*#oQW&6Es!k^!A_pd7B@H(rq!j3leTOoIBCnIh?BO=b3fa1h1iyUpie*zCz_Cu z;|0UU&pIL;=@fz^CX<07NG$}M6$}_e}_v}z&1N7 zt1U;DPa*-yKuzvoGvy6EpJ_;X8S@Z7!XLw#2ZBE=a2>j-8yD}=F*ICl+?_x;J7a8t zmrsvq2h7SqLZH=AO<05r$|Q@Un5|$;Eh30W!QVgLeF!lr1fb<#Gzb zFE&K!gxdz`z#ElFlWjxiOXr(7XUp7l8o=0UZba_~Lh999#9JHv{AEPd!^byFZ@lWy z9A*yzavr)GEQl^IF+=-rHdB~^Ls1{ju31Ok7e&!2K*=90U>_nPFpx)~!A{GgfS0Cv zSa5$p=R3%a-*7fz|2~so8uQ5nFv;n(8-UqbU8X3gw?vFA|H#~J1Tp$tPY(n%s8e-D zgOc3sLT#Oz3@NJiPA5(yA3;#fFOwVc%e0|Z0(eq`{Re4S!##g?) zkqtz>>yJ~MszAaGsWI?UpoacPo&AZ6uGNtb5ri1cu5Bb}eJg9|C&`c0r<}&9Yx5Cw z!+%A*wqg#rS{@c8J+#`}`(z8=TBhs7j$sDWFPbLFfGPUdlq+vY8x1LRskS!rO*tDL>&V~ya_d~7Y zh}Q_;l0A=~R~ESr;^RR7`Y403tExaP1X?qC*oIgmlpLQ<7fQ~%v9tRSd);=!&v${F zm>8vhMHAV%rVae}kJrw?fe-xq9KxM%{gt;nl6@3-^pB)qR}Zg`)i7ZS!ma05_pOr4 zE+cLEyS;a(?~34`+y8QTJx}iA@Cw+B@#bLfpYj;V{7`!0ee=DBY&s;*_880cKkpS_ z(A?j`;(1InbKrj_lYI|smKi<|JTyv9ddT~0G{=m*|Kz-I6&_Mpt^}n+Og4bN<=h1W z!H4t>e(^hB9)7I@QuTTV7zT%Buo=>Z6c@S~`ECcV!#);yYz3~2EPfwZIr2Y!ccWKo zVg?s*798ojkjl!dYSQBlkVvnp+8lT2sKb{~lR4GEPa)pU$q@0u7h3Tx9Q+ox-lL=w<1oUtb)%TdrvKI{1PI!Q0O4HI;?VQo!}*KFAsi9H`3pT> zWRzv#j8VDPXz-PW9yfa6Ti`%!HubEG;_g#O01q93(;L!1lw(JS|FM_ZC_azFM=7Z3 zTn@40P}5~6k+AP8I`QL`NEvT+qW(zPQ%ZC9$~P>=;1rzxNMSKeG}e-Rfp7W)TkpsS z^kxHv(BP^;QN`n9uZCXx0R`{ujyYv<=w*3)`)*uN-;Vr3znwW4W11l-&!yh7Ch8`h zyOQ%D4??T+Ko)=W+*BuWEzjvu{s(AIzsAXP{0}|PPw;C%72?21l7N+Iq)BypGu zW22Cpv8^|J5HtjG#)^@HK$GP!cvayfa14dIJ_3abR#JtEqa{?R_D~5F>ZhE)O5rFU zN<*Pu|MT70s%1tc&{tSi&2ZpWP8>2RMQ&K(?TX;v`x=)^ik7!vp;-LX7V4_@Y!r7H zL*m2LWQ+F+mp^qO{6;_ zLeBKe7_>baTp8DWXR3KzT#SF-oxa;k{~-U<+yAoEM6Ev;nh`=e0<_4>ba0~F$c(5n zv_w4{qup>VQPE~Szpbs+C%nGDUADPe_~5B;`WAuD)*{GzV-e&%Edm}=ShoVS;i^Ob zA5Id@oz-qcPe- zWTseec3T3f3R#5SXeR#SZ8u!p+mP*rKbL*Xd-q@5<8!U~C{zkQz<6S>6Rl7#fdV<3 zS$@`z3Wt`4H z8zRqOlHM@1thNCAw$IDo5Xt`(77q72AB(`c-8)LGn|~j7tPRZ{Zpw!7&kYyjB5w9t z{Qkx{r6%d{KjMKv(2*;TiMAbo{N(h4_xzQmh)+%U4~DW&UwtS79Q^vf(vjj>LCgz8 z4{tt*Y&zC+)E}u4&JoT*t}9U1h_5?R7#s0Op)u3EI&N8$s7pB!XB8}eSOqK3R>AUz zRj^XSDp)CH6s$Z`!Qz1m779%jEI4Lx1g zSX#k%a#&`Ic~?d-E7p75$xp>nu%*;>N8o_=5A)Q1a=W{}*Ow znI?Yq06)%=Mnd$CljE|_PC+rIW5jidx2ldMYQarnaBiIH8yr4cgCp;a!IAegIC!AJ z5a&duAUsNob6a3_dk($diYwUOiK{$Yapeyyu6*vd;wq(#xXLrd6%Q0w$b%8r&+Du% zT~tgHor_8i{GS8FQsgv#3JzU?^XZP+r4cAgE{!Y?pY_7XLv_%qIMIddnXgZTdz8E! z*E1ZPnA`&!xbECV;cz{8XF>XsFxfo0ptuTVzPK}-T#$X!3wwO(EMC(fR`xX{J(A+M z3q^}3T=9e}9%J#(?KCnGm_OAX-GQ~b?))V85=?razT4((sr-}OUk+lXN`JNT&cws79?m{ zr$h(3e4^_QUOxsWK)mA9A1%i+p?Vj$1!j4hz`GP)Fv`;5_t)if-d30(Q#5-uZ0`eWvD`saa}5aUK6 zkHE39pJo{Wmin~%40fRnW&tB?xoA@*&`lOk-Bne7*i9wS%@4eg;5Fz3?$FubH4|K; zTRXBziK$;D&`t7N2J&$U8zFd`@d97_ypokbH!%A34PT99s`=a!Y({Efn*QLfBU-1@lZqRc~Mvv1XK zhl1@Co3j%ag^x6po~|h!mwS8pHajsp@wvt&an!!27TX#f??gJb{CC#L;Fe0rRm%vB83a;3 znmC>+r6*<#Iat za!+k`BB0fpfgvH`>`V0=6*eh2P^GW*VxZ>RTrDWDa*R?cP+Q75oUq)InV=NQNqY}h zXD2RbVXNp{+ryS2vh7UVIY!>T+p5HfC>QMLq<+BDJLk3n}9rvw3~8;X-*5Jw@S_vj#n`za%i}; zIDKn1dw63*oe?M>5akQ#Fpv#5lE+wX^Eb?plt?lpL!?qmcZfxnbdTYfa7m61a!EYh zF-ql)5Qy+PF*2_P&QV< z2dibHn?l)%A2+d3X;YJqB|v21PZlJss={#>_lx7G!bLSfJ%*3`N#TX_oT|sIN2#4- zaA)g8DA$0v7ND@8MjgDk5_O_NM`a|$H&`RP5otnChgCz+bgN49_9LxChR99X7)&_= zTxgs-DRbLo(FLamBpb+EP{nJR+9+Ik2GYr))em+ zTj$qSJEFGLYPxLtTrC$VPCvqnyT$%mJx_?Y{{_qgA%-#Vi3M9L}v^^a64lxK5Ojx=zP@FLA1 zee!INK`mH4kD=pkfxp184ZdXwXq1*stzz@>6xr&?!LX|F0LzPfoADK!oe(;8 zPqOYDgP&zQBOJp1L@tju(3@os@RkE)%SOev7?$_-fb?0e3aGgP@MltLm4tp z%7O3&Q6Yeoxk*|M{JO5b)N(vUhY_aFa9(zzlWwER@>8wRRb6S!lla??&DPa}O9Yo_ zN-2O2ZiyG0-)JQtzz8@#aH4GZAXi()j%NH6C1P&prYS~niqp5ps8ct8!`L0wJL23r zwYwzFBCx_*21>SU>;dFvqvAvvBKX=v+*r#*(;PMeH8Wuue1I%8TRAK8D$YVDgH+{U zTx)%8wc%iuhOKlU0&0QSg3nfIfnH` zpF~Ah7z|KT5}fX zx?hJ&6~*ULX%Vg>4?(6aBTwxPkvZYYIZut*HDzAsfVNg&JaD53!2ovlYZya;7AG)( zZxE7`I3;Aj9*YN|NDsJ1VvsCtpn(WK=aMqDQ~0AxW-4I#d9(qJ2dBx#@W4b{&s2d5 zIAYSYaYaZOg3DBjFk1oLo=K@%6v_ku6PLvRrpQ+%XSIFV)TvzCi{>?=LXeBiTPILn z0k&-rD+#0mY=ENzZK~*6<5T$>Oy@!B>-!=ldd+BsY_!ZZ4Cs_>RQxm|&`r^V@dBEY z@L_czKn2VvIl&HC_k2P`gzSnLmO}A{n;eQuCyLupAy-?5A0unIDUMpk2;`?S(Y1Q` znBO}P1)uZE#;6s=?+9p)Y?Ru~G4^wqe~KS#6hf)R&5KQ? zOv;)JV;pt9BpS&XP&eT=WfUO6ox`}(D~?TWiBplloESxto3gcZYv zOxucu06|os;X)e1 z=~j!%fc}MN&iRm)-DytboDgo0k+_?`LE4HZLzjTAm*Cb=MWK?>2c0p@Q?Uf32(qh0 zm{%H$pzJ1^!eR*e4?2h{+gKOhyb6iT;FOfI_GXw_Sx#8jKwg;EPGn=r#30+whA~$+ z04Qq3geWhGMpq|XyYuxt<#IKWBf}n$pP&Z{jhm2Y?&BtEBuwq8)w9tuf76K!EtRmW zVIMX9qLPM<6Jmp@r_3v{+TvJS5iJ0!X_9h|j~=e($}-mFj6Lx2CaNm{307rk;jQpA zJ2o+xgod)qCBBg-mBLn_pW`frC&Qe$s|n#I$Q2eauju{w_0gj>%{|#XeiM2PNrI za)HJy`c2!8bzZW)8-`ox2{L$Cl$@;uNiDUIZa^FABu~~5%5=0~!3a#L#px?ueQ{+<-3Z48`>CsC5+lKLro5~r9m&f8#WB@#z2#w>Rg9_X30i{c!o6yU_K$XEudVL zg~3yE4)?$N(#WJ1c9kwz#V7)m}N zLThw{GO4;Cy5i~+OM0{8^9{fakX=bWNG)SD<<({YmaaCMYvn%FZarlm-=~MxjyMOh++FhDcdJM{uJ76f+d=@ny*eUTS24(Ju^<#Nx;Ny;s8?(V|H0KUxrn678a9MOo9Our24F#Pl*FFEyF%q_A%aol8`|nV!a&FsVa05CJstKhl+VI<E7PZ9iVP+)DeKM{6#DZOb*(&#)u(ZgN?*h#kvPOX58#TWO% zjeJvxrjCjLQeeud+a>1KFbx}5lOh^CU{iF;B}Q0l!z`9f^$dflWXZCRq6t{R+ya74 zp+w(CxK&YUAb>wGtZR_UhJppGLy$M1UCK1J*c_)**VQvj1FF*SMs}j5lT%x6wSSmw zh^Dl%fhiBWgOJMNAr`f|L!>1ZtQ@agSHoR#P}g9ei-D}fIhV={bq3#HLnmNE7`TCr zA%i>OS=|$LYjoX3aUwHC2fDLIM&l}eIq5Tv7n?03nf?Kb#ae)6vQd$!fU?CR3!N8- zYuA(GW~-v8$qfQ@-9bUrC8S(!nZh%u;{Z9$LjZ^AQU@o-HUJj(!Wh>TU}dcIaKFi^ z2Vbv2EpufbQ4kb;*t3A>=W4~zHY`A6OcG$FCBWF~7KsmMM#C@yQ_e^sH8Axpdw>>Z z4`m`OPE|me6}SjAz)W$3O$AEW(~v7SWoZScWS={tBQXMnpk@6b+~nFFMQU z3nQ}pGABUu7@x|r0AB{GPALH@P?DW!wGe9*n;l0R zCSV(R1RUmMQ5QwHDr7j(P}9e?j6#(%Xk2Dx0@y1s4k=rw6b*Rz;&+My53sSAGsDJo zxDD}*lyWvtiKn44OW=UF&?x??VHu7fS1X>FY8il5wr3mwja%B7^O?3(XR78?X3N!< z4azXMdH{lF<4m$oH4{2*QP>nws(1XoO~jMXbQ%h=COk=M(g6JQjTzUfgr{yI9x!T! z9r#*T&v`mE7KFuUEEPo&U_$B~JAu?O8>|3!9#P9N+tCJwh6p1)G}hprF-hzbJ9)jEl-o$H4~fvx@SGYKkx`?LC_!ohsZdOp6l|gv zzMWAWDx%813asCOSX9%2Z1*9E9N-6*Z#hW)o&qmAQNu08H zxSD9AA_FnW_%e&dER~U7TVu&NkF+3*Fhaw3M3Yx-!@~0BjiUe_E@N8Ql!@KcltHfS zUG)Psu!`wz@#M5@H2W(goNgP8e|(4=GIgQ0-f!){pfl9_t>jfVCJ+<9p}Kf~HD2eA zM()1VE7iVojh8|4=3@!0?;aUaE5C^>6+tou+6V99hwe%awoyZ>i|jyv@YCM_*JMQ^~67AVwro8 z^0}?E0mA}!qt5;dGO;%==!)=P_Jc|TF;eM3izm6{xohGx@u+PKn*l9!A0LUMS4R|+y?Yy?!?7U%VUVQKUAaG zC#b*q8L$t;fk2B!DY&_Ny^hqXzs|%;myle8agd8t@IZxyO`W0SocBj}Ho$-GuVweR zI|BQ)M4@y)>(vl=2m>fgM8}f{YW6lwEB!Sa!q(5mQ3T%u_zRn|)$F&==X6E*Z*tD=Bvz#0Y`#^)u_7LP(_|fw4D)vwEBNXC z(S063d=pomERPMwF=ji2`+tTZT-BD1f(nZ2!o#88T$T^-zn~EL<KPRmBx1M!}ZFe*{HB+3ZE^zf(yY1h)M$=1i0|PC~)CZ&4THU zcf3&)E8M;Fhz9_YSDo;%!gtWKT_CmfurBb~b}@TH-I1f`4U>~%GsOg))*k_#({K;P@r8f+UEhIBqb_69D2T$tUVYZ5{OYCp|gRb z6pAjq41#}ISA_pkTHsVMdUS}~=KEs;(3ToxCf*%l+f{AruJx9+B(Ew!2lHgduj=ZM zO#fSxCQU*PhDrJ_$=4qN`SJtMBM&z8d7z6jnUJQl-jQZL6P#!3lo3iFLr~wILy^lZ z1>Kh7-X+emAX_4k5XVgHmc?GtlP8MhoV%IZFCI9ZO zX0BPGi`hG(pb`s&+E&FfvD;!9kN+mGI)FO>!73aS&K-eOfPARe4RKXrr8VKbKbyAj zvVoerp|En-Lq?FRK7x@d$*JNlzz7M5sGbdVxDNT|YSk#E}xRSxAcfK{&wAa~6-r>O7)2D8?sa=>Da8b}#C(_X4LbafOlx{E&K~ zHed%TYrQuZktdD|0XiGvhI|}XKD_X-JDFmLQ&9CcV9#0M^)66rO>Z^LTAA$U2eeexT}4pR*@NXA&s z79}=J6tg#t5m7t_^UH-3VQX57%Z$c`#vQ1^u6?#9ISY1p(I}(QLsp~Xs>TRNr7R{a z*dJYZS&s)&^+!o6V4=?g7RXAo=8c782JRiK0Xo|dkPZlyU{{PO41q~#kEoelIh z1mEIi?7hYV#-UWOJ9D^R@gPd2RBM$AP{AEpfRnuH)esu!Zc(kLtkxCbzsak%)WA;f z4hh>(tM$Z&SdeM9!K-jfkcTO(=)ZqkVf5cX4fG!zrV;cXn7Fe6Osw6ia_EYva=2{WFc`lt(?@V>ila-a8IT8=)Cn7g5V z1k8a6fjKZCoSWHFRdHyAxXWlB06rK8;4*5sJhdRQbr7DItPTS11NC5ANVZ5DY>T=v zgW|@}zRX!O1~dCZF%k6};er9%%ERi{=EKIXJsE@PM>wo{bkIE^r+e088HiR@WCKz~ zvwPEJDjTBzHq>=Cz>X*fTRTDyz7f6`IwW+~k?yQ_jx;=rMWa+7(=WTgN-?g*Ol;mX zNj00bhZ(hplwbsrAzDcteJrwW#NpQ=M!mReG&RXgxR4u1DOfrUAW(NTXTr3m3skY80)FAmDcL9WjyiN!2O*K;C`6`EcM{oZ<=JeA0$xr-+^nup@WqD3m20! zZ;Kg+kZ$A&)Prc+dXO-wH)~}hupyHKAOir+gi3i@l)E}oDd$K*g~G|&9TIk-x=A;4 zq?kECGpmUdJpp9GBkc1y!Mp(%zVwFS0Zh?a;3?FaU1)?#fVV$_N`NxOlGfp7Ye2Ir zn}!)s>RMw!4aP5@Hx?F|CcMvs2^T_%311B3z#tsjbfFx07_P-YOc4d=%Zk-tM4rS2 z!^Lw^I4!KjsTJmvlM9i(P5CfgmgVqGVA$AzwSS%UYX2T-fxX7DJFo&KPK8E*LRS@P z6KWhGBoH+5xZs}J3F;24Ibl4eE4csp@TwT5M$<7hBHfN92IG(b%Sx;MnyfUx8vS5Z zRP~V6bw!UlPvmAdE-pjoWM*+?peqJyz?#(2aF*fVWB&~U5E{d|a#Pj^5tvH7FXAhf zHXpWz5spu11JEqmTHGolMdDdehUSfhk=-ixU7ZP-tIh_f7-8KLm~n#z**50XDX<`Y ze~JLK$~P!Vm&=*~?KtG^fRK^+S4*zUObRT9z=5d3QcBcg@Em|8g-7)S*>HCfZau=e zsJoWK>jl|VzIFMaKPqGcIuT=Wbi8G`H0C-(XN;U38go&*3!uy{Al zTC3Mr+}|*)1^OkX1?W_OdniV%VDvmvExKvVm_CoMsb=iz0h5LPha3b00^c%5G1I0GXG^AgcAWr5yOn^Z&si7X=w)j8|%!OJ& z@gc-Hq6;wtxDnov*UB5>TI@~^#(`?F&^xedP8k*Uw7ST6kl<llnfFKEs+*prbJpZ2JxlXh9HrY(7Tg3{|s$GQ%uEG?vme_ z7^p#yB#5<5)6_IUu(5n_Fa$J|*MWx#GpsfkCY4iK&LC&Wgx0dgV$32SNW#k2u5~tm zAR@20@HYXjRfMSwW<2qxA=U_{fT^cNyhiis0OV^w6SR%H$M zgL}XTaRgLb7fNMYT&RZ@=@I_$@xo~(_bDu{wOwA&m9{2_eoQSC+1($ko z4*`oT7Fm1PrS_2K0z288aOiW0YngpIMA)vr-=n%l>!p7e_T1#%J$*2e+XR6ylP=yx zSJ&Zgx~&o|a!ri9u`qH6<}DHY%i)On(`E{bLoa@BFoJ)qJ7-G|YH8YNDmrNgz1=;% zAf4i-Hke?|q+0kN$s{-fzcQD~^W3=q0S4LQZJYdn?Nk2)w$uC?edGNP*dnkWtlNrx z1Pr5exA)2P9uNOe9K?lIu#QEZg%=&k9mDW2uLi<-MPaC|FhBBQ2UfQ5Z*o>I+#|e0 zArvlpEIbP)DO*<;!sAQ{iN29{UOu3+*?-alKFT!D`9(kp+-lgm*rJme)#F-5&d z`AdM4jaHe)Kto-Q6dVZ+3lX8=7F_lRPuE~ocob*_j|Q1*@H~Iq@@&V=A9mb)cH@>( zX58{@$1OF?xbX~=*N63V6@=2;kxy>L8XW!|!YUB50IMhyq8yo!#*zA2C@R9y$3EG(5yaB23mSxnAduuCb#$ccmUVJlq?82*$SNe@Qx53Co#|H0@Xpv3?}5+VnS*dOyC&K&C1%>Z9aw^ zHkEkbDiBZ9HN;8V*zMyUmC2yiY!eZ11X$Pd@Q(bnxA1Zr6|R_%QM6 zI&a^b_0Qfi?&Cck-aLJ*^^so$Kk9Sfb>Q(y7S=Z8t^6M8Nz07%dEJMJW$%OmLCZ{m zblLTx+h!~rbLY7C|Bv|px#_pH_y`C9aPiH0Y1u)o@9@_>SnyHQKH^{eI@~u6!_?=; zy2n5Ai(w@SZ~bu9Gv`0@3zV@{Q3h}E*LI6XTUkG7v%}i5g=|CG@0s-Q?u}E2!8r^D z0Y~5J$Uj(GEhEA&#WNrZq6PRPa&Mh+;}~EUY%n-sS5y2)S{XHV?Tw&DY2L-p_l%K- z^q2I5NBxDrfX)@w_xec?=SsN;d5u(ME@Zqwhy5 ze#esOkNm>Hf`^ZGPIgdeFLv=#b^Sz(J0n9&f7JC%G@jV-&J0UmCoO2v zXYr$l{}{goiB?STyNuC6ieE3NdE?i$p65OCi${LZl6vB@#jk{_S`x<}A63UJ~1`8EXq9H(%eRz@QO+Z%M6KRzLV=gFe5tb9Cp; zWAu6VyoMF8ovY92W#?PcMhhFqDA^&0C{>|tMSN&o{fC^^a zd)}eF)$u<9e>quMb>Apu3QJj)+<-i*v{Fw^qK+XeK z-1)m|ONrDcYvWs2B&u4H1N%LY;p6*iZhWbk^w`@_yrmTnhj&H5ky8G?>!K~G^*ci+ z=GP0q$nR%L^*tZfhpXhrgKJ{jpBU{I-tezW(q7n;Z%_SOs7-KZSqHS@COqCJL z-(DZN@xcgW5bWty8CgTMK$!%X)qPrWUx-r4>hFL7M96JhUDw0{tx(9lDx)F}yd8ru zxK>U)+yJI`U!Q-m7AgZjK~d7{np?x77*g9$B%NrY4rD{c^p=A$ie{CF6G}rfghfs> znFw0a=Y8AbXholUx*IWyuu%H3?hy1$og9yk+4Dv zS8eIw0-!PnUT#4XP=xqk^$_0aBgzlXquxZ<3tpI_ zFHz~K$IjDdbg=%ZG0M>Z^}%H+C_*q7xTz%th1Bs}ZM;g65EwNCd2c?_{Z2%NDUdyJ z*~v|H4zMKpFhfAAg+onl2-Lae3t(Dv$2h-5R-$5@ zc&X3O)o=8S)#u!S<}L4y*Jt4M`0Eq&8Pq`UI@Wh@jH824l_=X?uQ#G|{V{Idwy|g_ zFu;>J@6CDM=HGbnKXHmoA8y~(0LSf&^rV%@VTI3H0f+_H{_ojW15MsC!*L*04JZ+B z0c1;_saDzr)V97J27@a-O!Md$?ak_iuIoeObjbX%b)oqA&TmCoU_+IW0BOhU^pkUn zhV_FN;?U>^R8+S|AszP7`fOVI6s-yfT0(Jb97L)%3;}El)kI{C1AHSPM(Ym2O+b;l zFT=*EU&^sI2rmd&d;TYsqHpLo{2emXK=T2z0TSm1+Rn;RgJ1aX?ZbcUD1bo)TCnZv zm;xUFDyleeERAmil7OMtGci%#VRfk%?WD5<-48YCRCDh@1ey|}xny0Px@leSj?y>l z)`;6z6*6l2`Jafo51XIq7wHP2A@h63&1@Jt2kzs&)H%=p1bSC>@FTyALV@e^zDMYb z=`-2@gkb>KlFD?@Uz2B5%1Y*?KO&WD0i~}9AdCKy z{a)WMHjOD9zs8(|#D|-(bK$61l^tMm%O^;Ho4j{V9sb_+Ks}HMHwHQw4{v`+)$i=X zqg$wcQEbD%Iuimf1b*nt1aVleZhPwqt z9uM8~(08{U8U+RfeT>pIKGUi_>UbEB;NMn_;CR5V630W!lsO*rZX3bzFl{YATyrYN zqyMEc$K%KybUZ+h*N&FE95>w)rZujjZT|3qa7*Ie`Uc=sTSk%M>}-qk9(n<9?xzR=~rsFTqmxW&g;oCIsu?dGFr;VqpwcK(o-(c2Fo6 z{P^a&Z;n()i`XfDuz~_P(ic~182G>Mz{-d0`Y9i>>qJLbKIGuXQ=`d_Dp>lAFfqIW zfH*Z89y%~7Ktea3^4HoXhA|R-ET;uKw(neffI+x4SV&$3OT1l(Sy%#(R?uLrZTI#S zTD6a5RvdE@njJW=P=v4~@&DpJ6B%|5V3mAzQ2QkSzAmQeXN4KdX3490G4=LX=F-dqHL>cd^y8z`*c3`8OKTz_uedXer@0NG zkUZrMd7&_$MwR*|Gny%LDHh7luGXCPt<^oRH5FdQ`3IbTsmZT7CpRBa9^E0F6?-{~ z_jOg>A)E{M@>591l3m zReCd;oO2v8RE3wC3a>?z)A9-I!Vs|T5CF@ZL<~v$_88y*c9L_j6<&+>GRv{++mFbJ z%%y1h>|nw;_tbHmOR=TTaiXCmXb6y;T|+b5fstq9XIBXW>+>+M{LP3a><(>@0TPVN zVEi~f0ulU(p<@4H)j8N8A%qCMxD>C?vNLiP+8_y$TZj}#Q~3no-g5SE@2Laaz#2j8 z4v`8V&NNUCWCICvwSb(2>2C+MfSBYoL=^-2%QInA1R1kYd;q%=2yC-nijQYEo>}Pj zWkaq6%b-;t* zqn0p{iGL(bhLH0rQ7DU7ne@L$CeB6 z=912~{CDPEUEGlXda*_j>AjL~wyp67?n=-uwj{ceVeGx5bhx9fFR|@A>0&c$e@olT zPo3AE-|5l2LF1RqYs+59D4G@=E*-`H;nbS>qyI=dt(|T2F3B#BV+{P=*_v*h*IKx> zrSuMR%mIcg`VwfWeQ@7+(^Kzv`lj+t-)qe*K(JBe>in`6g3UMWGLWSnMgr1Ft8IX- z4X^wv2noS*`g5TE-9zc7c(^wkuGa z7-3Hc6bFD96961(Ok~%op!-gbHNb+sSIUU4MtOsiRvC2mLUi$|uoC%zW_V8_kf9A! zFsuWHB>PB?fmI0rQ885-zJxMj*@L`_5<>?#4d8;)KzT$=f)8d!LLfu} zgaAl&Rej#VhCdT}5E6)8_Cf%1nqiT1+rXVy0GT2+t)NnC;YBaNRQW3aKLm;Jvwxrq zqIF4-nMwF!Db$tpsuB``){+3^u*>=*mH%Dzl;6^C*PrrR@v$a+3kTZ_+cwkBjm5V} z@LR-w%U&qepsmTt1SK5PAIa>qQ-)|q;cv(z_R{WmIX2^9{3Uffd zz;>c*lmlA90pK$!@*M&85&)}}sL)%XL6^0VQ3*VGhUx=49`mtN>v+**;0KX)VF>bC z8#s|Ik@{5NV0t6>VsxegD;W21tc1|gRB#sRCg*Tcn$Hr!bTz@2FyvdS2a1WF$K!QX z+dtvdiStJgp>HvGdEl{Xyx}aeXRjw^Hh)r&SZ`#c*n#7hn|~SBIVD=asOcMF?wQrP zh{8tQt<~@ycY4dZV4YX|vH@@Uye0AM?N+Li#zL~+?hyKwlG5+x*(kkC)I_As`t;u`N=l~lik(>s{kTWFU>T&$j9l}UtE(OHzX$REOwBQ)CD5;?&Ku7qE zj?l>E@cXkkWY8T#Z?;K*4&7z;6Hvgj2R`LaWj|#hxcnG>*Zbfp!RHJ?wz$d1!x%NbBr&l> zi{Mc*EO@9L?>P$a8I~wkTchv#qSuc`NmYQ;j=DNHR_})c2_lmb==~El0P8Av^r#_% zB4B_^Fmu>Ty=}#7#@rhDu25tUX^pNacY8#H^c$rJU;*^yaT!#Q{mWzxJ~cWwOgyJO zSBw4yl&#gr!@!Z$794%5NX!LOfPx}!LP1apriz0;HKFEgt==AkiW3%~uMdbtEvTZ{ zS`G2v>B*>+QaD^+4TzxQj&uU z6nK60Q%FCfoyVBI>taUA~VK7l3rH^Dc+8bXGRu72}`uH1fZ@LBnkl9h9*%N z3ZP4UEQn$n0Y{ubEgCBYxSd{Onpv3WVb^>$U(DNHc1nIsv%Bk*0otI?J2h9^rm*Lq>|A%|w%hs3ZS_BtUQGEl&;e3V zaE|sAcBG$~Iumzj#I>gSk9OA%14zceJTn%l9*jeNdQ5b)_UgZ$`4s(dX3Iji6 zOLmBzz5Wop^8OGz=b0f6{1DAsfU%k41KhXFA0nG^LmXJ@4{_j^fg$$o@rTF`%n*Bb zde3k>x{O*%8%IXOiFsAw^gf2IkhtNn;-7?1Autrb5<_TFAHZ_dlENVNqUS68Q{f*l z#b@Q8o#3B}Xo`mYoJA8l5798~&o_QfiXD0t8c-F<7q8wuYY=joiB65?R0}1qegRSO zhp5(WE}r2QI1#sS)|@(-CyS;Ir%Ex*w6)Z1s+l&E=TKhTp& z@Hv`MC2mTKKEyLin`aBv3fK>4Pm>E^Es$mm76k8*C`^Asca;77A#`sF)^RY7RARGG zEWgHM;S&EA9|_@aX+ao&(=YRH=1wG4T9EyKe{;X9#$UuL<1eFOQJz8i3q&%}BwySd zlh!jSG=MB?wkL2Nz9KRgeZoNF?HBt}y&D_v{H`$l? zH+M%P{^sA~-@;>~@VB_A34cp_@t2sPKT!pQe~u&N@T5(UYJo_T?cg*+Lmmo1!5XC< z^;%ufvznvGye%Von4`$uz)|F1;@`p@982-NrVx9;ctFM-dKJh5#9G%G0K)b1kP0`$ ztX{OMqUx+O`3F&TOTC*@r_*)fWEvk93IWOt;pq4a7n6!D;LpOh+7=ZNVH)-(7AOV3OEfOI*rC_x!^@GQ^4O--DY9_gL!-X6>sl9hPQXoOBG7~ z2chI1Q1ZUoY_tRS+pUiF9H{9;&LW+uFY89uon+4jrY0Jk*;%Ufk~quAnP305lvl0K z{Lj2i0ipfc>~YclSPDXL#nXbP<`w=K#Xz^X;&((xhAt}9Cjl_dmmG(W=n?5`sIms8O6#k-2KK5jFw*)@y{GnI4fJ9jD~!W6Fh1n6n4VUd)wjW!UJ|FE zojTTMsAE(=Gj$C8UYw77YM6smmN@@VPVaxBb(D+t;`n@J6{WI@@)cf1DT=b~Z0No6 zp4wkG!6Aj)fvj^K??gK8AJ>wCl9k!E>{JE)UwlBxFmfNXr0|}3P~>@VCjFMA`Yq}g z>oXKFS~?>|T-jc#EbPDr`|CHk&%Utp8u~G#^Bw!RT)*!Qq{mLKDNf`w5~kYa^WI%y z7Z1{ruJb?a+u7hgtU1}@Ont}W69R>nO~DVDrNPSgCUXDxO^GKNj`N|}PsE!0eujzU zRwCnlGV=x_N=kR0hrjpKx+ZaX9Y1y;$*2XZvD%Lvw{u=#yaT)a@pjD}V|z??J{1`6 zz{=owiTvCTIO654%Lu%{a^h8vX7^gg#VmhiH2$7g!@c1LU!K6f_j|7C2ob zKz?K$X>K3!jRN?aTR#bFeE2*shu|IEG?cetCeBEezRyM)Wyct=A0Okz^%-BJK|`Lx ziilTj=w3Kk?@+}WHoB(J8$`t&yjA@#*2P|m4DJv6R9N;#1E3nblW|Es`*6!ba#Skiq4Clu^&)=Sh?innB~C z2nK_d;T27xYIz#m6$?f3Jt07{$qPmCl$?n)1Fta&9a2QgwtM~#?3^Zx>(0e~Jib^j zJ(&!C$X>`A5m^dJCetU8Q!9zYCSKIO>BYLWKBux5I{&<^s2~?_SX7@SX9a}G%4{;3 zU5^cN5hwHyMox-Qp)W8p-b1L8DO6at8KkaS@F@1(O<~9$-gLi$-FFrPYLlq}RR-Ic zU@@5m`&XqNznIVY=UB0Ll!eptu|1#cx`&dgCHwx3qoZJ>_j%Z9Pv-CA!jaZ3K3k7B z=_A;HPG&oay1u(9Mp}2^>Ct@uA%L-c-7*GmiuW++A^praem&HRzxjDUIF*H~3wh`J z4<@?0#hWMNO*)5AiDY)WP@1E{=tEV=c9-nym>j}p0Y2W38(R3#_egy#!iB8-wQ1zb z?8=8?QTe%O8i2?*n_nwZEf>96QQ|;!MTuJ^RFwGFM~O>Hp^6%>sPUGI2B5|tOWFpcD{-4gY1eqr)BU7#AjLO1ipuJBZqk{*3B8b zVX~`n#-0mzQXX}aj)B_(la4-_bad)fSFh0t?~_SKr%XEf0+Wuez@%f~6HXPZcOIB@ zbOt6J1CIrq@V>H1hX8ZQV{nGk0VkXuOiQvTu6XuWyn}*%OiMB?4mtgpmSk2Oa(W~o z<8kJDales;1ulj|E^M|BWIo`dr-ME^90gkgSbB_Dy2MOWtZ&75`Y&Uug0zpuneSf| zbmo8UrY$2n^Mjj4cIMg0$j-bp_?eyg-bA|X(NCY(6jpF@_gUtVj?*$PxIkH%xu)C6 zLZ32o{apLijC2+AQ86FMe>sQ&cF2; zv&=J}k(RzMUFiuyDU5~h`-NECYJs0RY;P! z2nPic$Zd$feVefQkm&!2e+Qn#8bo67S2!q;=sdu`5DNV5>VW7ZdJ6p8w-Uz%68%T` zci=&+O(X_8u{M$De35^bt%I;7y58mAo=34-k?6Y#5}fFNg?|S&VznYM_#yvx_F>H; zv1}=V<`Z3S@Ndt(Siea0rLoSE=zj)(Q`w1FjWYK}bD@QD!}Wu&-9Xa&W3nVCF+Ir? zmV`cx$5OH#-I|=031boXYe^vECsZLCSlzP%DEe=dN zKnb{lg?>8K!L<`?AmEK|IH=nOk1yynI!`c%o0RR82s;tJpPyNja38oi;bsQ8n{b)4 znKIyw`7H?zdm|0M2)PC`TCug(jhx%hk&rzko*#N3U~ zFL39eeTw8J2YVztl|3J zH-*=}c*Eu?Sc^p$WwVtJxRZl*R{YgXmag~Ygz#y$S}dBdn(L#9TP+ThzO0YTzC{6K z2KHL8?bDk8kT)y!FOYh4k|77t+h|FQk|EzmQ%&_yT%qx#$EG<5N?4?cCI27qh!~RW+Qw zr}7&l%ZX0q@d-1>{96ps!-rr7j8y&#)*5Zjprn6?OEb+Gl>8Nlaf_RknBq3%7&Ff- zWY4A)=W7bE@gI3#G5{Zcxd0?D`ga^)Lvoow8X%@@W0W1&=kdi)1620mhpeFin+V2Z zE;p0EDXQo<{G(ZDCL^DbiQ(AQq;@~V*7}95`a`MYXO3V09zH9*IhQ4IJyza{e zVBVJtKyv&qLtizq==^d~(lY+~FByP~ZVW}j2jjSGZ2IB&qVSR1tNoSK?mb+aUa*Gw zJC3|L4u89|$l{S)Z~!L@Qh49>Xk5a&kF=qZjqX{HXQCk!n{s4H+(k%rKhor^#e%u# z1Pb+!w}ny*p7qeGiS6FLyEzm^OX$8WRL=)4v0QOa?c&I;IsN-JrID;r55CSN(ZM?y z9gh!U|3dKJU;+77PFW^Uyw9{MevMlvS;{1cSjn0)IFqg_fPAPfh8@qxN2k}1 z2e1!t7XWsg=#)o1P!|dfe17KCrLH@!sii-Y|>%05HEd@t6P#nj0Z5ocN2*#v5qs zWVyDs1yoxj#D?11ED2CyT6K%K7$|5xP=|)FK4A1P8i0bfAC`TBvoEQhTPGQXjH05D z0YN|^qcJFC)HDh?sS0^96!L#gTV$lK(6|j(bNgHb>wBigU+1%*Rr;>Jed37H2LW<2 z2yx)^VjBOTJVJiXNGhE{&u{lFJ*CopC_XjT-__4&CRs-zb$YREx#(svPQ`c9R7YQo z@3iD=Nv?8&uX zngCZ^jAfQ)RjHXWyqiSqX3z~f0CIXrzpKk1$tq<80F zVt{SP&$g9!HieThj7O3TBVMsj-|3q^-d6f+Kh-D6!EMPx&%oB2J+(SL0Jdd3$zd<2 zbXZ&Cf{BkZZG4;_{Xu%{uENFCz+`&xox92dv@hw#PNxSSEPRsW1l(BpY`uMWBQ^Mq zL}3FTmj0r(^m3yOFkhZc4CF_SCg!g*wmW(HN_zT-?bAS#ep3E$dhj>PXLXIza=QfM z8O)WhCkJxV->WU;{aXz1geG%q4Zkg-L-iZ-ekb2b@Tp%Qz2)6xn5fA#6BUM>(#N@h zPmBJC`18(+qO6KgkuIt?T&r z{!JS*KjyMrXWR08Licqx#eJ1NPT7GMQ-i~a@_8HF$ilcjL+P;xPQR8eyqPV`*&=TF zR$}nu#K4oO@}phlpQP>34H`#g;9#osiw;@Szc=%GA~A3|F7mY2e|;llX?yc;|9sV- z@NeaXt~$t1% flq?=y63z1YM3^Touy3U0w%BfVdR|er-{O|Np5oTZP}TDz#3t|Xtc4x ze0dbFE1u{MgJ6#Tpu8^y2<+_=2xv7POOO3tVMJaxJY840!^dzK z|NhS4BZFY%1|V`~OHG*C(b+_>o!OLZ2RG3@yp|sOp9?Ri7%cYyZq=DxTxr@?TWuJ; z{#nN^S&T4a^nAi^98)pm(g?WRSYqO4S_7y6fvqG)S;)^@!1By z)8bvz;<<;V2K!{_edV7o5A(eRleRUup4K8fhtwkDchKA!*4RnkJc3PXQM_Wn|`@cktki_W-CbG~VStPE)Or5Wti;`bK5(-p8!5$B=P=M$yLr;=MutbYQP;XumQV~QF=s34Kjoc8ppq;eH^L5UKv+!;X+-! zCL!(b6Q_TW21VU0Q(HjD+`x?#pzJH3mJ!)3eVkyH0zfi+fVy}3R3@GZ2+9%T1mf=OU+t~P7wp>4n8I%~Q*evF&qQuPgv<|IOo3_<3(PCA8ZP_2 zB_69%gL#}XKl3*$;+Hms8No^Rt#o5c1L+|3`hmBS92jTxRr$EWBfn6#r_F6uR44~n z@9qvULttZ$dNMtEq21TaEtS*p8)M&F5-s&(3lJYCX5LvISO1VA;i2TjjSioV7|$%~ zgDVxjGEWFVYbON4rA-*;z(|VId63jJ6+l&fsOp`ZrpZ?qYLzP-Z_?@tT1o*PuL~Z7 zyQbu>`GL37iV;Q`QdpGYeo1^LI>Q#`WK03Q@K|k}kS9|@h##au2nPfm$+_NDys4nC zqk%XMTp&6j-?Mo8coI{L*6s1f?h`kRiwy~ZNS+jtj3mT3Xt^B@ij@XuchnJ?#7eON z=W1Je2Xc#bMB-RO@SGKFX4>8JJCpJQ!n1ZMjT=7YZu}G?z55+Ljbx z9Y~MtPDB)QQnXRGc(N|y{o_eS2(f)JSw(C&0?9KcR)z0uSqE+U8muXf!sp6nKqM*f z%|6D}H~o`^9=U9(aV9)sX1Oo5jmw6AJGY z1vYRn1sf3b0CYXz+!HGzIQ2k62F87;sVjX1e2AM5sw=4q{< zavm{kr|leA&EH5i|3GYBxo-d);#%K8zY=$yUxdI?J|#jBBwLV?l(6xNxJ$&^bvX4# z<>zoLIVHIgLw`HH$1+x}B8)FLa5zmE^iXE>!~J5rATdI;F}5BsmZUS@whfc@Wm?}) zN6=SRSl)=e%PE7#;AU#BD&q^e?OL2=ZSk8cH2ZQr7|w~L7Mb5a+}mC8z3svG4$1f2 zqro%Lm)^qB&Io8w2=ddpwDMCk;uP;??<9MN+rqt#9*AplGOa|%c^Rg~NU*QJTp`QS ztPrCfB2X~1(U9S=xGevEC*zi3lE5EuAVC5>!nBUG$l`FqmrfHEYjhyJl{<)C8kIue>Vws2EY~=1ByoyrGrhuW$?&$(x2Cp)ltZlumD+_P!7Xw`S-h> zVP*`)3Qr0;MG-x+D#5}qE{YCEf7ujrqL(xU5u5Sw^*H#*i%Wb?lVhWa)1!&PSRJd; zm@sRQU5YDT2_hjsTMWBzOG!TL=o0wn!)qcP4y$D7x!V5ZcM@k)`u{&?{>k3!{}HAP z;jY^pSF11~QMSaJ0V&XET3xV{M<_a&U}v1K2g*0GZAGMf@K}pnD2%9LQN?u1C%J}o zj*g60W=R>QN}6sUs}zD&BHv_{OlMQR3z2y#IsMGSFkxFZ5D&g4ju(L0(nGUV6dljj zxe2~RuE4iEQf@J(hj-e2Sjq z>8eoQoc$$5kT?}gEcwq|TKP|qxH%NFyCJiSu)kKLT|4nXE>HwX5T9E5H!6+CZ!_&P zWXu7|r3sS7Axm36d0qKr#YHVYWq63PsD{Dd*D3KjpOCH=gsRih3Q(Yl-L@`ik{6Gx zCuMh4`HM-sFCp7bPvw?o`IbiJ`GwJO z7de%L!HI;GrZ1V(V$lYbU>JM_HSFj0Iuf+R8Wnyq@gKAqTAEziE<=Xmx~e?bCp_qz zKHM2;A12E|*zCTxcspw)&aIvKt7X0fsCq)-(S(9&kdE@wF7qRE*%0~#Svkqjuo{G^ zfNkGNI#@$42rQsOq+-sDWV_c=vfXMbX4KJERygYk93vnbnM^93wh^SaIO-e2QAl>! z_j$5v0JJkmUq+g&EFoDSlt}tmvA*eVtng_FSq%CqlSL2>2H@8)sa|Y9T*LS-GX&w~ zd?rn&RQt$5^SJ+buxCq_I%*+3`;xQtf*EqpAnn*iEKoX$dwa%*$C2Bn?2NinaFXqFxKu;oqFMtRi{y_T0A_vsi z7?{Y%37*X=gB3R-F)}FOXXfDz;xND^=GZT*j^;re#otb+6>*L0rbmm3!1PCzl1DGh z;Nxp>@c;vZYe`(z&g@(q!KU2-q*oEkPvoqhY6tOhUf+UqSqWrTB@mxWX@S&Ta^+(y ztL!;7xK=cF?aV)B!Zlnk+Xx(iDiK&&M(0AvUa-z!<1EfMOlUcfMra8O8DgI4T%>&= zVz6KaOJHzd(5eZz4L#26G;7{9pXA3jpM2=^DcArLfQ`TFj2L8e*X|i$@)a|tZ4u$5 zgh^qTfQgB4`#CB55e!N5c|bAg&8&UHQ4~Mm8++R9BbLkA&`Gf&`-lMR6TsOagE}>i zVRL*k%^b^)l)bqLA4p@u^VCR%4>-;{DY;LD0Kg@&IOf}y-<4GxYA@r{cH~qK> zRhU#Qht*qNN=i@+h-CzN4g6VB6h|`#DUUY#NyRq&v{HPKNbvPo2b?8#T zN4P#IPU3hZTE7Mv`4uZEipJYDl$(f&2WnxM#gbv_ELkXSWP<(kHSFKRoHV&!aRyY8 z`?v>ewHH<|oxF05;|e%cDeG zTp6?1wJLtDojKSV<6{p>!MCIoglHhz-7d2Jk4d*kOFfwBALGCUg|b_`e0pHS!B$THHRo~be`+jT-n}6pIh?Z8i2;~xz$$rQL%q|#Tpl^t`Vep)S5rcW zcus%TKz|5s(Y||dy8Z-C=TK&gX;;0F&{g3$TFgwamkZWSqE9x2VQ_c&kKcl#4_N<` zT;&$L*ae`SPr*Q<#-l74yhLB6><|c-RknN7I|FaFggUKY>S6P|c(WU1axgQHncm%4 zQ8GrPXg6lY5_b|Ys>KcCF{2^qKdzu3=f$%@<1DOhNRZblWhPyqvSLP+90WyoCLd8f zy;_6!q{(_rxN+x%%A6gu&!?oldRbcvo-vN|DOo$7Lo16JS|5hyq&~)`jN&~^zslW> zK>As?TO#;WzgNBZy@g}a!0SbnvM^omqh z_Q2Pm)@$evnWX7L4H779@&h;qDqu*!`mNJypCHm#Sj?Lsuy*3bM0C*}aWZ;Q$g}*s zDgqHje;`ROo*H!QC6k3-L~!ht>-3g>RquA45{C3jEF+0CmJmt<2F6_9ty<`AHr%TM zZBXC=dVBu zuiYp2agf7VEco<=)-+(EmyA^u%-r7!=&nXiN{zggW73~?x5|N}PC-e?D{IKlj2KVR znlKKS5|OgF(nn$Kb=dD=Nj^>_gyhi3T;Utb;(QZ^`+kGQq%|IH4!GVtGtXLKkEn%0vu?EtG?$$Xz4W3@UH%IDTE( zTtHo;6d5s!zgVmT6%Wx!%mM+GQkIEX8?k0ss@5B5UHePx z=M-j*c*rO24z^Dl7kjs}=MV_tkx?C$4w9zPBnfvE(!74$(Vw+dpwyWwE zj7f>KUzHtb%$k6hGhxEfLVD$CIVuNYzy%!BCKokur@Y1T>QHS5Z2GUM9PP zV}Pr{qzpT}01LGY#j}hqOW!e=7^uklVEz}B=(!!JYUOe9LPF`Je3C%x_W(O1Q6`tr zvb&0AtEt5IwhB{)=BsR=l_fs8MVwS5iCXEpP^tVVDKXJ7KbVOX$&hBE5g?KgWbh@_ z%BnP3C4$@%*>oQ3|NT*5DG zN>!8`d}&;y#=jA%SypedA)@zEf_;M5O1FptE{=o)H^C1}Z>d4;mu!R{T2n4rH#5SK^N!SmaE~>LJ zQoVISw?Z$VPdZGI`LX4xN@pvmBHD0TgdOAcCU{+96LF*86mMsWN;ZsAZnqX97c~_T z%uZesu*$*&DU$QOrH6Spt8Wi0Hl`JmBl0zwry0eVjKX}0d9$-oFXU`m9<2u0;o=!x zq$PG1%NGf&Or5Q^dIda<_sW=ZduETmJY!SoQTkpyyq#2di!KKda$Nt&1S^Y<$w73- zZzV>ysnaJ^f2(z`U}Xxiq#x9X0;G_*fXt*R1aMPZOeVxnuiN&9&=$bHh4ql5iSm+o z9EgI!?=VZ?utx>;}{GgKg7TfBGTfa(797dA8{xoSfz&SOD1HdB1oE50VQM= zUZ}95gbI8nESU0$l1V$$bZGDi#%`#slq8sGCV=5f0z(}v9tqv{MV!DGeqQEG|g14Ipft^dgc?^!h-1WKPMGsXRv$Ymf5n!U}UxtHNf)=71V!bC?rIPJiRaEixxZ zUMwP4`i1qV$*&DMEYr|PtMSCQl$=yfrvwRDKdFLQ6|IGu&e4e!g#usl4Qlzt27Sqe zIc`QgYzi|91`HK>2>nTto-q-1eL0DFOEwr6e3Pi>vr1PrUy}4Tp58d~dP-p7`V?Ss zi=G#I-3hNi5|$SN9p#}1m}lHE+M)NHIktSszKAuun)R4Iw|eP4Te{ zNW3<}t2Rg!rF`*9g*18oZTnn^g(3f@mhlkNsQU(^#4%}%d_b8e1FKGpH4+bUv(^;r zx|+C8PN@Xh1(qTBV*PBy*V9VPf|?|@|A2@qDo#Ka!u|$gfBBbbpORRPZYRKzijR_Nw+y6? zV8$q{^e9#brLAmMGHj1I85w|Iq^OI?gF?vka~9PtU>}jDIU6dHl=xORh`xum?Uu5* zh9Od8OS4@|WA_Skaj3*atVKz7j~Q;1I&pYa9I}NOAF}eKkWvvp%@QA%B?{d%d-$BG zlt`C*y9@{T+K+4z-%?Y3STCA;2%){DXI4gk1GTv@(?ZFDW&yxQr^CMNbO%(Ev(u^D zfqACc;TIio@qux)Nz!mgRK;$@4OoqvOQXd>+Hn#9`C>MXmIE#I0iZqg!64VmDt}mM z4_~Lij-yiTF@p&+#6YQ!BjDPqYU2xvWMAoDa>4(^Tl4CWK=TVjQZ2~pkpGgB`|0+~ zCr#SeE^BsthK&@=K>La>7-2j=*Hm0ZJL7*KA)FGfWS z9kDElyc=2q!dJ+JaM$LwN3tbx*quuJd=0ZvI%&DF&;dQ^ZqPyD!MIg&kXw2-S^Bsk z#CWMBdB`O99T@{N;VX+{GhsFxphOwZ&P}}hDDJED5J)#nwK{>j)?m=zL(RC}cV3~q^Asp?#s;N=@#8ua@YzPMg|NgKMQUyyYeEEretxhB< zvC7J~tEL&ztWQj)uY9B_xI1#a!x2wx0FDBKKNx0itkS&zfp`ewA%nBoH8rtM^#JlM zM-EQqy3$*sJ8_*oK9T)ih>+r-H_)pJOwc!fwZdh4WFGIT&JleZU`HS&2F=uKb+u|lVe()$7*S%1 zx$y>(;PWEE!7wDNS@0N@6df5BX3#77in!#=4AQuftni;!#jMaIHKRZZgzg)j?>Ghv znJwRuZoHA3JQI2VRHE1bs&1py@@b$Qp7h zlS*BpE145xYxpqT#R1U{sWXSkdm-#84D8K231}A$v>#%~{86L*Srk`ESJ4bkN`i@~ zYWTBBT_fJ`4#~qz+p*G#WK4I=`B*RVY>1ABUFzdd3wqR{7AD9f$7W64_>tNTZqdX{ znj(5^QW2i3xP5XJSXX}LWLtEGb#fM5l5%?B2xw_NNNLl*=<$1sVuPj9oXVk2hKcRK zIjFpa(GhcVEPyI&t`e$E!Rrf?sxRCk;V61K=5WDMZM+r>#Hf2z-obKjNfXUo#20Vh z3?O`PreX3X*$o;U+dbaxm<#n&MN=^ui#IPR-o*6+_BjYOXn*Lj3`&%3+#wDcXI62_ z;f#p7Z)G%7_hQ3bj8{g7yWos3tCRd)G?;3ELG!#%8A*gWlk399AF_s!Tyr1z`)TRr z(GoU=dBU_1GA#kI)wM|RvC7X(fchoP|7@cym#$U)%9>g#Iep;k3YAJ=GMMSiBa3xL zWf>rQuR;aBisP#rV-p`dpNPJ^0q=TVb-lRU>(Kh2SEqz5M{D3UQs#puT;zgk!Ol}1 zOZ0I@^A#ifMyk%M@yh*CjKl7$mj(dGEvf^(>Pn-AQ%y?R#Yun+VwKIh;38M}NhUxb z@zti|h`4NsQ%cG3C{p}iE{*ezuLB4}%A`WDwuYT{`5n!uX4wImZOc925?DA|?E|Dy z_+s=WpoByex+xA>uVQZ)?VmKWYo%E^%cd#FJKQ4r&7_lzYqp^^!$o#k(!E z)7XNL?s*yX82s;@ey%A7DmX(s2vk0rl%*e`oy%)o>8k$)btrRiqoy zO*e$>wwNNj)hy|Z_?eBQD3W2HFu!(ZBuv(#meL*&LIVXwLPH4AR*x5xQ;i14(MBr7 zC;Z|s1z~qk(X73JNrZM^3OkprAau7jlfz5HME9u{&{z2T?ii^uSk8JeB{R3E(pn(^ zE!&pWklF5HR(BC49ZiDo7D3wz5!EKp_E;&JFsmW3=cLEc=UVYqeGcP9XL>m$WjYWF z4~f_dF_A`DcNLkMlh}hw&>}9t%JOPF04d-=67%fp>JihG^?_-#B9ReaO>tre4qu6V z-Qim%#^Cz_h#8w_ab!sdUS@!4wF5d~qd}B*Q-~yMtm=b#!l)!9`vh^+$nU6aToLMF zx;aT|#4NL1Tdp2ftMIGP+!W%Uj1-lGEa=#B^BeDb~lp59*(IJeCEjf0@ z?b_$BO9-pkE4?M*Pgh~M#4}o??zBnA7fEy#oFN!hR!IoXoM7oRjSsJko`iq^knr#G zZNaY8rj*zA6;c#r4Dn-TFt4~BRu;226N1`cLSdH8%*l43r|&CyumI5`{JE@JWJEKv zkIUAY^kP_no9$pqaM3A-6se_qP*h%m%K6Oe!0faOpFfUa7c7>`R zil^F0fDyRI%iRj)pXH8lCED?Lq>EOb&d>ks z8+Igq(iBip(kl6^-QgxhlI<2rZ1A@0btC+9Qgdtbhl!N1CsAc=Y=KFY)ST&uMgvY3{At~OiEgx*N z2?56pkLgD&A)p1z+oa(Bq$>Eq1c-2iB~Ay>Kprf^i7B=dIp8OF#yXD&N7QerZ%X&Y zx7I5=`iCW2Cix(#$x=uOa)raz8mjLQ%CU2V;44{&4(r}^xH>PmiY81* z$}w2MvDJQ6L*wjgM^1Gs`UWt#TS_QH-@+~-)%)LVt(J48eyvUjHyY6nNGV9gCK!hy452RhHf2LAK1xlbJ1B{G~Bo336trt zT$AbKnqo;jlITL%ZbWTYbpNE%su;gHnPVH@=0OVvwDw%E2hiMJ5`67I_zSm5Js3V35Z2QqV-fTSxS2`D z>Z24UCU3DV@Le=@yd)}*vG#9fC2h-yOHi^&*w_(3!e*Xt!aDcQBe0NVs({IwhyYHm zMg6rx@AFh46`(AQNDi(WSXbJS5KjHy=zUV5v3>siAV}>oR$D!bfEjMb;LvhI#%D^0nYB%xV3Y4`YHESN{wciXryH3?9mEx z4*4*!2?=^SY%Kw_v?gmOKFNB@U~8UN^e3wbOO6#16;TQ(WO_oP=R6rtx^6rvj@fvK z_4iVR3hU-UN^Fu%5(ZFKf5zTQvNvZ9QU1tCV#kC-kT#lWh7cNJgi%(67MC+FB2BGQ zfDU~?<|pw1B37D{Txl)^*Ok(ppShayTQTFu?P&0Zr-yh&qZ&n=zS7<}V#KRW^f+u? z6sM$zc(z{oKwuaYtDt=d2DCo~9<){$T3h^iD&%5Xe5c150FXLMkXQ=)-=@ErABxPei@aoaDZq}6zyq`d-mwqk`t6YFiIiWLrxW%a63h;m>< zmALJqC*_=JdwhZhh8SeFT8z!A5)2HO4^fhc9$1Woi>*CxSE|pT+nCzimHawSSK0S& zZ31A<%or{1+38sFD9AF;ENt)e;4+_R4dN2fgAH% zBRno_!2RH1y5qpGoOiKHmL}6+Z7P2CQ5}{F9TG(uzRU*`_s0b&_aQz!piP@3nZcHO zA%DHoPcHVsCC9d_cdSsBS~lOIY>kR}RYHhK!La}*2bc4TNpV5!s-_#lz(VxuaaP49 z2J{1uO0+ncMdel04>9R&^{67^L+}DqhjaI76BY&=E^2sd^QmwWGF|SSIBQO-$#?yO zcuZmjaf%JSBK}PKyXqg75lHS@r%#>*(4-LPZEj!#WK5wH-IQD_Up!)Xr4R63o_#ib zAns9=eDjJpI1RMP8`!|5#fk*l4yMv z#Z24~`gkftEsj;~n|`nI=$E{#F+cI6OqfZ&u>oOWpeAA^X4s44%Udx)Q3p3n*&CI) zc|^3#n+z-VNzJ38z}HW2KuD~~ z0yavK{rgpM^^n?-(Ag!5gdXezK;UfP7m3O|AP@w|lnDYMJi)a#DQUW9k1!4H){7NM zO_aCkZh=P+ccd89s^moam<4sh#+DBVTNU5lfVYqQ!t!E$(Phtesh;%m1|L?P&EfQ* z;56!b#LJC@%kp!pqP@D^I1d4P0;}?wu?$WpWoEdN5Y%z?x|)NKWPB&dW!Fs85k-xmjagk7yC$9a9pJYA5>5X*^t2CRt83|YJAEL#1G%ldZQ=a4?RQ=UN`7!nHsYozC7aA~8F^75J z_EqY=3dSZ4_JLW^jW)?7>3E~D)?C?m4y=Y}9U{Ahfs-Ry^!!*_tcE0e;Tj}jVl~RB z1H1FkVhirOS=x!xjc3FBizdF}!zZbw>Y@dF|Xq!4~pPa&99LIo;l2#(gL zx&hH#;Uka?>SIhbG@VjtC|6P>@ztb#aJurKH|KsEIU~>$&zK3OKu-jl)&l%ui+Z}QRo9^S}{!a%!bIxUBSGF7s!6j?tBZiRo&?T4SvS-bCH*kS~Nj2a{{qh z0#6a8mm|XGT0QA&ZShiFAhDrvXNru(1@LRWMMy=s*wBO{Tb!XLf=(F{yPkmDgba|| zxsHSh$37mtOGve(C^=S45O@o(sxdsM^tXv$#wOmib%&h=+dCf{lXx^RN)6`{3Ic0( zYdDij0EhzE8k8Tp5;PN#5--QxV51Ptssu!00f{vQgrHI(N8F7;h{*=W?^8otU*U3i zlBD6eV>HN57gi;w)!VFr2Z12RRl1SgCP03`uJ;s7o^)BPLi$@aN~yj~+Uau^~I z*??d0al3J*LlmCrFjr&~U{KWCLkjY^+E|R@fB?PHW{I#NR&#&oG(0W^oyKoa+qfU1 z(@5Uq-bxr)gI%0m7|k6m{Q;3r$f?hrQV~OugUTNiP(wV&Jh>e8(r9Be@`524%J(Y| zoi$)FBLlE~aZSBjFY)g5k^m4I7OC4w>8?Jm00*XEsiqlZ8y;KnfEf z_(EqK{|3be+6qw7OiohU)MX&6^nYffiLCSNCr|QYGo|9E$6T@wVyBCW`b>j54Z*w2m#5-`*qvI67m%- zwx^fpRo#iGzro!wFE4bm;RXndLX>C^&?@Mxs_~SMtE3|+iqapUf+>r8;iA-c`%X*o z5-$>2m_#PPiV`u?iG?z;Te?|c9Uk9*4%{_X`o{pPvEwn*xp8qpyE0R=C~j6U}K@ zD$xyXFW!2Lpqdl+Wk)f8M#IZiU2|=m+m~kV%X{4(ED>|Z4LIgOIp!@YsNjL;0?-8? zf-Cw2Mu>z;oc2)1T4WHuzr8p{H8TJyaq@E4*L)-fvdnqJKL~gZ7Z&OAgislYnLG{E zl}#QSJV??}&mJA{UvfO2j+%qE{#_}aCW5*j6jc>gCZ|s=8<8O?AtoECEI!6WbRzTc z#Se{o%6lYMg~_v{Ep?co>-9{8#S zvbU_wStr||sibXypzF#rGNeZ^9@WiOrT{U0xL9AQe^IbsZ4&S(ZX6#gNR6SL+am+q z#xzm{5UioMe51k+?S5Q^KuihK;k6{N0?C5s{8i$MhKK8nDZP;P5`vqhq6Vh|p+EsL z%E%tEj5-81+La!HvM@{0wAn>~*^<`-up!;+1^9aluT}IEii)@ruzabaT3e39VzGj_ zU`UZ5CKiNgRpK#jwk|Q2nf;6B8o&u9HUSdFMUgU7J%aaMw-!L-lSaxt37j>}6D!wR z5n8?4jMyI@YY0B90Y1ww#phJ1rf2fSNlBN^k?biCcO&c*)1$|5QP>)C3z~SisONqX z=4d|&w`EC;-GFpUn_4~7z+Lr_qHM%M5`#5l2ZbNlLo8+JnLvkd3v}DSk!-E3c&!$4 zKcVtC%R#83lY<0uVvL+seM?>WQLnM_upj^mg<1~RPJs5)?S;3b%*;#lmHOpvw94gL zV;8u5G$6}wU62x}grVXZ=Js^IMWXt9wiv$-AhBCdwsw&(9 zhAf{4wv!5MS{j{^{L6D#9!3-@B^eQvqV4GkrrU2OLJT1bH4ILU9gu=lIuW0Nr|m9G z77OrqZPC0zMa-Mf444P93~%)!5}R=-V6yTW1G8coB~OVB%_coM5`*DL%g#b$(0WQz zAXEfnId9mIg}8;$b24!{=W1-JxtP<8P}c(&bJIVh6V9s#>%u*=b&Uudyalg!xSTb( zG~#YKF#di+lqw$Wsm&S{c@RGB&a4&+V8BX_g<_BusbOAJ!oE{^beaV?+u5Vk5loeg zWV@d?>Uj+TqRmJx^}xfUk=o5#DF>bvh77AUuSXxk>jN_O%945uGl5=OZPK9;+4}9u zMK%R%I4F%$GpdzkH9NP<%6~nN-{%C>r`tN-nF!9p=|@hP0XA6!4&rlZs@V)o#=1qF9q# z^sF^A`9NTnq!fLHk7DuxUij^!JCNT65ZUP5{Rz_|7_dy|YYtscgqCJEIZ}xo=)T z6o)5@DrU|#2w_wOzMU#oKR&@{TA5|8^fMm*P}O%yz(LR4ICSl2B83zCOF!r zF!?mC&{7n1YBPCef^42Z5}cWw7J_cptS}iuzgaU-fH{+YdErDTV}?p^UMLgy$|rlv zFPQGCz)D2G=hN2iuxrobayIiV0$mk4`EUD4@2TXE^Z`Yy$$`;#=HRX^^Ahp411T4lA&Z}@1)2U64 zJ``Uo51O_M8`)vkdq_CB1XhbM$x|Kzc>s_IEMO+QoXpgqAk3aeTY{lapSE}Osn)?` zIUKts5rSsu;HWx=HpWXWB+)-WWhP{cy-a*>`4#KIm&xMdy+EFl``}ItgONR}k;O4^ zIpZfAJ;KUzk+5A@n|Ghicu2mB%TO>un;86+Q`1Qc!VcJ-OUO zDL<|NTr|0C;E}tDc6DjlQssm+Ej{mtr7qf^bEO~vH%D_oYCEkpl0sVEAI(98D`iH z6&(3-GRB|{fzbdCB!HfWjZ!yp{(MrYu6#gqL<$WdzhV#I^60EmhzCxd%m{DLs*3I& zRF_fdr#ZnNC4<`-F09qf9fpUw2lq5DHYuC%9S5>LjN>Koc-@!Q_-o;s>`Mqg_r+~N z*txGmVJnOKwIn9g6wJsYx@Xho?5uN~bjUyJK$H7(VaL9;1XyS|si!wZ`nFd46xNPh zY!8YRa0wjeiyz0|BNSkn+Q&m?FW9r{G+O4;{gFM`VF#wieRB_DWeftE09PW=ghN7% z=Nj1pvV!mz8a2pd4v1U2gGldK&rp79TSi5j%)IzTIx=$}L>LHy3XWA|0do02L>5Fq zdjcIMM24Yd0txsyArdgxko|fgcl@rI4}>P!&6C~Zb3>`_c_TyQz_oy4~j+0AneR3-PV{^q~q zdrz-Emp-`qwU+GW*{-S8Bg1rH@af_1Hxoy?Uuny3?(dmuEG-{ym$D*qL~i)iM4*=2++4+ckdOC+jXHXZ{1%P3~wNySQ}ZWJU|~-NgTt-F!JSc<)3D$J2;g z`W+5^q2YV=_9DA^r2cZIygR8YF4*xxdQo=sF&=p{u{%}XUe~w)yI7|NY33A!Z z6D`|1CJHq$K#Zic;!4xj+UZ{f*21uF;szmmYrf58g~nmy?e+ zzS35DIJAF}VdpJbu^Es>>JkxWdNT{KV&; zTHib~oOrKkXEwgpr0!^CQmdz_*j30C_vqK=+@vx6v8BgVz|>D1Z*=a(Iu9iWn=uHT&{pFa?5x-| zUdOG<)f4&Yn(sHB<08Y^OSzG~k^=bf{OW5BWoGBUEhYw87uf+!rlUHADCh%W=fykOK{&n(f;+}~$vWEU1TyJg_mW~m!Y01mDw|;9hqDtc6X)w}!tmOq+0CWe{R^R8lkf3ie)@~=HGRlRoLPLV6JvK2 zIGebX)VYTm=2l_e#6d0kbF~*Ua?3&v$gZ~ULT=(d;J@_xBErpmS$;My)xrNakJs(L zXXc-Hx*tQE>3HLRJ&+ptC~-KmrFEtwd;isDid=p(_US!*$jSRNoLq*N$d|kRxkjcp z+qI(==rRU$PCXcrd5vFIxUm4*&0~L zEq!IYj?40qU2|ZeE;^ho)nc(gr*r^yA55(|NxYjV1gb0rqk^nbTj?>UlHNa{!q z+uxd8z!&{%{q?Cj9WNeG{#nXmlK4U4)eIl<7*AW_-_CB%mz!p$a|XNlU$u_ZX)>-h zE4G}i&rXfhV{8iI0q3V0kJk`A9NQS}jn7}JR{$wDZR=o49HgeZo>~vWpTf>2(^HMm zZz3LxQ24v#(={hjq-8|EASw>>PMuFkS zOdB=8w$k1u7H;%eflD1)HeN^Uba;C8_s`WTc20H=Hji9N88EWnpzDQNAsH@mAJNKY zz5LaxNiTm@(@LoFX}|nxLhnl@J-wf4pSWIcf8U+5X)k}(zH1ZDSN)5hnRqUA$B8f1 z=H-@iirR9^*P6wwX1nBCA2Y~P?WJ0#T6T)*wLaN8ZK!}q{!9|Kly);em!4R2`MZa!EQMUP;;4%>~Y-<(aRw@J3fSvzRL=AmJfg;ap~9B$@rwsm2*S zRfEeuS+^sh0t;}`y~Ln$)7Vn{DulP^YClPiFklwoUKYT4 zgy#Q`zx2I&`@dq}WH;D9GSZaYylctCeb8K==i@tM`(4K_a77$rxSW&;t7e#3P{%v| z*kmifYb^WXVwNjqOT!_bo$No6a~@qZccDXh3Lw&UAW>Yb``^EC_+bGfw?C5n?*H;( z0&Yc=pJ>666^1SL5ri4)$V!Gfz0+=qosjsvFu`r=Ca^WraY;bwf$&PxwT9_OU^v`j zTDSNLt8-`>Xm@y(qur~&zqd{XdMS5wk=$4bRDR;WTxmLx;1|Q?gd;0)!f?Uy6%dEP z{|YfNihUwc_}rDI_uDFO1b}m5FT(Cb{%qn@ivaytCkgXxj~GBAf1~%s#=?W*4Xo4Y zg~cKGK!4BNs$*}fJDF)7{KD8^Z8kfzyC#toqqcVt=fI9rmCq_9Sl{R(Ud~)@PA=&0 z$z}n&CJRpZ5}DrD|Fx|k0f4izbCtSTgU!;AdayEQ_}^KGF`iBG|xPz=i%ZriN@qm zL$Ut55a1&AS?n5Yp5Bwah0+W*Pu$0%xz%xm4C{ZyT zbK~>(z=agSPqoA66ndS{ok&~ko@$(HU>0Tuv{=fRZZhao?c*zkAGVb@3q65_KH2(y zTjfo5ihJ18UVT@h)k)Fnw+%4B&x?9!hS|lAgzTc&HQ5c5ldW19_%VwxwzNM3Yn62} zEV0(Tbz-ep(B0%Idp_rh#5iGWtPW->#7$1LKoYARg@8HLNM7%NU;GIRw6z|=LqM$@aXl=?_L?2Z5}%f8G1#G| z;Auo891Tj?0B*`C4GTZPQg$sFTIHC_tUk7+d1j5WBzuwlQwWp###yiTn#M9(`BUwO zmMM-|HX!SLu}M*e*mU(?V$;~)RruNSpyM}L!*Ufam z&D^vPXNXjG^w28zP6l_SsadRYXVzXQ7!kEDsjm+k7liJu2SRlwto)Ufa57NE7YTvc zsp&Qm5^`EhR_U12jBVteHg)-lKmXE{aZn*5$x znPoC>N$G3NN=nO3<+~wFxE1;JNb{o&?pXdxa6a2bP7g3~m?Hc@vqkvJnYmSfiora@ zr{Q9z^gBA)sdi50kRtYVB5myJ(6Yg1kWfjS=gG5OWWMjLROTz@yp>faY$piY8oMN! zhMDA}$&YzL1~0^wZI>+kA(byb{9eS8Y;xu!l6uz^Cc8&g681u}`lXw- zB@$@G63;9?x=0h|ag+%o#ETM5c9TP1N`)L!#Dg3X3f|uXpJ5t> z8^c*ZMli78(#Hu&i(M+$7)@9W4T$K+G%EpyXhg$$#g0r+hm$7{hMk# z=AO!5eEOkl4Uloo!~TVu2i?%oMQ0W()p0h+!ko+4!ayRrSZSRvkuN;>%g+x$RQE3& zYLNKwG9=<=Yo5f!fw%ajPirBvgoZPV6&g5MBLBv3LM;EAUK9n1aO*&SH7E>yHlhdTZ*Q#Pfvei{R_2Et>0Tu zh!^`L`y>5CbVE9XeUks-GE7Ga-=@OZ*-Ye2If<8<9xSK?UYq_R|Yvkn_ z{EhD+s=d%gbx*8fXcZTREQz~v;bx;2JG1e%DDs)bP-ILc`+J0{iyT$R(Yf|PRunEs zb~7EvI$?pg%-%%HWVezlO_V6bkJF+QQ;pj?pi!FBq|qp5m8=@T0w6GB%K&7?2NKye zGaV)(8h}j|yWm8Y(I{8HL<&aEph)MW`n{%8EsAHu*^zo&(UM|mr9+%Nk9)t zIsHB4e#^i4@5~9t--Aq7#BX~S-N4@5(GTn|(Gl#;ZJuDCMOUylck~56&u)|nCv3(oN!VDCL&@PRhF?8bM;Ben0cH+(BY@ep27wv41p_#(2i?k2#_RAS z2T=2)bG7oL{R>H1Poot@YvLH*RA=_Dd!%GbwV zNL1zPyo0rN!LK+mBFeU{O?uD4CY*3AiWPB3LKff6cHv2Q>230r7V|S%7KFXrGW`H>9={9JF#2MtnG9MSC3u;L@cEn()C7a zr0dOgoms3LMZAU_m&W6q^JSMUJK#$Cly&9;KhGyICXi zpG_TF1|-=mz@T4jQeo<9Gksa;b&ASd{}4bciGTQE8@ZFqVrqN^MmMjX5(#d$5(!}F zvfiurV!eu53y#-3RkJghT|*R+ea|(#n#7}q8puG$gBq+Gk{Vp9r6fAk9|%mp&sDDbq(NvW!VB%O9swCaWs2e8#r^M0Af_?4o|JBl~+h zT5%uEL-}szK{1wT*pYgpv)HQt%gN_f$;l)LOk(EG@DP6mkoN8CVwW8=_ODKw>R^*F<1Phu3*Rf3Y&xRUyErB;Nbw}Fv){8XF zcFnCKvC-@+u{p(TUdln^pC&1UUyIs|Cq7s>)#k4>5ETDYODtwE5Rhco5;Urjh2U{2 zuM~4@>ait|aR$IQ@6)vXIxSM|M2>AUXOy9f(9X+~TB#^ciU8ih(xA}L-swh?p;2~}$bYbKBt7w8RH<-b=%^@KLU)ER6yJz1T<#&T+Q<<1cDzSL ztRn8&yW6x3`>dj6=$*v9iSdR17CGZ4f-FSl7ODPJ`>{^yx3gHHxLx<&s#);YG6*olT2%gsAlFJ~0s-ebJ50uf{~Kty6(f6vWULd>(?zi_HpP2ucaq;U4;jug)R5-FU$xsAfvXOY6$ zn>$iC{Y53p!7~lNu0+W=@5sZ{!oXp|4l+7cK}suOr*&U?YfS+OkBWFoRg8ktxn+}S z)p_FBHwlNlwGP_&%1Cv%%8>qgQFa1$-#D;T!4K24o);U%deA)XV@!qwm;WkL6>NBi=X+Ix*HzjaK`;7z7sr}5 zHUX^aE;UZM>UarH8EOF1D9u9+q9}?d(aQ(l60(fc z_xF%)61BQlG9;xNnMltL6Q8$CX3`02v#x|)v@_21k7p4H6JXT zTS$0;QIPiR%Q*En7C}uNnmdGJa;zLPO#p5dRoj`h7uj79I2jDe1)rk#0R~W~53*Ex zl}}&JK;$hwvTC|ImGY%p7_gP&VZanQ*Gx*@!)hiAb6pspp8Gf#)?{5^|x>%78+k zkO<&4OtLy+2JKEEG$^YpH-RpX)w)b#wC6-hvPT7~ zfn0)8q9D*1k7X0fU{4R1E)pmMOYfR)gNED^kE!W5Xf~zt`)rn$&!&i4rs3qNk}{u^ zNj0Cq#a9p~53k}Pum1jr2^BH(mvStj8Ru;3r>Q4QSu8D=aemSyL%P`-YEYyfkkSv6 zn3JH0jwebJTI^WGKh}wUk4$66C7GuAMlWiSUg&_lpvy|;f-bG?%~rZFryD&Qh&rjy zX{4TZvfW9WSQnFbEupW23c`QIX2tGKBpzD=H?~*$z7<4FZuRd^HPYoVmNf4*8A&P6 z}5-9_A5shv2+YW zvIM10)yTHAO2-&%9$&%z*%)2mQ_2knX~CUH)*il(K`Yf7AZ}{h2?)d5`}A+QT2DNO3s1 zfi)|2`BpQ_`%JT1P`m4bItQ|Jk*5^GvydXX3}*MxBZ3rMYO=-xHFqW!G=%Tc-X)%jO4^o4eR!Je>9)jzrGsD4UqtQ`OT!)0_sSo({h|KAyeE?J z59)MH#Wyzn;f>Cw@uZ3AD5k!2X-;SFs@AUZnH?S9;pQZ&v>*uhaPv=>((?P2*%Y*; z(V9vnl)~M*KpF*S7q?YR0L#(}5j064RMA`fi5|giD#amM*#tc|U!4 ztfE?&#}f3L?+)#IwkiV)wJI|mw3T`Hp_-&=Dq~8&LP>k?671*E<$UnP>=d07ltU2m zX#G=5+KCi2pY-|ay(*bdl9(d3oLufP_0tEh)fY(^j?p*D;(8LQ_@Zu9j5CAQXSpU? zpXdoXayOo7%o}PnR4-}-MN2VW>szpR!QJE~VyHGUoV8kAH56mMbQNK~Qo@dtDN4{$ z`?3o=Q0}*)A+J*!?>MR9zfuI>pi7uf|YN7M!Y~cnM{*zd^0po ztGL)~jea|`@wMiQ4koH0ioUOZ`9wl|>-{!=B__qsH=w(!)6I+F^ki~cn2`2gTFpwW zg9rj&lmkw!7Ijts?~%4Q>Eu#FNA^z-(G+X?j~E`UtMFs$E%aHc4mF&GK|${jYv zVz+CMT|PZDau#1w0PE6@B>Vg1k8C0Icj@089vV6MB5Sfw+huQ4!a4UHmtne%{-Iwj z6^RRpgPJvHId6k^iem-dsV0W;y1`}=Z}B5~jXJJ0(J#IRsY6ky`H;M*6Rr#!=rXBnM^~M zOcfAWAu;+2gWtATJn(PZRPv?OJjDo2$hIM=@%H##wx!S0a?40wY*a>4s>OeXnq0&b z&5{&8+l4-XB&dQblHer@<}{5B>$iEer{qBDNH=WkelqQMRw~ohm+sLu$WrhNZzO=M z9#GGajFi0jv0BmKpmKhgfbL^2Hzf_rv!|{$<2Zhc}A#>C__u_tbWXEv?cp#+KrXY)?=(p%r84CZsS6(bOvHjZ_=; zDv9KS87#x|n|x zK$$>AUu_{^1po(p3@}whb@DykMh%zF8&l171K_-xJdSljf{`h#=60i(vh;%*4yiBO z9F&key?Sfv;A$nl#V(TLJuR-N>Pl+Q*tKLh%k)(#espGBy?6Lwuccd4-SaI{BWF4g zv?v#-F>UY*on6^K9c(^i@yUyQ(r5QCgk%uFC=YNsbWS8fm+?WeI8@zS?~JE=<(E`bOuu^w~~*^=9kw z8lsHHN2O5YoI=kop{La6nGULi=>CFl<2Rm}gqI>=WQ&wCt$)f?G~}`sx{W-c(=oz? zY3gJ|&*L??>*R8GS5%kU;(cn7QP-wPf$ue0JKm{wdjZ^blTlwv1xZ9bJd#9qv<|Jp z@kB8~lfb3inZ-(gO|;Rh(RM;FE%!&G*mPs{tY6Ae<6uuSAS7gAcNzNFv5H!kHgp$H(s?U%%L8P;X3I zt)jzOnw0nE3}(X~uE}nCPIl|5h8p^po}mF=OcyG$r+wa3%O+ZkEmhEB>iFgx$xo8f zqPWSlZc^&4-6W(6cz?Z}L|IXgn5rm+U+=AH?opUUnvLyCo`wrCpa-} zC^#|tAIH-dMnla7lg5Tt@nt8>QU;n$7}%MOuXTx;4*cBA7)KWop*^^6_+jUbhF<4t zgsu5}O&U^Imo#)KM?LWAgh^5cGH|(LofN_77?+NCqn95Fh8P2< z_fz%Uol+dD*7>(}0Q$&MYNjaFlIHBSU)xAS%$tQ|JG1t}2;OoQGq})mwIel>)y9?{ z(la+bb+h0zu+S+4t2)#atc3G;o8r}G!momB2*3A8Y|HbD3h!8hyR<1uKVWh_g*{)0 zvz=Oq>9(u)?x}T*w?O!O3xSZvGvJw}QFIXK?pji+B{~Q(O^y-?#ihlPO%SB5ek_C2 z;+9O@2U&~V{XOi{&}MHKI9pPut4liej?`}cPj@Wo*wFn5Rvqz^DX|n7xa2hiwkd> zL(RUPimW1sOL~9X2H6C;Xfgx2P-b!K$!>9gDjgfQgiYg?NDm9>OP}8Yr9W691>#)b zsbCOi&;u7FgE{t&wl{TgoDt{A?!cS*o`35zyjehar}X7`%{HZDt}jdfM+lyp?NZB! zLO0HgMuGH)7GT|BAr*k-SSrv>^1~o3(Urj745ZxnJd$O2Ejwq2^ZRM5R+yQeON(&P z6`~fr$!^+r*xcc1j*exwsCC-`=P$IGZoZ?HIvF!q(SPZvUN!tM+b*K2i`U+btgz>E zEbo?Iw42OOEin9lp!As#Wm28=0ylG%H@(5kSxX5~K}z7AL@vGitg?k5!QVkq{e5#D zj!Q!f!9WE7n)EQh{r6bzNt}^-d?003xTRHA$kxDw=~lY&>*@bLZXB7CP%%H`|M%{F zZ0XHblC=<^u#pdwvJn7>;x>Roar-M%jqn9fOB?}xf`9_JQ(xYR(9Wj&l}I5Tah|Lr z;-G_2JZKb3vvZ%>8f<2^h^h>K@C$>lCxgtV#jyBGuQjm5wrQ#OYrOdC+xUt$cjHx>hjDvxEb&teR;Hx>hjOwAms zSg4<;x-)g?md?~6>r5T8&eUC;ZpY(`yfbx(ZEES*nK~pJufZjh80K4n4tHxS(6LUMuXbB8*gN^P zovObF#p~?^afXWweqr+2sQk1L(($*ky(*6uNDGxVNS~k~!njfZIn+YU$9ZFI(xM<3O_8rh|hiXFh*NrFLLOMq3 z^Kxb@CBFXU*P3x=k{i;U*V|QxAn1^z?#no}ws^yaV&cuQPBd5QlS-7VCg81@NHFv` zRd*IlrXj~@4ZkfzSwwP!!w{9x`*2fR z`T`GBoZtV-J24+MyHrbz?$a=(;n%GPZI6Br22ctLqy8*~k)s1s;(K(e?OP4j-li~e z>S#jMTg3@O(``yewKeZ%E8C#BW$UbwGGF|XZsSxtw_*9F?bnm0&US*;M(+f`kRsAV zv7A@h^q81ZEjk72hbcPB8Ptb+W-+yaXOfBpA5)-tmO$tWXzibey=K$Nr|ziZc$kb!vdd~ZGG2v5TN*`%bBJv<2kfZ>zg z(%Y^Y1J7buNXK>1H}n{W4{duKN>PHMj^*sAE2`3&%p%jCYak!nw_5p_o);p)Y_^No z3yA2R2!=3Y7Kc95XObo#+S<^0h^j$+kgB0K+50hGx{W0{(`|gEN%=7wAC@Xm;y0#z zsRe_s$>m%yxXqJo+M0NDQGbRW|IiYp+W6i&)y7N9=?frlo>zO0F5sN2YkeW=Qd8S4FcO`Pr5vu?E4n$?X) zjcFCs%JJyQI`9Z;RmhFh;}GPUx9sp_Rmnkmy-9f%5QXXUcSVW0QY*o zcTngFa{U=h-j~KyzP)u)`Ht7%!;{P1hd+JrL<&N{J<^eRu}f-{7lZYP2No>NBuY>6 zM;fT9Vx}VxI6D#>%~4keHKhhvn=3meE1~RsQ4kH|q9^tuxh7mW1q97&v`iK?Jp!%- zl|jvh7b3iLD4C1>%Ia8@`D@3}TJ>mNthX0BA3K8Sv65Pb4uv@xq>Zc;q)7^QWIokC z)rewje1!w{`jH=YJRb>$HnU#y~rXD3PJg6CMBZPmkch8k|R2Kid8*B>Tn zy(TEr3deb6p_g)Kt?VZ=g-r_Vhe=alC%e(;cc-0%3;j`+xC2PIC^}-h68lh4u&q~9 zjAS>D-PAa?*R0h5F4o%%F{Qy~m{PSgct1PFrW?%iL(z$+IW^#8Zb0T^TWjQFG^?+Y z&}sBnjeX75y0tSKU(2hh0MhiU{V}_i%-ZuMtP1nkk^we5TLWnwGw$5L9N}Coj)3{0 z5m4ks4e`zrvZ!J`uWzzIX)TcS zS6`qJb}C4WmcHV(lG!dqM1)7VI1sIfi^y8fGMNMxcc}IE;PrZRJuGkAAC7-Y;#cA5 z_AYXCdviyf%Kj2Ly1ltgr?Sr?N4Gb3)T#6r=~Tx~H2pfAig8vE?OUIo#$keEynig| zhNe^)Ta?&NALf0&ylhK?n~Evnm%lbA>q1iuJn5(Q01*zhGProHZH5wQB7ft});rY1 zIrptpshE1ChR#0r)ieP7ft{)yS?qK;OAT?Z!Ba@3yb{GP@iaZK!Q>$eP3LIt*QO!! z99ZabrFN=5z=NlG$dCxh&??miG2%2Vm(rlbR(qkTp*-+*jh&A<;emW~J?nfnK}C#_ zIP7_dR6CjD%~mR7Ji18z@t7l9DnLHk)Vd)f7Q6hpb9@Nt=pbZk-U%pAw*9VQs(SG) zo(NPw2~T#Q34+u`k8{H;^<1GMPrMU*7dn$n--8{aP=kFm0~|=b{^c#L%BZK?&|D#_ zgr}qxF9}dgKh8DPDCd%lQS2(;O|130Dk(4p|M*M_B5s<)EyQ^Htg6Y!?}A@b)YT(f zjP$6QylV+s=LP-Pj0Z>VWim}~1+{p)xJWx2Um)#&#$)J~GF-vw_8MvZRL~`Dwt!4JazA> zR+Cn*vRV)yt(LftH4gBwBW2?16;ZmPY+bz~d31m3?4noN-T&3>lr+fOT zfW#(~RoK&s#6ncZgg562uG2YLEsT9f>eJR@tNyQvhPUi#pJyK8Duo>NO?FXK;`LWh zehM{I?X_E>+2RhWO?WNag@Um6^t^R?=xKT25M(&P5`NZ7hxBm3@#fGri^wDOzSy;E z34S$?1LCkW2d^Rsnb;f|(4aP;ThUT|_v5hisWyKlJYzMUP8jrASxLpoo2?9wCnqYB zMLv1FhBS{Sworpj(0u=4LcDRL-m9&juzAM=+;|RsN!4p=HKL6&`P=^3N>ORSg%N>%9V7btbpHn)m*7< z;?d=7GO<@O+L;dJ4hNZ?u>|cnyK2MnB0C%p*M->eP&#dC0(IL&7`(3HPa;7yuwF{_ zc1QX~4-cA%ZHCop{&;&05ADvV(y4Fq3=Wwky7KyEVBS@^u$G8 zj`%>FK?J(kjHT)tD5$&BAqz3yTH_D5C9kK{Z(6g0D^F*EeK4&AAAm*2PBj9je8;2- zeTN5$semVBt%0Moh?Jq_`I!D<#EDMlYr1EzW4QwP-97=`^~@bjnZ<;NMv#TaF#QRxtfs+QHS-X4O)pf@Bx9nV5ZEomBR3nxJ<@~w%{P$~s= z15@AwHD61WqCU8J$4H33DQ?LmPNiY|rZh?2Rg$G#MWB5FN%50rCkkh11r=eegBUNd4&zP3Yj>p#CYYio2M|S#cvhgC z4un&Rd0D$I&dpT>A+oNHYECW)P?x@9mqL4{Sz@xm9kC(L94RGPBs^wQ+P$oT2!z#I&g}CA-d!mqo`nS!^FnnZM0!_t z;uUsQfl5==KXM@f*ZQDkM{O0B9r-vQ!98{2HdXbf(3t_$sLQJkFTNPWP)d;z*O@hL zQo8C`;?gygOrTLz$x_t@U+STViAhu$NsP@5)7(Zk2!>`YP-0Ngf^Ez3UDFGp?;0a$ zlbKDdRg%}^vV$azNrj37g35Fbb}U&_5Imr98)9*W!__dpG{T8h8bQ58H3B8EkwF%7 zVF14?ekxnUL;U6n0j%i^0g*eGpaMKnN%zKY+n^_+D-sevnix*}$X1waCL0KKp-3Lz zdLA?6$>C{Loi$Wy!d7b2i1Ho4&0l`e2PTDTGEcHd@%DDV+YCRc{Io{atp1m^RJA+xA zotOLg(=zc%NmETJOJ3}&=~RgVH$fzi>L2?o#D_je>f#*2kF2B$YA$Y;>(PL}adIGk zrVtKJ=m~L+D*T{l~)T*RjFYLy}&Ql2*|KijUSx)WakKF0;X>x;6dY!x!e_hxsLI+ zPio$gCH~BjK}#eaw(C^9fSPhdqqM1b#_nYl5FwyeCWdgSh%#&uB2qQ}%nG?!6>Ufr zyBIHL^@Z)_E>jV&F``)rD{=JhXMUOhgRr^?+QZMFyarbB8irlMi+lYbL+s#3wZ!PY z-OHR_xO5G{q94y)Kq~02diu$NN5uvKe6-1fi^_cclSG9;2g7r%U^V$EG1P!(#Pp!U zi}Cb6oiuo6!IKc#AY|gxU?BO-i;iv3i^{0Ed>KX!ZZ;V;!_`+`kPkI<)F`lo|t+#%_g5D(c64%Mg|RTv>%!S z&A6cAOWLGjhTDu=S8_q9x-^S&iXv;8cxLBl;)(Sc@(5+ViG58Ji)a}y6h=lK4`zYg zAZk+-TDvF;3Mn&|(0nAAh&(CgLGQa79Bvhakkjn&Vhk@F6wF23Ay{P;jv*VG=~$4g z7ZFL7pfuRWw3Nd>axe|8K=R(a#y38MX=J=mgJuP+lmlKb*PTgP;O=TllaU2t&yX&D zDI;AB?uD~~0#-`OL>e|g;qZDdRlgyIg+M75KFk503bCsFONmc}LlFBTJij;o`X{$H zisYuM(gp>m2DZ0Q0Mm3EMJ2Ifqnfy{=D;F}3KW3@R|*wk+lX3Vr+P+ItNMi!E)6no zh?o%0z#yQxRGKC?6;byU@F1_gM$7XMp}o)vU&c`t7Wbt$WVA5bT9|K?!U(1uafdlb zf(YS{(CJ+%nP^mjXnC*3Rk_+#QR}1r`@kJ3{IhypB2+XFZVoZt`qk z%)e)KJ5B3Y;ZCE*saTu9I+Ch3IW0v^!Im^$ROwp6p^#^yGT8-yJra@A*15eqkxU`a zZAU@p+QFQiYiEMawSyU*Yu^Q(YX@_7uDj1$lni{jeqPYb3&*j@se&~mabM-#*vU+cLG4V7;i{I~=qW!#YQPTQ+I69Al!My%4& z&f85#OWPJmXdPc}vz2?w-XNYOgPwI=EKjpov1yg?x_N_O(6FiFQ18C);Lde!#il#uvAMZK|-J z1B(hOrDukRQKOI6iyAGhp^5;ux2m%cB9W5A(HX%?yO0q~9?VMlRYNH!u98wA>qT}r z6am>GK15NlB0h{8L9r3{ce!tI37V1xhL91Bl#cuER|RPuiPk<9)^xfjeub%_{V8M zNG>uBT(}`OgggBti*Bcy3ig{4PwkDU7IO`qT>r4B68G&f~OEdh^dj`>0;5?d9R zvqCl{o)R%;Ln|oO4>u%IPC+MRJBkb<;D$9Y(m)B@oBY5K1{Y{; zRiMtXv<2#PFYBlWmKqBn{4MAK(5VrUu{?4i8O88Zsyl<#XhxQl}$81SBC8HI`D+UkOs(J@dU`A$(vC0 zTHmI5M0=j09~d-~1ORqwOQPv1&?DcsnJKm&iFm+f8b~ttENh3|PPF@#v3bUBSdzGw z4?M$qKtctVL6As2nAQm)!?c?OX{ouFW6;g&b2jS=J%uWGq&hn;8SfZIBv=Uva3wAh z8Ieq%H8NIh)t<_ZXISh&wTTw26BC}YPOdel3#DYzOl_xDAN%a?YI)^~`IF1hzvVZU zXP|94d-o6z78(XDR;r&Mc{L90Er@0pqlU-1_r3~EtfNp9>tJq8tTUk|*1=3otnWfi ztb@5VF=xbJP5;}x`|;2T^D1M0CPjk#40nv&XH)qC>Q=%+C{uPM`=o57*X7&6ivM@} zDV@~!?*vgu!6N>~L=e?0-wPOV_1D9RB>p4dcHl>uNfIq`N9m|o1ph`QyUK@J&mA4Pt)9q7MhEt zpobBwP68N$SVekreTj#t3vM4|mk@6F>{>UH$Ct_`oT3=4i1a9>$%|O_IC9y!O9*S1 zCONG>C(X&m{peEFh=zDrqlu-GJ4ESKL+?vJ!9xk;5xxpPMd7R@`$<@`iP0D@*>-Y! zD0|4dHEQR`5=i;GR{3&_@IB2*c86$T=*_Wd+JpceR14cqiD###dki!oLCn+Sb%h-P zO@Pm{l73=9!6n)N!XoV*fWfz$1FQI=Oi6mskJSIkzjsR0%kgGX$MA`Et1C~dVvKTu)(*Fv3v4mwI?Pvy;x9>tM?~dLP zc>@+C{E0vtTU0`U1VBM@9Kn9RSQZGBH5m0PMP=HMqGzI0!J$gQLW`L#FAyLamTgux zqr`l#mufO0!C+QF3~;L?9hwMxas$B!fc+y&xCgNcP7Y!M5$p0T0z4>>uI+~0qey5U zCDajxsz7tn*p=LDlfzBiVjdt!ZaId8%O2dMg+&Qc=VWvA3*@+p(Gf#Y&*->orS-X% z+ES~0szwgEPTN6iLTkms{~24cj!=;->C@V}(1nq)H7L?SofdQmbC1Xt{J3IMM;ed_ zI8c!Y&VkC#2!ex@Iorq#V}3X=$*P1GBbQ$r03BXTjdPl9VoX>II^D5G?4yobfy@Lj zFz&Gv^~T&#fx)VVm^hM@CL&JWn5j5>BR*ra4^Zr(*e6t;kWhS&Cr9G7`)VLueQ;tP zF?p78p0wb$Fca=nwl@o}ioT-qx+PrD#V^+#ZVUGvg z^iS&!6ks-P?6wW!#;7RIwy|gfBTLSlMiPW%i5&$H%?{>-Xm%!mXm&6IqS%hE|6sO&jq0pGvr9<~5tgC8xT5CGktt*FKUPf3kTo*8{8dP6H z1gNS{$`%d}G@1lLV}C^HEDC?+9knB@-ZD) zL|ssJbHWkiD|X6bFdIuLvYvO_MxH9eM5qSXs=zXSwngj4;-dz*>2NjsuMnee$Z%g8 zNyk=@DENotzTGHa164(va~BYvdS^^e5m>>LB+nWfErSp;!<`b2o=*(VG~5YTV7Cfu zqGgs4^}z59W~rGB6JqO7ItFowjsdvA@8T2CED+~QxZ=MTZorKSx|f>m%#dD7B8S;Z z`ZUPKg4 z%jhv!eR>QBPgWRNLdDSm1!A4pjgg5)AO3Uv6H;SXjgWwf5*G)!BmN`>PS9oKXfifxt3v=g zd1DfotP-oJi*wmeEK>+5JUT$5cO@w?`_?;v_Q-{o)u~h@$pRb9EZ}v7i=Ob_Jn!Kq zJcB550)VVujPE!gh{HJ!XeJ&*@ha_UVwITGWo86Vi1e=O*Xyc>(#p%=GBG|1Nk@BO z8^dVHHl}!vlyL>yP{5D+tip?MQ8pe+{F*AVY74;$7GdryW5r|bslzk)Kpi4n??zSY zi;Wa;k%?^enPyt~z*1fm0n4%4g8Iv`Y+}SnCZWH`*3JUK7@4*Jy%Gns+ll93KU@FW)2?48>9^P220;E=hv??Oc8NbeD zLy`a>Tcg-lGn(MZCOjRTLk|6F7M)p;CISc(Bfa3!21_qspO3Y?stXCV`fyBIRo1i_ ze4owW_7(+5AXQBh?)k^4CbXkK6WYO?n$XS!n$QkrXhQog(1dm{rzUjwnO_qEO6S#t zyg+W-*ak`&Vs^v2bp9U^jjn^>HS~GTx6p~;CG>g5JLs$674&(|H?R|D=TwqQ^(=RW zfxqeb)>}B00J0f$jijF2suza+0`T976~fDoF+5#A6$f%jItv0y!ww`F`B4qMB*1yQ ztC2;hg96&XU^iGQA#>Q-P=GwyP`GqA0e3AXL#3?b)s?=afJi-3zCyrs!1y6_Dy;fF zb;Q6RXTt(5<4t(PsMuxngXjcQs%ZbA6@}&b&?H(JshiE2@Ic_nGFNQoS`y_0F zW~7*n@g>%b%j$$P&oqv0K+wuN0gpzZC?KVRG&(cbLoQ-oc~|hwbzNU~*` z$IFRnnn-^vqoUKVdHPqdpMXayvf#yG8GNTnKAgjgso;&(XYLvaf7z;`QKA#8_J7Iw=J&Av36*NXPzmcp|O>Z$@$i;g4@DSJZ1;J_J78D3YO{~rGfy~-03}#wvt=T8S zU*`*cO{58>XfPJpEDzj3vO_4(C3a*m%g&CMyR0LdfyWYB6-4?8By+n4WL5>y!phRdJ>Xb-R|7PR$IsTDY)1H~O)L>`(*b{>A z!`0{`o~UD|jF3kvUSjxYioPyW9(EKUBs-WBBH5V$BiX?WkYwKlD9H}ygh}o`^8-E1 zmwAC6FChOLlMt8^s^5_YoDZ-C!qbui*QO*0sK2$5t_ul3Bq|kTkf;hka&LjMgS|v1{Kt173YKN0x|(GrC5xppKxyU&W>iX6>8YB13^i z4SSG6aagQ^DGr6Z^sdx4Ll)NWj5G>Qu0@GkMh=PUBpj&>vHK+PYFm*?p;;rLfsrf( zb0S$317RK~-IrYM&2+QmaHJd_WC`A-ANy6D%?m;`K9&exkc}+Pa{uGR5;N^fl)F;r zqguBi)(G4++Q8oHN40vd-j!?w>H9DwsFXo~1Ahw|Xcn^rYmwn~RT08omodTkWlZD{ z!XLR#&>|X##tyV;9B&Twndgn|X8>$NEAVYWkvK_+n};37tgT0`4U?z_o;nHyhpVU{iS%pynin`%p1RIH{P<3^v%9CiaNS0G)7e*^RjIIl*KP=N*@3IdB&q8?6n3v+N*_p*UqJ^ZQUrjkjR ztx%`hAI@I9dl@b;YSoof^$KERj2FOql;qOrsa<> z?Pa7m*#KN-bsAjmyEIy(%Z_HiW&19K%kJo%kpokd3c$RPgtcHinKwplD)neILF$zw z)>!|?*>+VSMg(JS1Pu!D+RiR6{@zlsSVrXkr!mld`$DW3o69rP4lKe$?l7dQ;ek6w zyc*8q2Kmt>p&VqO!0Z;hA3-9w82>Dz=~Xq%Dz4>P?zZ`BdqNu_yKVmDTAft@@Jm`x zgxj#5cw0Z*1_#+vhFbFqNpM}$oivGklQzk$3bM%L%h4QUj5jjI4(e-&hrQ1XRl5%w zYgQ{`tyGKN7)<4pepI~_y3%P$o$}}z!8EeiTJ^gVHTsveMD_!Gs$?eA)EQ6*$yxP; zW;kckW1rnbWX3iE;bCEnV3m**;%;w2%pv8_9tUr(c@J+nfJx6B59$O~6za-Fw~(cS z2r`LWiuX9-C!1b%JaMKWoH_ZWG4I(W^9GCRW{DxM#K|s!FZ|k;Q|}iqB>1y33|EG z%kGn!Ny0jzVnkkJrC^F;rHCjc1}F=SC&^m@;gGufy1HhE8MV2Wg;Y=g`_3>IEEZ@bpmHpk_c27cuOwoz7_&fd1XK@Mia#chz z0#1;;ZLA(q8nPb9D%=884Hk8z$dy~vd6o;ymw^18m!=euFI*cIWN;M_Y;?OiCu<`4~%toU_NW#f6Zz3E$b}M^y5-+Jb(d;X+mU|iV zX`_3eZe^Pa>Be>>jIn1E5yc9|w?@Ptxez-Bvx)*{HB8kglLR0_Q~u0VRyIH++q=C5 z?>W8slH;?BuSHds9nI)X`!4KF9jQ0*zPyv(WF@SVg`xB*YW1>-87r%=hA0t4rs?I! zJ`2x6p|>vTU4U3b_k0D~K2IMB%l;sP`S(#xetgNh6ld%f55AHDNo@FK3f%eEjrc4Y znM4S8LSb<(DrJf!MV#1{AmG3kDR#|_gE7{i(>1ir36P`Sct$NFfO0)1q$3YxL)M!; zN5-E2Su|spyz0>~MZnZ@B%UonT-w_Ai(Pl*u(oFh;f(|%I1^BO5!uPWDlGma5L4L9 zub|AP8b5fB)UDL$Oi+3V)0=z^w@zFlI)nA@A{P+MRb53p> zgMk}X$b*@7A4%N=EI->K0Y-ji;y)EG20`v@rph(SNs-8uuUHC%(l1mX<|MV`1uLOk z2`!MI=0Tbz)D7B*+2(MHthQm8_Q>{>ayp1as`Y?N2hjl;}x9XtMnvoLAM{8APnK(Pvsq(nG~OL9b-*=qG--^+cLHosu6iXu9b#cA-bjQJ|;=cxP0T zrN_Iiw3r@b0&Gy%6#|$(x{6S7_Jv>)#jGOQeF-sk03oXGQHDX?MR9_JL})b9;tcN& zjB#8?@iVWj)MeBnz~YHgBJ9$nUA=6c`9vD95{OC6y-$9S3as=nd^c>e=-&TSb||fY z47Mw4K<1m6GQJBpCKncZE_P_kwM zOdu49nfeN0U-^L88cIvnQ4>wuIz;=JO~zF($&Wo_Ic^f83K?-i(Sp9M)4LKzW#4+Y zJRiA`N`z>BqEl2Q&1W!=$4;bWhKpL)R_(f=1iaGB+hgq;LY@T z25qLV0@h5Q=OoSS9`kc%7+UjkW;9%r2yskmdYPMIEAb3cp(0v>dXahQW+N@6kBR1U zJBbj_LeT-aH~e^RrUbzTH~9fAW@$i+td%r_KEvEYGs2gnK*Xq_q_hQvMv2ucYwvmH zJZal$RI@XWjr}3&9t)5#!?=6^5{O7x7hXnvqnlj$P19(AxQrpX&X^%O)^lGC)Jjns z!e}#Ch<)rrG2FN@HnJ|tV~_trnfualpmp7VDvR6zShI!sj1RdP5mFA_b_aQ;3q4~- zHBy#zPv7-mf#NV!5=Si(M{*NkMy-=@h!RiK%Giu@muNw`4}1GpjWy|CHGO0YEwV?0 zWy+6s2YFsAd^C8k2F}mu8{_q=iF12 zm>8M1<+w+&+Tx++Ut{OL$UliBfK0G5bi=C?A?ur1T96%nWu* z&NhR~ebW4dKRdC~g%b!fD4bxjnKxtqNP{`1fQ56&cx_Uoo{MM{&{ep1dkg!0p+Zas zw~t}Bh9R4F6i8t^m{SbfnLrNP!3;rc-vyG`4(1fa?mllJi~Abpm&MZvLvqYL0TYea z%XKznhZoxifjo-G;kGsedn?h~Li4CW%9^4EkyZ#jfQBM#PuG)V!=6p%b3VS5Nd^L7 zxZ!XW&ZpA!T5%!^S#g8kO+=QQ9nDyC_FcH>bR^@H8U9X;Q{p1DwL>8Ii4T+D$KkR< zQ&1eArXYS1u~@dv7j0*y5&H(@fzm-p;lsn*14;nBO>6Hf%h#CH3M7m#>Vd<%p2cxv zV$K{l0_Rlk+6Z>>EdW(9!9|1xV4oze$UpGC5)@`3Z69u*6t8L?YJOaW#39KmFcOLM zaTp9KClA_R^vh{#9M?!{DyQj#S zM{bzXKyGOF)=pNdAA}YrD3U6hM;#k5(!=oBY$XNFC^<1?Fmfz7_MBnQap7fSrmZ4F zDFxayx1;PDVj{3ZL2QJ85K2!`0Fw5Dq5x-7is?kBNL55-Qn z4{9pK(22-%etQ~8_@4j{w6V4Fk}-#QSLU!{z=x}exqUe9TXKp%en~OO7@9Bz zeRa+`FZYrag3Oc6*t67Z2kwug=5-6at?|bcj;RMq7^CS-d?l}!D z7d|`#=5CF!V_}+AZ$>|!Bn3JUrM?9|)&3fPSo{mOQxg~_x+#sW?#VP=)o zz^8|Z>eS>fQ1SX%+jdbFv2bihJv1+}AiOCiA5S7ImJAFektQKdHl74z+JqJ}B1q-_ z_Qnl2vFq~3bsN|mjFzZ%*1HlK5)R#iWTzx1o0t9Sz8t~mzY2tWoNi2*ig z5jv4w4!1~11B3@9bj`-1~GW`+L>sVX1jSbt=Z{)@2 zdY_%mNI#~6>V}h6niU+@>I7@H!mx!4r4dsE0b=cN{?z)QUq!bix#WS%@C z+NX@@2pb&B0B+1}CF9UM&Ss4Ms8)6>wbELc*7l|ZEZyiSVhLM4C0G=V;bNRN$Vl*M z<0rgFE(B>YRuHaDx|pXM#uRG7i>_+y;vh^doS!=aJzA=%(&YtYpyP}r1jQL)PUo0O zHUJXPFtDSm$aNt@-$8G=5Q&pwb>VDCq1Ei&;X_bnig1Gfu5b9r4hs}vC0 zZnisuY1CIUw!zFBI!#vD6W(K=rC25=ekUbSe0ju z5}B?Fb#R(k9m#&Bs>xSj9h0EE!iVZw3`BXd7}&|Op3ZfxQhc>q#U9S9S%WSxTbn~3 zOGpL~n5~o(YUZ(-y3dh{0TV~s8)NgKC*5A7W;J`qKlEag`-CJy`j0O+tb>W6LKY<1 zt`K8pl4xK0jFL&16a}=TBU;QPf3#j&l4OB6Z8Y}AV2CVgcuzUxqQuE`hI~N8s1Yu= zZjf$2(+JEQuXS9Cum%2qYMCWjslKZP99F6}l1;krd{iGRo?4ew&T<>3;AMGc5pvK~ zA=N$PKZZQm!h&{FZO;y?Sgt?~P13oFRJlEWtO}JV{$Lj=yLG&(?U6J)P;Fo2R@s-- zM%Eei&E0HkyW1sar?$$~=OaN8!XxfDi8Oqa(Q7b;8VI8w5bl zqzL6FD6R|OpHJT65?c`sH_5BPfcsdqc?@EHJiH1~EGFC9E(nTc`cCblam9U|J@-3oim{rZR zw#=)zgFM4VM>U~7vV%Fh$<74bWCt_4$-WD^$qwf1CU>9t zy9uN|uO-e4$O@y>3u*B(lAP?vslWy<+5*W~JaSc20Cd+yN0%xJjw%)`NZYKC-yBMw zFWp3Lot4;gdRG>iTroX4%v=XSP5UeSk7$^Bz;<^oVjD?}0b+;9R&UfOGy9$l(S2a?pdA=yrGyq0<}SwM(C~IAayAvpc{ol#N}@0iPEc>FvR;W9c$9R zQbzEpAQX_7i}3{*6L&g_7Zlwf;%uyziai1*NG+*OBL5Zn`Y;ImDPy75LPs|2ses&T}Km^eQD zk%%3#pXZdroUk%O-^j352RqIiyzI%asx9a1F$hvZ@`8$ysdjS*(?ywA(uh9M%x=@Z1%?R z(mSii6oc48koIPIXPt!$O2)!P5@QM7&}eeHH%2%){t6T1$3Fycf-oEMP|`QWTnCsd zYm+=#kXP#gXubt?SL#Gs0FCmyYj}j25rS>1Ei$P>ETtO90GTus1=PE;0KRe}=8+2@ zt;fFIMv~IS&fE~(A}SpUf<1T%1X8w(z^A0WY;PxAWd9;AYFIbE7h|&FCnGg_*k=aP%1I05~AH&rk8fqswxy%Bi0+e!OdRKOACTbkvOBI}&TLAh+d1**( zkum7rLbR;jf-95=Y<7{)w=%^Dq=d{f!&Npd%qaHzL|XPc64nqk@|DUPs7FNW(WMF* z>s?7Xza-Rek*h7bC_BSPyH6qV`>=Ms*}TN>If+@d03?C#gB_0n0Mo?&MipkBjB{q8(W|3h_f{oxqxk*s z^@&J5un6BCX16IXX(EEmq=^VZ*n(!6MPyRiCr5l70Mk!SDDs9{(b{Yii6nn96V?!! zHatXr2=L*OU=t~3w*Vi24MoGy>Zux9h0Bxfx=uhO(tF&GZz()u841+wrZ5KT!m3iJ zG97J?qe~t2Wy%!li*-}X6IQXS!Sto@gsZ*+iFtvQ4S=&z(6{?md}m(`sWl{`E3Cz_ zb1;!xqy4C?d3Xt?_yrbMXB@i+0@aO=nG2yFe9vSlKQS0iqfK_ z%~Ft+%@v>(RVdIFgdd_Uq+i*aSaDFk2;4<1hp<6Uj@TIlqb=JfH&AQAygj&;*hmR9 zjKsyeLNu{r;lS@6%Xv10A$X*oRqR0gBNuKG{YUIb25c12I=C%mPg5kdg~{jZl0#J0 zk-0$D#7cmJma0m!u(iWh%~NtnVZEGGLmWc8&re}W>MctvuPS?}tcn)le z99^pXm{N+gmd>oBA%veG*g-r*bTBW)st#8ZLxB_EzfhsY%yVi0AP`}h1T%CXQAc$6 zGtF}lv_MRp6|$w75UO7Ng!lRvs=HT*~uzrI!`u}K<8R7`koAQ5h}easdag=2E#eJlwbgLM7)&IXut;`gMy&i zTNDd`aNwob1$=*Whe10EAebG@iNWkl0K)8G1`4z90vKiobK)?a5fJN{tep>t(aw2+ z7(bJc7}>(q43_fj*mOxhD=fcrQhZ7tiQ>j=&sD}33lN8~>RfD<LlkXRoRNZ83|h&n%dWjEkP?IG9~aMcY(nPZm;y$A~KQCLG1xI+XO-$C6PzYN|8XqO1aQX zTH`~Bho(%C0RV>!hlppSQBLSuE2zk5ZtWvWkU6Ys$v7$iKVyur!3GHq96B}NDML;U zPbugYG_^{iw~wezCwj@dXr?_T2!u_}*BZvX${uHywTi1`M|^)1#lnypfY>c^NBDAj z_TVBv++dDAV1vuW<-vq240%^hR1TY-&A5>dxsumnSR~LGkKHSL>r5;mqzDp;4ZyI$ z#LQC1Eh%A3uq13O(U=d^8B2(pT02EVxoKph3<+T{RYoKD?{73pY>`1ibtJ%&BrgZeF*G}iYf~@Gt;r=9f$}&O(+hjx!t$>Vq(J$U;uEtIBG>$YNxY-d0!R=U)S7KsC zP!#RyT|p?EKoXk_6Hh)BpUTU@EnCFcUNj$^46bFWGZSY921h30lor*~q1<^!EWB9S zdLj)ru~>T0vxJrcFi0`o2!ABT$wJ7w;zvEU5uwb7Ld~fO)eInr&MBe=*8|q1O%0f7 z+o6#LLI-Y&n{95k*Rg~$sBymVLNpAD3NBXh^*En5g3F{;pr9^SO{OimFsL$bQ78E;r*!`r} zj-?Ig4|y;MH^r=#w*K(N5JdB60|yc#1&9kre%-b~ObS_K>^%wuH+eq%&rx}9M}a)I zgE{57oeAW*9n6sD_FW*)?O;xM?(Q=`zlq^Bzc7E3$wuaVf<7_c-?CQZ%g5*5K!2LE zc(R#oH$aiLs>E5D?i=#R2O#3e=BPT0pcr))#>7%3_zOH~z26C()*>ctmY^d=5gu7W z(int2SluV=;ZGPG(%VxAlJV#lZizn1AnoP4u)ZYtwPg zu4Y5cXYkay=mxXPv}YsJi0CzT>C=9qt}q_6X#ZcL#6)rvQIum=8*6W~8o3PTmz4X| z$U~8SA`hvju8p6-^?|meB3BG8;#f4}_bAVXB57|?K?jzBE&4r=RMf%JQLyOk)SQdn z&IF6z4rVNR`z~1Yb};9n*BODhp`G(?J4ZX`UHAMoU{uf(Fof(63_%t2Yga)S0%gMdi6cvJOi(nzgpuYI!iaf=TBSfNdF>R4 zW$MBY^CR)?Xpkh+$W3okAurVkNV3aeRu0dVFGA80l!&M zI4tWeNN+cABY$of-G!D{jlL0${@_R_izR>LCf>B&pTvJ@xDa-LBBl?$M5!jcSYjt9(w07_VrC8m$^T8wr3MKz zJOCz=@Hm72@PHF}>M9GPf=GHXZ->=}@eHa>sw?n*>}uoVy@PSWr(PoFH_vnwxzLl$sCwaF%8u+QDH4W z+%TLMg|+OH)F`)LdvbS+>zdB+G%`R}GB}#$Bx*xY%0wcZAcLyVY6))5E3@kzP6=(5 zMLB~kHk)s15m6;miwZQ&!tqWKU}DI(iCvS|EMqCUsN~C31WH2($-wq){{_+g`X}%6 z#1y>PLz(=8IgSjj*!zy5a zf8F>q8aP4m3Z}DZB%94UG@BLVYEd1B7ZaNNwA<0ugq4cN4IhI0=_3ufBU4F1->3;A0eiN8gfyc7O* zD!#K>VZ#K?$_Am-73>?Cwi!4|n@Mn#+Scd5qOlE{JaXrX8pWNfn)nYl34ak#wH{Da zVL%WzyqVXVY(|@Hf8AAVAeNxYNNWCeji>oLSjzBsVm!-V_qwHBi}*bBOcQ@6cQy03 zZTAxX_MBM8-;v!b`8$1L6@N=NxA2z^;qU0P>-EhVx)PPSm@=Q1pfjt6}e`H`e4n z3Er|0ahq!)0&nR)ggeL_&$wu2g!>BQiEt_FRH>bsPVDx>hUD_Jhei~dj%bCKa~7>Y z24P%uf17GXbbMs8jkhXi_v>iJbg?VK>Ee#wS)$F6B?W++B)5$+U_FY!BIg_wRkhh7lTA`vy+BM{HK5M9L(G$l006Rk07=cVsrZAjoSaJj2WeUu z@b9{E3kXU`ScOcHk+c#)??mB~a7B>5G>L+tyA^vKqcAZ$c*B^=-NY?6a|?}|yTccc z$8N9Dt-dHxh*E~5Rp}h(Ob{#6!RQ13-I^Bs*Z32-p9b7UeL-k|u?>g`w?a_*Wa>x{ zutLxS14$xJo=zB<;flW;Swd-hqCA{t1E?S^1%L_Wiqk}cMy`!68xzVKz+?_>K?)q& z2zQn#J6y=E(33l|MEcH*ohK>$t!|jkz<1+wL%6GQp@`(ei?Q};rDcu@su4d;LjD@88#m8iuA|<<-$+rnr%|oyY!tNyhZ?6`jBaW{tQnRL(tIhi zx0xF_%GAsw;_bM{a5WH%WHhI?;Z}fHa-vc)Jci*QbIRraDbi` zsHaI5N28cXi4Pzvmh7#XC!nnE@Dn8KuX$g{_yTtB7S8SsnB|J>OpV&jov9- z0{N5m=XQ5z`Mdo@j=v{vS*35r*XT3R*ZESnrz;O0i+k~W@zeUey{)!< z;>3~q@Wk#nn!QAD(P8vZ@aq#@>o~AqZ%f1vX8Zo{Z)YF$0QkSk@Vzx<^0@{c*>__T ze~Twq@OSdo_58h~CcI_eK($V8Z!F(<@>ni>dS6>Uxb%+`@o)3SeSfiwH}2S@_ppad zoYMRD?AH5sOz3?l_UV0lp4av@r$Zfm{L^>lgAf2Tdoa6bC?gBu_Ce6gqb`D+qhRa_mN zFP`+S0H?TK?>nLX<^ulq?QN;D)0g#u9lhd@0T1?VT+=K+`7zz3A?_wx-{B=*#(n0D z-OwMNk?w?-^k>@08+Bn<=@+Ts`-9Q0xCP3w_`iIw`tii{bChuj+SYZlo>ooYx|F|g zApGs>Sw{=GsQ=pxdlfBoU&gEWn))w|LI38uwkN}f^|vhw8`1T^vhsjTU!M<0s{io% zI7z&^4L6S^c$wzhXm?{QX`iN#RmT#8{V`gy&x@t_B(Vd3^h~;K7z`MrYk_&U2xN|b z4=^bjtpp#eZmv90I#<6)#MF<#$v3L?lS7SOD){DL-b=dA`O>-E!_BpdORXFEMwhzJ zWUNC|?eT(D3u@h);?7!7JfEoy{$yDN;aFN$gjwzJx(*nDb-y6AyO_)9+}?kQ>zn7- z(7Cz%gcAqD)oQcR1o^}No4wi3^uzK0riM(r1Ukl0ocH*JM`uhK?h`}cRs5`ISwfdhAo&W)Q{QM#^{&q8F) zxi&K{EdGKJG{`C70uoQMK=%d`o!HB)l7PU9VE9{L!>ZMQmMpu9m zF|7W(m|X|I>G_7q=v7g}d9(?;1pkMjPpt zq59j{#Ni4l9$gcRu36!21x=yfV1@rqpPO4yQCJKMD(c-)QMo_)9@aBd1Vn>b$Xf3? zBO>sS!v_+D&<`;0|?_SXC4$E=yrC;5hZnHS-0LtcayguF;@!S;V&*!~yc z7$}og`1>HO_zO`*JWG_QvM-HGi4awM84^__%|TSv`7^}lqi_*~d=&0TpO3;R=krng zDBz>Ev28B%Enijq^boleT_3H=WqYpj_=|s!zeD?AYOn~nj{!U+);BUwC`pcr= zZ_jp?PPTtUe}`_wlbRhpt-ljLg2%~DcfjLhJC5sb*Y{X#*`8PRm+&3_4t<*yoE`la z{hhcCW+yxSEB);lqDQh_*HVT)+jCNX`|pJ7$qu!_No7YL<}X=DaCdq4>LR7IZp5{` z?_Q+sId(1wo6&Y8N*?;`SDEU3)%IT`wlB|JP>e&Fzb^;KM z)rxqRD8;?li2}J-$TbRnLqyy6P&`1i zIlMzhn=~b4IURqO;~xzzLwwkIA!x3CdryekE6FbKi^RVjKNY z#1fNf8xPDfnX|FrAaEkk(C@FKAk1!Wmc0c#&Lb+Uu3yT#Hnd;fHRt&1`B(Ya-pc(I z*v#l(TGULE^OmqTcL^{V{i3C5=E zCYn9$U9W1|Mkx+A`2D}owB=9sHvX!nj^d-pedlHW}v)3^pGaNWJMfLfF@`AnBJauDF_+* zJw@@nONQ313sd&J7lrpeowAB73WUt>E!e2{Jvm{QDq?c*#aP+;8=t7dHfnoe8Gpxn zWxlty26jts{IWzC1=#itnc1aFSA?;EZ4X8xK^G!l745kY`MMDK`Wqu(-w!}sBvug6 zD&_>}9EpJyfmsm=Yu@`=@v!bD{9O4R7xN1j8r%K`J`|NTgdsfn48E289W@AK&ktOM z$3X&7DI0sdxIY$t4h0rPxKe$8j)#mUl)DLGzZc`xbw`PcDGFv~U&*J@OJd!LWHsWj0+LQ$~=Wju?XYkikHPg2L+llx$S1<=s7EW@OHV~-@;Z8eV2nz zro(`74ENAvAO9(-)`7Qu@TQgJiz;mSg;?i>SZ7OzRv1Y*U&=Ao?+aJbf(5IP+yx8v zcauJv`;+|(0oN|f`}Y@cy|83IAWL>3>U$yTI}*OW5cU1uODR~0`kuFtL?5>MLew{= z=fudp%JtYng098N?3?YXO_D9VDlx2 zEJUWi#|uR**e)Ls+vQzynBQR_V<9~L-72KFz-cZ-<+zxm_kb6&`NMa9(BfqlasbQ; zxR3)dui5?mg(YDIaYgd;LRw6ai2j~Ri+NXa4DMz@ocNEsQU{XH|HqN%>Q1VZv(1o^ zTM6XPp8Rh$R(#iVVpuV%p^Kw1qc|$kyU{`ZF>>w<_ z+pKy$qbWal_4f04)WLi#N!?cc+9Fl8WfEbpW(o6}gwk4!vLEbgG0L)`EnruHwictT zA`2FDtM940&6b!~d*gShX79*l7Ef%k!ixH3kBV-Bk@Z6Yklkl~Vl`*(s3{xZMA?S5 z87%%bcsG6;)@I;yxi$k=g|!(hMmJ!caL~Hi3<`m!RWX%Svin!FHxqlL+97wfg37r? zT;2TPAAGQeT?6)kAM<|3{NSJMN|jO%{25DqI;<`4miqMR*-M>k&St3(e*RGQH!byC z=_xAo{jL^#C<(@|Z3${M{^r%cFb&q_R{b!xuo!YUk68ji3L7F6ZHI6_$Di(e3^f~{ zL(S%PA&KZTQd(xzn-8@a*(u*!X@WnZ%_x6fE;jF%8H-eqpD~5G+9@#Z z-oCu;tFJcCSaGH6GpI>5Cs46yNvBi!`?gkJpp4L)kCqDsNdt$Tj<%nbYuo4reyatk z+AG@&yHeAc%L)ufP^+J!M+QEq@)wUOyj|dfI_!MRa?M*yB-<5nVTR{c-LtUH0XhR8 zD4^5);d?(|?>qQ=dfyYPJC;9?r3~V8^~g4}i2TA}+6yPo=k5~;*(%x=r-6Epl2h97 zCgVP{uZ8^*@U1i2FUM^#;jRiQDzN26;3L*Od;8c5gJ44m#+$LpPORRQx+__nsP0uI zZ)-)dms)B0zI6llx;m2*ikuC0d+BS>gce!f(5k?QTE@?Yuc+01cPw{z?Ybp|#ME2+ zxyo|&pW@xJ*bw$&fWKw_i;aGRX8JESohh3!l-7E>{9^r=mv@)3UpOl<&&|oT3}5WM zX&S0)P9)6`PEyWnU{TQFu6TwkNC#jDJo@FLq4Zl=G?dTfO>D-#RU2D@^|^@e%x3(+ zn;`cutk#9q`nw=<7FKJ3%YTm~(Lq-02bwU@EIYW-2Le9}H*A{$1WMNc1f=9a0(mP@ z-ki0I&tCU0qe$`KepCAgv&PV5(Lp2O6C*l^q6XtX&Uh=_gG}An)^J^oTz>Y-sb?K` ziKEi3W@{}UHpUz}IJj|FYFBDLbl@v*zds^SR!IvoA?6c5Z_STqZLE~Nt+jD5a3S9R-W_u1Jg15K&5~#d0%M1y` z-Yc^tP;}Rov#>rSf!Nx`t;WIZ!O!nX-JfMOmN^+SJhFrBwZ_R{foKzk2rcv0t5G-p zL!zzJL0yfP6|f8Fhw7M>m%nS7?TlIOv#8ugpa+Cjp6^E<^*fS1Z|TStX=g&pd`P2A z5cAyTY~?v&&k-Iike}~>MRTfPd+V;$sr0+13Kpn4^Umc~{nLWbhPr!T!B|`{7T*WP zV)KW8vM^H@X6pN3rslr-(^zJ#_ROb7u1&Ec4jbf@&d+Tft}gzN>UV4*K002jeGhZn zMiXZ~HNHO`UX$N>?qqL`o#V2;M>6iRsrZ>s@qP5No_`Atzn0rpmJq)*aH*V>1?cB<@?T*RGF~* zUhRE7@VLtNjs2@4fX-Z{ow~bkZ`Sue)vniL&#PG9ne8gm*L{)h-eNHRO0 zjGWtfZvWw6-hC~%wKvw)!^Gk7yh$(}n?L*z!u}t+mW_L*GmFPxs-FPa|9x3Kbb9&Mb}m2g>(%O73OqLF zI{!4b#BWd6AB}t5adIH*PVpo~#t|XlUIA0$ANn^v?-4K%XQ%n*Y~Q*#g|(W`O~iZZ zpZA8dozJY|zV>eYbbq%0#g(O-TRL;@%8B&&q)M=v7l1gie-a>RdztMREtc8e zy^ODjq)G%=mChxu*)}}Om`4*YnJait))^%_Cd*V(4CL1d(5z zDla^7$PNd0SG4J(!o+yi&JVvyG*vF&|4ilEaoT^oi`vr!eA%@JE|2S3qlwb-rS>eR zii@A#nevhfY`(oNm+iiQE&Y0TgTS%_llnV+A5|!`V>`98<>@E%_e_s=w%qwk{q6oI z?Qhw8jdr#ict(GR_k$X;W5N#E(+_EX%QN3aYP-u|AN5y|M8pB$awisPsLVq9dxRdSmaq`N!70 zPUO8=$BSQ3N)c0TfcC@xyr5ctm#X!msrZ6s%`a%y_XHeR(5&xmxVE5K|8O;H?ms;o zThIkyrv+WGpbOq5^LN3@`tL(KELd5Newok8%6;{b1)t=CPx80&r!M#ad>_C_ED`Hy zZTloVF-}B3eA*=hL+Fy(RWhm6OjhJOPo-fjKtwv=G;0byZc{?dP?<^7-aPv;)s!?BL&a zw9;U_zJBXni(T4sN$~+*o=P5aNODb(KBtBSWkSql8hO0mlx5r%zjE%YM`FeJ-bCBp z#NoS>wXxRHWa5f$Z%3SemDN3v?Aevjj;`s_o(5>3?9h#AuWFGyxTF}r*5jA#r5FBk zulwada&Kbjd~Rv^aj%`vK702jy%h2E`aE&ZA}`^89{T$7^7n625`+tQ@UZB0&^>fMcTS|8 z3uUKH=i{9jI^W*z?$4FqPM?Ffv+w=4v+wnGd|=r2Cg^WBAFaQ4 zSbtXz@eX?o^I;&#e6aUAd1GXXx`VGCjrG+8rESuC{wp!WxvcGQ+2jtm!EZbJM-#2o zwjo!ewyL#BGc#BW@Vz{pSE2F@G3fNdaP_g9DLzB~J?FX_?ZEcm49?8hfD>n!tH8t7 zn|z+N>1J>b+jTRzM`YX0;H${Ko55G%wk3MTtBT^8XS2=V2imA6d;?eWC$imU*b?ox z8T`a;xM?+kR9T4KZIlG#&q=@g-F&5oIORo8c}@N)`h;`w=(%`sj=DXZ3zmT0G7+5P zedasI!h1(@sqj>c+~6Sb+<@n^ijkMZZ(?cVLwb8-g~3aumG^Y%QKsI6`tC#K}% z{COqYbZEQxuooQXE-75=aU&*2<&7Rlg*Up$L=o#s#gq2fY~sF}PIwdUOWG9be*vG~ zC8LRg=HYp9Qh;*VwntNOVuf<_Pn_7B+(iS)@muCS8_H^WUIUE-9s2StOCmi*dI1RoV$rNdw7Tc6uA$){ZOW7w&po ze|v6`cewvw^>^ryyu%~i8s#Ap*tmS3W`O%?#~D6*?)%csC}O@JUYpDI{X(AN!5_<0 zJaVOc#N$uu@8qrW6t_+5Z}9SP9wMjmc(nEZ0~eS;DJ&^ zaN9w!2<+hPuLAcoe;L?88;C?;FT5wnSi@{x0UJYpW*5a+< zL>?<5jF|*p(sp$2G*yd0$XM_$|i{%yXte+PSQ{|=GY_P>d|c5scR zz}dWZ&?>!l@Y|WM9kfBO9b8lK+LAEdqR4wMioCb~{o8zR{|@%v{v9Ik?SB(_@8FuV zeea-Edhg)3Gv7OCgWfy1rsBONalOTn_g)-%Z~yzZ`QH8=?7jUvMBdx~Ci33FHD~+Y zL96uM!Ea~2chCmCcW_Ply+Pei#X#M%Nvf(akOTw}n?eFoGt!l)fvTLtCGIkCi7rE! zy34>Ny3WCpQmPm4>4@(dbTTLGV~R{W9c0F|u zU{$5bb9dR~xn#JyAgDdxt$%~9e@UU02S{pULgVa7nO$wS#%}&ZjK8;i0vjx})P}s* zJj8};kqMT2K5=)-Nf?MAq?%gln{e1~wA4SyUD8+63IftRg)-}nUGd2nf5ib5n3jet z_uL$jYwiF8Lx^g=*K1F=A`;y_+y3b0Y^?2%Fcgb%Udqt-!2QX$Wj}Cz*?{nW>?{cX zj58rPcsFc_$>$pW0N#SJaE8R-br;H#K%6m8?8Q)iZ))X>^jsHx|hmoo3@seFTNGY36uKkzV z(M0jY`pK6_F!B=mb~;1Jndj=-?yZZbM9Y^_@vQQMyi{+jq8*0^Gx|+$?D0gAnl{th zPF|j>U0VF~BK_CW;{J&o=oVwOchpR02D5sV$#YH)SL@4xWJSC6e*eKs99UG0>oTex z=%T`qH=20RE2SpV6E8F&BwdSwUk-Vb&n|N_-lTeu zN)zgXvD%7J@%ywl7Ch9(S3MKH!bE!XWRqTjUk9&HD5YpU%U55jZSCofdQF+hR~EY| zlNhTl#!sdDYI>eex!W<}PNjM4jNYO6i`ANNOb9nI`)WE4E~1lTf}+$=`kczQA70#c zaz95MFME{MXe)l2Z<-f%T(&_^9j=bf4L5uJPGHQh&AEBOY`H2<$|z+_SPi!=(QA#> zPNet2lMDVIw@w+%KIj!j6Z>MeVo1*9vKjNj29k-PN0*1lR*vh#YHm=TMs~TT-K=rf z)JOCroEs}Qx*c97X%sM31CM9DxPM~0Emywi0nm7O(GAqPb{FNRw=r|@hPZS=yO;BR z`1kTp*u8>_bg%Ft-OHV@Nfyikd#{<3j4R6X#O6N(JKy|gRy^owX=(N$hQjGrF&u7o zCx)y0YBX1v11GPlJ8RSteKoeO_SJ?zFKIo5^ZP*$jyK!?UspP@n%Gkpie7)E4^Tlf(jvc)$50*`WyGm}U3nqd`1!M8 zthTR4W5#S?%+7K)Snr%l7p5~>#fTTTiX+;>y3B9Nfki`}2Da;kx^Q4CCgvPd$>|;I zI(HWnu!X^_9yoc9|GctRwdeVAiRAh~7&5_w>D7r``j=XhOa4#}B%?yjZ|spJ#kej* zwS^=8&3Y+bjW|l$jW}{m{bnj&a~-AVzA>HPp83uCPJU)Lo2qtN+!ksCFU)-NEfTe5 zo#DfTvJ$qD^E+g?x?^bt+|K}P}}{L zJry5s(zHOeGcD#5Z8T~kEnL8l9WJO)?FC8ruAlx!a3l2O9%x;tdFc1S6dKE#VST_9 zt{83C-tuw`Pz$TkP>XbQZ*0gzL93J(ohW2eFg0F0YHGYUR&hsvH4aj95SpsSx_@7- z>nR2$Xr?Izw_JQhYipGDyqz`rt4t?2MpF>1;N~;UmEy6b<=PTazM-^{^4$_eQRwW! zEFJAeuR@B=oW$WwGHBoiEz#8&&Vf1RO?e^P@MtdkvxM}>ChMbzJo;$vSz;skvj%sm zNdgUolXv!>Zp~Wnx{K^7{_NaSH{?mGFvp!(NjME@t#<+hsT-~X*JoT4_M*Q^HKJ2- z0e|N7zFL3mx*9`mtKC29ef>or1WxPb{KV%tm5845ozKnro=29lm^vmzUx@j$>G0wqPvb-H zsXrODvYxTEP2bNwYABK3SX2W0J?rGC^}a(9rbi1lkQD=Df=UB~@ExuWrK(HqY( zK_%u*px?Td25PztSBG5?YK?E!^H}SJscJpipJ;{gCW(IRI5*G2M~7?sSD8Xby{W2K z7VC$zfT=;&ISi7r0I+%NvK#qAzdH8$*5zZI!o2;Q8#&H2^mcDA#?Dq(o4K+ zC~_;5$m~ZUdMd1NXFb_~`-WlIFTcu6#XIUXpHK>#VJ0lYj&UOckO=G(n-~bWxhDpr zJzQPUI5*lAFvKm4N;PVI;FATK_O_~Q*1Hws50*{yIfg^1(uXE`v#nmDf2sRsj?Njb zu9$pIVpjqQTaoH*1Gjh7bV7xg;ylNfbdpQH&F^tzqvQl6@IUC%_lDvpV-WrfY`mQ_S03vpOiX+(XcXkBH)!sT-h zi%sXS!L$31^;n_A-76&=o?()|xf(q4R_|lWvSP~3u`Hb?we=>UN~2sa8kXUaXOCsv zx>08gW=C$$gmz<~_D%Zu8r{7F-F9QffieV$Yw1*+>GVRAKg_<7=|r2 zP*frGpD)Y$mxd~A_VwW#14Vg^FaFgWy>0R5X}>9l7l$|TVa!0XVqW-gKnqmX437^* z{Hb&&1e1w)hn)j*&+kxQ1PLMd3)MX$p@&K=qt4ujnHI81ZNSW}e`aW4RF5=_{5cJN zD+W`cYQxn%Pc`~eo!}<3O5{S^3sX!Y;Ysrlf|mo)_xv7ZZ<1p5YGir$5%d&d-a7z-n`apedc2idsqDRZhBrJuc}=|7-PKk)xR65w{dwO zR%I-X!fTX*-5ZA-#X~M`uPV^^Sp7nC-f=${v1Entl6#zR@@v~ zD^*jkXBH`5`lTfE2ieHVxO&-bYhS8w8^ak{0C0b=Vb)rkitkAF-Z>n@Z?IaGC4dK{#6mRU;>n0O}J+Z&K?GvvgO1H*-zWibDu|J93 z@IT;LvHU2U6~V6Oa{Mx2GqmFJ$H3SU1CT^}0^ka!9wn{{&5$@bjGZT`Rf2 zZ%@CJ9KB&(`?Ix@K%CJGO3Z9}Bu{SiM~b;%_;k?!?OF?0 zu+MBC6rSihy4WPN{o;)k;#JD@iW5;Scl9TZLXP8$5nu=|5?r2YbW?NC`=6Fv^>ImnQtG87 zH)rD1>gF`Lh)fx|#biQlYiXZ{Snz)SRm8$CJ^$#Q>%12|QxC@yKiq}nm(pkcC3Khh zmHs6K>dxG{ZkBEzP3%ab&JaRQok>lj&O((vc@D)<-%_1T?$y<#ZhZ5v_cCw_`h_f@ zd}nR8wcPA0KJrBH2G=A*o_cH@STO7cv?}uC!)) zOi1$H=RLDsQT5dcYdkZ-$wZSWS^pt-%VyVFcU z3@KpCqknWw)7{mNDsF2>l8Gmfgg`7$Jn(OM;_W~rYT6x3xM|0@g9A>#$>5ym>1h-hM8SMUrOXEWuPA+2ZsmRs}Fjlndz0BFz{+Bl)kmGKkk zT>Bj8Tpa{DSD*iX_TDzO&gx7T-s^du{o(B$e`PygYyy;F+BtAgyW2`r;WOfW)ryuP zdW0e|KOzKrE%fqd)FZW{Y80eRl9>i44LiX~4N1ceNnM8|)D$K)W5d}bh%tjF$3V$A zB`0GbBo6d2CUK`G^ri20-D|C9Kigli6B3fJ5^4MS{#egi_qx};?(4dSY@aJfnLbyR z+2>+`gB}=E)8`ibeLgw_)9`GE@Vu&6)`#Owu%@Gd^a;-^(seZt&w^oee3u4B*MTH58+$$dU*C##96ov_PNJ6Fb~cG`Lj^eY}U z>;|PaVSHpd4t`De!IE#!`fJ+q2(FF?=GE;3qu1dBOBI~!BhydYqyY0?eJDXk{D`;q zzP40B{NH0W;{Oh$_94{CuSI8EK8kh5xnd22EL+1MpUXJoa)vbwa+EO)vP{E(1sVo? zK4Tb0;DBNOxKQCY_h4_ak$V+}aHw~I#{pY*5%Bjoj9thFakv+D zJca?x((JM!I=-mb8y9W(_EJ#RE2x%l@C8x1uJBz+Psy$uehdOr;1B?=+T4Q>Z8_v_~$z61DEHWN%6Lp zJp5;#TJ1lh&1v(G5x@bpbEEP$)E`iApC9+de`cEccncrt%4zN3e!@oLEfBoe zptPMtPro-w?+HD!?)x#iW|!)rOQkUtpK=Tz9ex`M4q+6(fp~Q$e1{>GHVzEg4Mm+m z^xmNpXks1aV(7OY%jlR4E(TW^k<*{+&xRmP<+JIP70880tDvV0w={GJo^FG)Z=HqS z?+vO|H*NC8H2}sqJ$x^#DjbxzuM``IaA@eC$Z`%Hu6A9178%MuBP+SmRg-$B);yj; zf3T`&z!x(POK%%K*#kFujQHUZ9{2eSOq+*O%i>Mv*M93iGqy}l{SZ$dy0d%fT|IJh zI(rz=*+ah6U>I{e^E&Mz>lOGsr|FlmENb#PNQBGm^!Wa-etnZ~92ks6;LmVKin2wB zX}a(#27wb_bPEPG)<0ZlV}?b4ZxW5Z;vni44>&ba`_ZBpKA1&|>PLtWu|Rn{;BPA* zTCbpGpGNJ*%SnHTF<96iayje|xnlc6Imq^h;m{35*xg;}4^h>!%OwcRf-{}bZ%t=( zOpmZRL`AQZy&=z!Y>qi;+^GZF8(v?D;}r3 zo$Xk-VmlUDwjB#stj8&5*p5YxG98O7vtz*mI~I--*nKvC8ZOg&5{T=X(;1?nh zT+Ewhv+T$GNmXKuW$4Ew(Gdc1qe<~QP+fdJxOP3o@TAb`f zMh|_-X6l2NifKj_;xkOwR#>T5-Gt1@vr6G7(g4|u*^mcqB9-;i_YA68r}yJ2IniJB zl*p1djBKlJLaIET@{-K^M?vw`>cfI6{ngry$V@1&R;A@a0*(#HzhP3|%vbwmk(P1v4Tk zG%>k!NuVKRKR_()lx zh#2Lm*nGW0US2Trd0-F*j`9R(a}E;%TUVdPKen>PRL`cwrZbhdb$ z!;aP;7EG%dI9mUZ5Py#J_LfqgY}l4kpKRD##ldWe(A(Q%W=n5xju#~&d+tPfd;1KI z*1uikm$Z5XvUO*|+aXo^A$48;*?^;9Wxg@$s&!3$@k zoVlpsg+}(|%6!wQ)jl_tH&ERdu|fT_t2qCyF#_kXNVECx<`_Rvy{Pg(Sh;rMiaRLJS+W#z<0f;rW4 zQt*zUOAQ6H*+Wog)|^5BU}l8snBO{+DAmOPU~RFwSn0gc#d>7P`joJPTsSzZQ{uX~ z^u%D7oDx@5F8boq6NBB2DshqTO-73!;KbrUE46+#{_I9%62Rh{@3=(C^wk`wHf2EI>$UH>yN+0LjvhdQ_uK|5hl{H|Q0x@#QsLDx|_8BV2@pA}-YN3^=Z0N4M7Ry(61*bf*^? zjb~rHGw8n!A6qG44Nl*d&GtVR7X>~pkMuUEz;1tFab^3Tpeu(1R$p$g?Cl|oVZbZtU~V{O89!6b>=1waScW3xTS z>mf@xGb`lszW7XBjz#~7hg#dU5(I9nwGjM6tp#xl3TPk&;=wH7XV}%z$mQZMy>PoQ z0c8h|2QeJ8DuG`hXz|)S8p4aq>wl0v6J_9O<#_Q{jQM%PDaY>3(ZNx|)zQJB z45SmV#A~nkAaj#Wpj|qFcIgD#r4wkEPM}>nfp+NxEESuhgKL-0Pt)MdACS5gp}{j- z24_oyXO5T7&z?Jx25){xBWrLsJ3o62JHL<)PE_Z$BY?h)jsVOiJ%JcFTG{(|9lHv> zr1&NJF!&`r41S3|41NiZ({9PkOm54NLEi~yI$4~Ok1dZ|YadEj=PCqh5+q4Di7!36(3!H@pdluQQSapX5}=S@fEOW zj(%vN-OQp5kA^Qnje4Y>?DpV0Z8M{X!pUGiM-TX$h577@!<`U6p=U?LPtYq!*f`?# z-^Oy}jZuuDC(32(iOP24iOP0*qFC6{e;clBa?P{r=z#|L zszIsb4Udali?Z}6M0{!MlXd#yloioAm6E6-=+oa`6<^sF2ifp3erjQHeyRq$P_P3j zGMZQqmdnnT)8a~A7|y~0%9tX#o(vs|wBX60Th8*zQ>&B`%z@Mh)u zsCD5pOySMSF~*yf>sP#46eY^;HZ~GgFtm}df^peaFxhS@m~3YS!vfeLIs-H^JuV*J zqk6Az^YMrJ6s`Q+-}JrW?b%G>wS=GI!c#T=Jp4ty#|Y~&bDp@-ZTgcz*&ah4Qf^cY zc`9rtbhTUQYEO4*YTRS2A{7`z6;xm|AlEA^TJyL8twu#gV0uatEmse-p`S?1Oh_@3 zUAEhURpXNyw#q7U5Rrn{v>Xf??Dp1v*TXMk0IjH&!ZW#JI~!vLouZ00=@kqxW+K#B zowRHgo@KMZKoK##9ZI}!uq^b&vzhUhST?g}t-^1|A^vv#!ozRR@A(^r{a;+H45RPX zUj}c1Z{fHlqLu98=9ai0K21$4TB>f6Pb4EvQdZDnKlSim7)R{Xam4m?E20nb1iDK$ zMnSJ2+Jarm^wzRVHJM%TR#C4A-u)@`yb;H z-hd;fgH(8D?frCW@8?sBNrgpO$A~NhFVM4K+ou(snK=$nJpe|BXdPrcfpmy<><`gX zpa{bEuM_9*+t*WL^7F&7_}bceUg>Mui_VohXFmHZM&aGCP|#5`PP@F$pu%M8EC-_f0zjms3q z`CELc7QdzT41T-c=5M)s9)1Tu;qUMOX67=Zk79Z;Q#irj;;$R+>R8*177B?N#r00e(@^;ffIx)#j2f67WGd+bQ zBPK3YB{#Y0Aj!{k(?M=J=!N)&7zw%QAPfpGndzYBZwx!$Z^!%Xc)uO*x8wbGyx)%Z z`!7|ZFaB!%xa0kHyx;$tDkZviEf_92-tWY2@4>q9bVuV0nGz{mUo5A=g`Oy9wj~_z zx8war&zT#!V;t$%b&{vmoN>J0-8FkW$NTMgzmp_ua=hPKD5I(qalGG-_q&uRzM{&j zYik>dw}bQhYRCH>H~uDN0)Pzi$G4`D;X-e#l;~Gv!5;I42D{^565I-^-=W}8VAKSF z_29?=ZnS6Zs#*X)U#PNPR-fu8Eg=|i;JYmi8ewTcvhIa*m}vakK^$UeQYVs#R5?v5H>RplM;cUye$7h)4?^Cido9Ydom z90i$BcFljN)TCzWc)uO*cWJ*m^+R-rj`tf*Np5)ag5f5|`weoDjuVde+wp!o-tR+x z`IC(2c)uO*x8wbGyx&!kp~cZik>mY#yx$zMJKpba{#KPYdLY0$_W9%eZvMtwF4Nm( zdb><-m+9>?y3BLckRuX(iKVW(Ww!W;!(?bDM zG`f#_6H_$4q!I(E$`Px#%B3pBIusP&zIMld$ku_>w#0b4yM~u4?y2RmU*_o>{Bg|e z%MErd&`noh$3zhvSk9bCu1=1pxwZJ=Qf8q1uCBZ0xwyPR&(9@C`!@^v z6|dBBd>&?BnP1Y;I24>ncGnDdEiv=F%^5wQ~6+FS7z6@`jBbyl0yW+p=M(0*pf@_XKb$k&V;S7FtCQsnjmPMXCOfjDF+6Od6_UQK z=(DY&bSCb_E!Y?GePAcT&b+GBCwAsIXSb)bJn>pbf1Kc_-0jg*lw47Mki)e@!5$Ds zqC4&SHz_Cku46c;=U7rFl!`YIfeTH(6-&N5-Fsh9Je3byi!;$AEn3qHFc~{ zqbKLHhRJh;1*5+V90_ZZrDDLyOiFtnU_U3FyZ><+_VZ0@kh!x(hDZtLNQ0Nkl`5leY zl*dxK=&^N!-&#trSpgiUn^kJeuVfaR>cTH-8mttRd=?x&^=ApoPab%3`Jp|YfcH0^ zW_yJHr(emqKd9t?S_=1PruUI%SyknxXqLJsm1fDF6c43sm-L6YrAHNv=IqEFslX<% zG*>0CG&ijIhdKTidNbsOrB)le#>Ek1rQg%M8e75UOnf?ccB!2o~XiA!en zPBo}7lUbeD&e=@;f8rj<4Pjtg=*q$w@oT7LU~{+!Qho8BLW3ly%CH4&u9CQ_S8^SV z*cD_VyMnac^~U^Z_x0>i*ym*mABOp^URtl@#^3d)+b4OqX+B>m;b)j*J`_MF;7B8L zxpVolI(6quiO5^p_pQ$N=hkS+VEy&x3!U)}ekUkfMf7B<#6DNu^my&rrNT2~ANl!B zQF)oIzSWv}`kp~G>l}GJBCB0R@&=Dskr7>WQ(KCUcu6MDqm=k+^l9^kOS~w&=@x^diI{B)a!xZDn|Lgs}ZjT z`bQD3X3DhO*3JMI>3Fc#zz-H^^uca#AcdSfldzgwv%^N(!q%9>ZZkZ}{%CdbgZR+q zFRXs2mcnV+!_OYzsUB`RP>bJ!S}Kpc8i*&vCV4xPKE4+(LUGp5sTeD5E0XE`7zE{L zTGD5I@VD` zOK<8i`@w#qDR(qp+N9xNJ>Lc`FT^mVYuipGa9JiUgs&DKDN}1cgRz;4&DSf`C4^v< z2WIwcuIB`J)BU(P#-FddMhFNVY1DS6tiHNn!?)SVl@cGsQNFMnwaj@0tg8r|)}@W9 z=&pf!_Pz$R>e<@hqJLW9or{mwpglhGv*2I;OEwEzfR&<;Z(T|Plh8k zjz2I4eA7F*Bs%`s=L2!dpnN7@DtM(fuW;4VZR&Ydy0`GClGWbtHLbd7G~m$edG*#x z1Ol?l;3aj&Yisfr19s|%IK$w69 z^Y*>BZuM&4O3iL)Y-hM%87soDrb2B1KSDJb0pcv7!ralvCK0k z7)~*<9leq(JbF#}<$1n%o)$B=+uNJOSd+@OUjDVOJlwQ5_1af3E^dpXUNRa5RBokS zA|zF*mu>1j#aekV(6tiHz=6=(;$6ob2u+9V0I+z{-*wGsBWWF$BbO$?z^}Gh2#p%> z?`r;l+Q|H2=%p=Q?p2s#@)X9@*=*@Ii#%U172{@Z>ExZ+hVpCIGE}(FFLfqhJmKXd ziGTfd(02b>Ee1k68teF;wGK?LD}dNY@Y%jAZRH)75a!%sY9@Zf#x z@syaFZs8RZ2npaKoCH*XSspKB^)qU?mlSfWe10# zo|hdRgKl0HvdiHWtau@>P%YXf*RRrSlVj!}*(OhA6x-z7xyilc5H?{DRKg|MU zt|C>w1wWWf*_*H{+n1LU6;}x!{cn|Zqk6wLs8-#y$rq;yC#8q)WylV$+uK))>sAyu zUC+wNGQW^^>V6i^uEo*_a>2vH7nQ^pdPr% zd+U}1d?D~F;oBeUneZ zDdB-i3k}pz&=-e8AVmd#pyL1P6*>nK%W4Z;06Ryz&d!k*{k=&H^XMAH+BU1E?6M&S zUSo7n^6*bc5Ht(_q#skFnERC1hQcNvN58gSLE!c@S`H(YbT$~Uw9bYEFRin|73*xs zLDty_1DYt??(PcW9Ry$5<*SqMLmA^sq=&xRaY`|@@MxJ_!j2#yn81<4`wb;s}7m$ z;MAp92s?Gjl^!#7Da&^1Qf!Brx|B2Q)TJC{rY>cfQo*q1YrQyA1?mx5Q!C`GO;t$tWrKTgAOian)+NvCWB!yS-1R=@- zqZj7t@0b^|U^B5Dx%YkXDy)VELkRqW@Cd*!%yAkuHr(_=MlfQ$x5-*3Oar+E@cJdU zU{+b{1X%(dAds04m+IVphmzgnP_macN)}bP3h~3%G!rIP12Q6@T8EOQmC|@?!I^3_ z9?;beCF@YKGP%Xst*ftQ*R>l7y5m5jIh3p@{|JzdF2k@x$vTv*L&*xHSvrx)C{L${ zMv+4MeIrbm%3I z4t07+o}K?z@p2DWazg>8#^a58O{T`F}Y^0puf*EU=3MWGXYwH{O}&td;OC>0nf7nu)yBZ1SRq( zmt34k6|^fRwPo@r7r%VHL#Chy?HIilb5VYjK;yz9Q{uXo0g`gq`}qHjR5S-g0m%xo z(12Yy_5<`gChvyJLpJY*D?KLfMwXd(g9YZ@5CWk!2#fwcANijKB4!2ho>vvi`f$7n z)^s$GA;a@Zj4I*v4vJFAKz2|Rr-$UAC=QBpi9k^d)}q>fMwY%d|Ghs&lK#$($`cgE zHe~6G|NMDHVxYTZqB{W8AvX1b11|6k1jkHxhFRb$;M5XVk-clQ2wkRaCz_}f#!)h2g5ArkOF zx~W)`UIAFCGbnTh$om5ttTY;|-J;rj{3U}-U%YEu@DXx9lBFH>4EgnMCHs+H0c1Dd zy|rohALF&Ld=pVmn-nh8@eEjyVn?^u@4bWUJc#-U3XfEYAq-DeEDldrgrZ_jKsx;CN0u{PnltWL2QFdS0o0ED1~ zhk{nt!*BXThi3|}CHxc@o~rTZ;qUUk`0y{L-HTd##6zv^T8SpXS_?gRd=tb@;mZ`B^uCB0h{i^yZ$IPL+%Jofcm2;=4ta6O0t8z@GsxAjx`D67c z2^a#<>z%s34!=UIH+nD|HuHYf7e`=Ltim6+3WQNx1--&B?ckOxBZpre6mSBc;=9iQ z+@=}hH0(&9s!>M~COPoKT?piqW}^cntgx7nqrw)Kvw473iN5F8OTa?JJ9#2Y6$wMIkW_Q57QL%*;BLyeUxbo`lw24 zSTW}3i#x-Le7Fc>j7p&5U=8|Wwp|eA@s36=OM#f}QXcJsC~p@;dAlIW+XYeHE{O7W zL6o-(qTF0T5as3yQX1`R{!kBQL4B_x3!@!2F1)*RV(n;_*j_x<%)JjS#P8s)Mfe^4 z=f(K#`aRez?NhNR?Dbg^?V~+#rP|Rm;TZiE-7E-u49lTiIDF<5E#m0$vm5lsufL>~sfgonYH>cild@Hp+3loGA%2&ZvN zmaR^9cfy%Y79WW?g?c5zH6z*SzB{T<@#vE2_M%ks^j7VdY7Ap46e7N~ z^~pMYamtG5oJvV}+33^XUKL;27Do!kF@9=4mZt}cT`1VOvY&K4MYO#>i}k}^;Syv* zK~bXYZet^11w$JND;Sq;1(WTjg2{GPFf5=Ab_TEqdR#2iuWdg5Fnz0)pL?2ije3s} z)??;8aiiPxCu=>;ArIq2RYRT%%?MrXR=V2LU7EZi7^_GH#!v-)uo;l+l@%dXNQD$0 zGSCN9VW+qKRu8|sZp9eP4zQw3APH?|dT%v6AlYTY8Neqsq#acZ9>Sdg4~X>?L4)1i z+V6V!WelJd)lxjm9or*zO|M{pL62&>*IS*;!zAi!7QV7Q;}ytV=eQ*z6bzp^Lx$s) zh@sK#2WwPjaEl2=upM5c+!FVXc+u|In- zDBO`<))ybz(g+=e!X1UXR^7mDg}WqphvH@##UB5=Cij6(--m70@uTxD%z(bEFWzyqPS~RGaoHp_=ayc{%@DE56?Wz(Rj_kNHbmtN8T%7? zGt0O3X0Wa52|p50p(3*2SPQYeAU&t0yho=So)l);v1IWT`5o{Hf2;Tjt1g2hc^L#P zaG0*-Jn|$zHe8|83ZI*kN%o8L68z%*$_{&QZTL66*Vo~f{Xf1uZzrFo#zZY`aTEtzA~J(OL>A5jiHWr{L0qw%KeBw* zxkuXQ;^l-i>|lUDbkV{Tq)0^rxu>n2(Tc_onY{r-Y>19$@P3op)%hFY!CqTN*jiA^ zmA?9Z29+(ljGV60Va<)u^)ULYvl}v^;Y414UDT{>IS<3hEkh%VtvOm}p%NHkf(zR8 zUDBzc^ZE%!*{hTLd~~c#C^Mh=dBx8>!M^i5qO~uB?Ce45|Ku9N=o+bp!h~z9Ay;fQ zlx15DrQA&loSb1(;N&Qi0w>E%fx`lf(ln!3jCN7F)+DeaqDA0?7MUg&wS-~5geIvx z4+A^2PS_{5&d@%wTNL{=)1b~|EJRF!*kaJVp9>S@OyfLKIO0*QITuq(doGXKnm6eS zkq5G=3$#tA6>Jbs1uGb=@)>9rVVTdK*Rb0D{8m@Y>MDq$($zSRTCD507^{=oSX~fm zwEIt-{T%sa;X@8 ztd)|7_{GZ5iBWTxzJQ6!tV~p9Wuh`G6O~z+sLaYl zWo#fNsR?GzR#FqDn6i~)&MtjH0^^tDNL1%j$n&2q0uG}umxsY=uuCcr!wOK*?wm^aghaVb;)Cz{eWx z7b2Fa%}7?!xn>+q7a}%UNN`q8Ek|_Xl8Y1ft8>VlnkmR^QmPX9rdQ(h;v$je<4DjSi8<`R8^TwcKJ%;UJ*%e%kjcthR**YYXHSZMo!pq8E);1N7XgV z!~SML(dS`c4L64!uB}qM!K^-Sa=5m;W)Je-`w!=!3k8)bvt=mg@8DD5o)6Zw;|=6u zV<}!t-uz`Xo*oM1&5wH%-~3B_r^*rfg(9+1qD*B9WFS?gYsF)sl{Jv6Dq8&{FRRtb zq2OPUN#A|k8!lydAO)&cC&$y=TKsS+-$|>|$Qw|V23vl!pkMJ(@#*;9(8KCz914tX z)@0EEDd^S7QbKOEYXNU1*A2C8`0P>)R>nhE`l0?++fcmy>Z_r9qSa4A4HP`oNbN{%~kQJ=pPC|Di4;P;u{Ixdbd|qe~j8#IkPPRbY_hd|DIZ1QYb(Bm_;9qPJB$1 zIll|b92#UOzqx3TeUxrbuc>1d96dQ-*XLlj7d9>ZEU7Wy7HFuZh5>r>msF&vUsh;* zC}{ols#Mx0R^qTGIejCxm+;$-JAET*pkdo~`bOk@t|<9PuVD><^q|^KLCdA+(?+3F zqCI8bb0R6Ni!B(2Y`0g{7trijD+yaPzBLU_5T5zFI$lV+Mr};R;Fyf2>K^HXcY9SLj(l(k$JwVW`l0E0 zq_MhG&|TQ0kd~kBIhLGwgfTcLlF}WU?veh&>SQ@X13P*n?W^=&EIB!BV(%q;L&ss_ zUfhCxA>RjmVc3~hmHHHRaJLt}*3lm)P%^N-?6}6$i00}~ITTP{5;Bx@BO>38{Sw|a zG`Urs$mClk0E$vW!OKvPN5wc!ia7hhjV~0I%SY)ke@z|h6FLRfu&_!=!RRj&@vWp{ zaDsQrE*f8ZAXW8ne2d*)l^bF@npWG-gk^wmG7o1hY8`Ic<4LV&)e1kB(>F5Jgs%ixx?_hoduVZAA52DhiRf~?e=)yd(ee}&5r zf64fp`Xx|oThn+6)-U^{W4<&FHY#j2)zMfLQx&y)#4(Iq($rjQfyBF$)~g#Po`=sQ z{@yllg3+9V(#6Wv>8I8ketc^S~FUdaK$IW4NX^v?W=SF0eT6#yZn z1mrVv<)77t!WpAC0-)oOWG`FnOVI9MhWLScV|Y&92?mnmjVP5`m4EA&0}8qW(19RQ z+h`#r^Svt}fTTa7z_sxa+fwooojB69Rd{)P3t+F0G*bU0moKYRcY>CGvH715mljt@ z(Wlsl<6m#4;?%i8$jNWCzWVhpcjCaFpz>SlIhJr zh>-T{*!m(+jZ_FfFMMbC1^@NM@2Ov1_x@dj3Ji0^Za7(`vz1k<&7We+vJm~@TcEI1 zz84t)m%qqTl~ExF0c^_Sx?`9ix|AX*!S<#S*?7QhS@=%*-dC<4tb4N_CP~v=o_BBv zdxRJN9$g3&jo2|A@el}I)0<`{`V0QU1$7`%2Dug5*o+*Txnd_q2r8CY{qMr71y2*{ zc6ud;=$7nje><{IuOML*oLe0=yraqwAMu|3(t!lId^!Wfub{N$#F_2~UU~ungFHCYohJv;@h#wHG)%`2L z{p?>u|6=sU?!D<5``6J<9u2zX2k+~|hjZdg1><#KC?HSTx7qwt zMQr(l_Z?8W%X#50x+f84j3f^Q1YI*@ymJ5EMCk?p!#|fUZzgdjBunj4?>Cmn9kKRX zb>(~Uch#K{`JAGU6#3!Aq^@zdcf>O~P3fosuPd(&#CktoJN%}1a>?Dhs2d5Bj+zxR zaDbd%Qq>AqfgT?~{*W!d_j^sNZW;}S?|pOe^XjdYthJ!$fW-ihe1(!8XYA96@mCkG zjH7VY`_hY#`50h6mVuUEtP!CI(5RpqgCbOxVVCkww+V&9VS)9YKYQJ#nyLy2fwao(t1w4v|){tNJlQf5%(&uoH1F zl3BkvztDLlQox>F;KjP%15b6w&ze17mWEc=cnL0X^ddeNM=y(petiutmR*jhP=Kv7 z_2S(%ZSiuiLWk=qMAfp{(r*@dzFsQEQ-Rzb{ZB*rwQHFZYuRda=MLTzcJA;Lgaaiv zdShv%X2#APgJ;Bn(T(l8W;A&4zV&`~Ik|2S-8~Ds8(OIF-UwWjKf(B|>z`BEMdjwFSh#oIiLZ>Gtex{CbLg;1l^#v6 zK9m5o3quCV>FN>0u^Jgc45WblqRyQ1L4Ha$o}o{l*oTG?mB|ceMv{>EeKL9UAjUTc z4dqIIiO18;wY)dlT+64I>VZbiNm!i+2`k72MaH?HG`ppws)xE2+9g|!HSN+X_iy#Z z=hcBbc!KXt3a7T@&sxsv>^Ym*2Y}t$j|sK)S#|^&w*2>v79{3{e3e5u~+h5GmDdynZ**O>o798NN-er;^bZy?zgIvFf~ zoQ6(uziNxa-NrDl%Hsw7rgzZV|5Hmkb4am*ADr| zA`WUia#>jcR$;V4qS+_NRapQ_;i}&64XRZ)ZSrL>@Xkt=9=`WHF;nGAS54~uLq4hr z&u4)rc)m7p6hCm2$DlDD?{S~cM~*8er)8XreoP&Yzw1jS0OFuBEYmCK4$hvWi=Kj$ z)7qk@!4imot3-mKHD}lShe~;|Lh*LQSehHw{KKE=RIn4t z*73At8Gg9bWEpBWfCCoj0&>6tljuck0J9qr#AWR?iBx0$aFb;8zoO7OrcRHOlu-vP zaKHj4J)<^FT}vdBnR$22FqG6(G(cFu5I{j&XVO>oL1F<-?c6c1U(t*qVOX27K&L}W zMk^w|ycUl!02^&haZGr1MAr1xw;{W?yGH8to?2S`FuPOis{J?c+ z?1BKY)m=~o>!$}5NSk%Q0zJZVzyb}Da=-$cWyi!j&bkkzkR?4SwW_HfL#6W5}}SEYLs4p2b>Or7Z;$=J{Jk1U#W3GBBS7mzyBN(I6h{;A~fKUG6{S?Ynx z9Gu8s$2uBi#R`JUiV(hLMF2Z10@zs*z^2?R80DrC64j>}L2y|S%Gaz&VA~rC=WA9p zuW|eh zH?%EYc=Q_9fQ5N{{<&r%AiZxWD0F2JlksaKf2Gi`#PTU z~$nm0U-$5&Kl5W-FCzs<>Xs4F%X0kM4?_T@c3Dq)qk1V( zUad;Y%io4x1~wp1g~@tfi{`X`5C`%D?iEUW}Yb>-KEDY z*&nTDZusUeFuiK|Zf5qfz65xuhno)6;Ub!O&%Zh9% zN%5m$WqY-T6#xZDA7bFlM5kY>lS@8stio(iFIiNGo8E6J&maDO&IGGKX2!&x^Zddh zw0K8t9HhM09;jpPx#Vj-?!!l5-WjCPI%){>GIfDH$>k!}u?-*<1m|dFfV@>Punl3E z5S2$7kyJiX7WgA_ZB%T&ULl`%_v?)wbUHwRc;YJ4_NcGx&&Rni4G>p6(x{tqS$%cE zhHukuml7Yuk>)5VIY@JC$R2KmJ<`>eAX%>Ghxm$`NBi$=F-w~n|Jf(zj@N<+HjY5 zGknZh0?%Mj=w{Vbd+Qh&4Xe;spNu~%C0gmvOy@8Io`&EC)7%sJ_rH%6_x=79a5Nay zGwPPWP2*+!{K?uE9zqWO91v>R<6Oq$u9b#Xdq$^=29Xvl%rVi{|Ku7TV1e2MUjV<0{3p{yf7R2xmQd&7?%% z-QM0L20cup_VTZN<>98isn@=Op%>UUPF#!GSh&FNe;--Us1t3#)p8hiFwg)+6n4aWdD9CY)Ce$=#U zqZ$pw@w4C#)J%ZP{>|e_P(vr~BSiiV1wd;bX(Xyc6EdLHoi!oD7#;;4hkg|4@&yQ@ zxe!|V;?u#X##`8Eb5U>$pCP&)U9Q^q&O?ZMphh>sg*Z6 zHsU6W^G%1Y#yD!O!L&1oM6Er$-Jy;y!S&#L}>cror)`>htu4uuOA)h*<)6 zG_1x}+nH5S&>SyC*bQ2IYw_rk>3WjG{c8BuUt-%4)q{dqtgW$fXZ@bFA{c#4N(sE85dF&o(z+J(Ko)G`u3jhD^? zBqz?$#q}>GN-7$?01U?n)A-O6zH@7X`1UhmZ=6)k%u+R_t14_okHrh<96VV1eTfbA z2UPEu^;VAwxsBzr-l!C=*Vbp4a#ShkRcy42k>~J z2%%XB-T;ln*(^aL%?2`rFf{vVczod!_p6&Rw^3|+H0DxvHBQXIw=-kvTe?$(9z>en~< z#uo(9gaq}6K!}ZgZ=yT21#n*L->tI~IYobO60y#TgSTvm!K@fLSd3&2JT6R9<^dk= z71;8uyvbJB1wGlhU4m_9vQt)2KW7FHjrpw%K}_e<6I-MqLg;BC{ z6BBZj-fcjZfxV_BKT$%5flqq~rb5VYGm<>5(32u|f}V8F{b+A3!ZNLxpxvPuNR0Kp z5buj)zN`i6DQng${B|7TZ`Ut8{Pz5wzy15sn`8#J1p0W?0|pfrCZn3@kGdy@Wd*47`h4$+vY@bbmeiP zInvaeXb!A}!?sP!rQt+#oM_HW%9zt*+dc>2HO!|~=5fzab;i%1`r%-mr1?~d%$ygb z)+L6GLb4h2nUD%ID+mOA^h5Y&0ebB=aACANVYSxMqV~ z0(0mjkkQ=HN*5e=WH2|8rZPc0WoqmVrjN*>ZPM#zmNU8FxEst_!-hoXtj`F!5nPHE z51TXItMbt4ePZL4M|}VZ+!T0O?vz?A)k4Nr&AArgiE0`{I{eN-Rn9 z=bH06_^u#4p&~oi)YjHK&ZSc!=gyFoY6$Ct{Mv8*XJ)QRsH5*JA_6s;zW}+WvrjHP zQ}*bb(?f5_)0S3pMnAQLP>uHq^VzcZ{~u7?&PW3k(&Ru2qKTmJO+bue)CU06=a=SY z#l^)o16^2TmJ;=p2ouMv7+j*{ERfvh6;-U(WWyo zCICSNu#}1=i;qNrTY3fkV;*-UiC8KxH>)rikn16P!zLkIOX!`wx4xtia9Mb8=a&Lt z8IqYsmT0GWW^^(@Kr^6xfmE~zSlZF$Mc?6DXT-MxHrGlV{B&47h{3^GCX}(t$E_ec z32JYpFE;HJEKIY)P&hqIQ>(P0j_Ny_mSKRHQ??8|N$5_8kVgy#OS`?b-}UgzQP@mq z#^q!FOkl;;Neey%Y3o&5Qf~PZB$?(w9rZ+Q_HQN8pI(uW3eN0!rgCPd zKC>?l63p9hNnx8geOor$|6E*zj=4P2+n@r_&B*o#7FV|a=~_RAjkxh9g&XS&7fuFH z67Tf!SMzA%mR&0o;ltMPS-~O+;c9FH6~n3P>+nnS)fb0AjXeGzw<7DPbNetg+( zKE7<{$HxLbTW5gs*7Z@!Tl85cd}5JNWqkl$QKzn=N4G|*s9yQ6r|^x=sb*vFk4xdX zaxQp?dV5aIdQQ>4szBY=khyF&p+5Mf+IryQ!Bp|%!R|5i8;ijl3-vc}-w0AMZl}$O z{>~sTz<0(_Kq4R{zWv@zTaX|K=lW&7yPYvr{;%&bz8*G@%V z+iaiXYnv;`Ynv-XUfXPmytX}Nwy$lUimz?YW!yIU+V&av+V+^L*Cua>GE}KzwhaNQ zqfkY4n;UiPB@(uSn}`kz_7@&KyKWuE6MM|Q8?!etGS$Et(1*bp;9+nE*u{%8z~i(p zPzlI|F}JNI4A^2v0Qy#RC~zz7Q1mPDC*)pkNC5Wr>4pU8mJql^z*Bsy5B___x8ky$ zTjH`*);UQa$XRA)iOba!OLG0Hc_le!4%13%Mj4TUL+`q0A4b0P`VyEIj!iytoKvqFO^ z*zNTzV0DrA%2WH|57t42y3y!^>o-V2Lv$#xjh+vI@pOzApptT{Bl2=YUZ`R%3It~4 zoa(NjkS3<@>of_Mux?>4Uo&!z+<{#|bXQ2$)%Cv?OuNq!d4V(05qZ&&?e?}M#?#$3 zv{A)9LPJJFMhgX71X)kyA<;PfuCD0Qm_nFC8&i;uy;|R%9&RE%Tk#bIdba5-F%I39 zXzi{cKN;>3(!1I7h1rbsZem!sCED>ka#j62ySY}U)f#QcLBqGy60GO;t}#x}Cgk>^i9eBS$8lof#dl7O zL2r*;jxp0nSJt=aSG*YProM&Tlx7(up?ZWMy^zpjig&K5FVU%0{OiI0i^*dPi?=uM z-_=*^KarK`iM-B$PMsys7m}0s1b`-25;ONdq`GT`@@m0`f}%e15Z)bf%9Au~O9{yH zwSY2IQY?@CAe-&zt(DYEE{_+on9qbW$d#ct7AhY(nR3vdkZX_w#SXk2KZY$iNbC#_ z%=2PU)yXa@Ux)3;!|IE7bbXDSEY-At}T`Kef1mUAEvl08RDA_A><>6KjJ-PA-q zo#m^OUB~O>jFCf;GxSOh^a_*CfG${PTFw{~)n{{iy^_O}ujD#Tk+zyEy)%E})lh+>b$>r_`AMARL8*Cw~0zFxmSlGL|Lty({t2^!Jo>S$xm7)MN^?@q;ORx!No8 zrJn@5Rw-yJ{v>!O{+{~Pb#qqK!N(d33SC(%S!B43i5KXL_Y@kW7uAKT zAQRaYr0uRZ=1;q?XAe|~l$Q~DUtKZ0v|h=Lzw1x8FWd9twSJmasg&?D_X(d}kQCXAnzA{nAR6E0C`_Er;CDmPVeR#QBB0R^1?1xa(D% z4u!=|N~j5PoA982jMrxJO;6vm)qh~#Xi)C*?%s9l$)L2;dw=l(_4?Pn&3^$PH!0Y_ z74Rk?BCVU$WkWDmn?J?Iu9L%&Hhv3Uw0tixH?yIegHdXwQJN0TGk5B_*3JM$JBh=t zFWx#6xc##ttgEt=%<_k!d|Y>Y6A0{5O#9#7RATBmvi}R;Dc}3b^@DY9g3t>f<_bbx zjC)5TZ9DrqnD*?@KLOCdPLL}-57kP7hAbBvDmyyfm|r>L6XfZIXS}|&at5qdjNOH1 z&hyBeM~EPB2FDVbi4nSL5OF?`5JAc_;1hBn)q|ysWmf;YAP96Hs#kL0B9VRVZ%6j& z6%Y~uUC>d(J1WZDKKrEu2{OQS28agX<9mE~B;wzZ3Sxy9=NFD5Uq6eePRffFinn7+ zwgIH;EH+7dBGqF^0+^>jwCN?sTUd|@Q;^oPVA+M91y`(R!Q~F?S%d_j@GL-d6M7b4 z93-O>vSm7Icw_Sd$7*QgAgp|}Nsq!s-p!v+Yc>=BhH<1(*XpwR>Vgg5rcYE#d=SUQ zspd+zrQwX&_qO=pFT|$R=1Y4N%$j=tewejCD6`i4Q|LBIHyx@5k0d{c4+XqMbMf{m zyhC`5uxF*j@Xjwx^38^VwmAF@7*XwK(9MFM5xQKXH+Jt$dmdCzv&FuI7jR+pf%;S; zR(QLnaPrP>NnYaPbv5@G7 z9gB1+=nsC2(tPl~V}b5hUbqWA7Nhk;o2M$3(+Z`6E9g@Bm;WNA0wqB|#^uo)v1`Us zfife4Jg7Kt-Eu5|j3c`{i|0@0n>PRL`c&n~PNL&`@V*1eKAEKvMDM2AdUWr`7bx8I z%KdxUv3>aG(&NrVD+s2NJ?j0&5^+Lnzg1Vh7k^jXIX8yEbhPWJE3XZt-mwpdi%YFN0qFen}~Z&zO)a;Q`)ai{Sa3*%dak;ld8L6 zHLS}&m4=-A)A+IEhW3J|eh8nzT>O>$Cn&qa_u(7jQK&TfyrPpat-yQF=BveY7H_Ag zsjaFk=U~FHC&*Ox1ZllzyahWFksS%MqvzQLa5Tmn7*c}Dg)fbx;%VK6>;PFMx=`jpXuWb?)?*=wSG`dgOv9wIi36aV;qikMD*)?K$BCSL%vh$<2Vv z%$46-YFl2$HnDTVYUVI+fLwDw#ys=!7Tt}*&Ok|(u2<)k_^i(^@M38C_>Mno_Iz0y zT3O>IxWsWDd?G#0TQv0RYjCmba?JAvzF&oCc0getL}E!UnkuzJR3j8<#u9Zmh*s*-7++?*xK)PHmEU@%#-E;ZA1#aJs()w-CQ4;{`n?VdNjTIPy%eu z7!Q$xT@R0r)nJvwk9~dzEt6P7sV8=<#uaM~W!YLosX=C}CTCb{C`TDK~`>jWi1c)WW+W8#$wOs zQf~N}1w2)neXipj!aYj%$y1<08a^mu9u>IGQ5XzrB=knARC+S`PQC;h$fz0rJ_G1*2+BRVaWVmy)WE z22^NiYVu95#AP^u2o{F|^zpa);^VVL6%ncIcxO^ry(JGK8#dpDeCcaz=Xs^C?OKSd zM8;2O%DOAXnDKy`nv?bH;UMf&fIaeK0z5j)PDN@QgNHeED!3Ykksmj`gc{Ya_9p31 zgJveywplf0p?ZLnB-oT+p&;S>^UKn5L!=tu$nRya~HF++)1o`C{~%&_RdIVB>Z1*h;V4E@}6td4;mcKf*ZXtRnn z;`Z$OG42LpQm?E4GzRURBRdR+LVxe+HjGfMFWCFNLAC0pP5xUeVT3_NnI68Erf5c5 z9l~iau`V48*l0rURF-paue9U(vq-4s8Cl8AKwXe4T{X!U0afkKm9BY(o!<6aJ^a3Z zEjp0syP)zJzqYpxweNwOyti&Sz*j+h74OooPAkJ?n-(Tp`yo#K5Ty`~u=bLk{gIo~ z*~6GxI^;`{q6fyytvXT7tVn0lpF9 z!;EW9H8+F^$i%naf)Tx)xL9XH*hPPD5_XR+hpwgC=mF9n5)Z+X+3`dYvpKV;!=0I( z2t@~%=3QMD<^RdDFx5-1Akfe{6MYfMbjMpzQsNOInh|3eg)I_|JL;V$AVA@0!~1-#WE>&;ekEKC+Px z*H&dC!Fy$T>EYU{Tv8lZ$;S^9E-?wu^dl+~GyMq3Mn-41FJ6_lX)>IciOJ;8;jSg- zxQZgNWF_(5ND|dL!;;VqXGlhu%~ex)%~ex)&78vHo0KyrlC9%u$)5diDPj@a=m0Yg zFyjC-4lskndg?h^r@PUC;m4}VVAKbO7Nw!Lh1oT!v&Qmq*>2ii(fYBR? z5RgwrOx93<3^IDu5gZr%U?iXcl;HWr+nX7E9AKuBGC0k6qk^6&X9l;Y^$1Jq&FbWE)4!$!uBYJ_0ZlC=1bhGm4d~gzN5*`? zkC@MlPeQ!(nh}|}4z!&H@ z$cWfrKJ*x__{%HIxR6h?8~7+7S@?VgK(22p4PbH&4jf?SvjxmxQb1>dUSj&^KroI(JBJ#I%w|1%o5QprCtp?mR4`b#p`LlpBS@`s*q_TD`-7QW zOi@*)$ z8|o=e6lHw+oe$V9%S--UI9U$Q%Zhz6-dF8>Q_C&HXu@^RxO0eEtt zrb(bCP)T3fBBS8G2IYJ#713wKpkGWQ@mk2g8Yv5?8>1){o3B^MpbaS^584U9m!E*n zs&83w9j@!#ab3tdW2R9ohDC*F5F^bI5e_k1BEli|nAr-4nBzq+ z#hyD+IK(~!x%ure2i5DbR(3>hB1CLN)(90bJw2d91zfpXwZ-^%4QJP6t{YZ=w0k zMX`d!5=Pgf%T@c{c?dB=AUPX}26S*!0ZhtNiiV0-;YQnm@f3i*19(=@s(AcZfz;e@2e(hSVDTY`lmRGJR7Yp4Th{dy*`)?jq7BxU!cbB#4-VDD zK<6`-#&2;$2FG+X$}tzOh8b~;DBd*ie5!^?q5i z>TCk|jUVvJX?vXsz0<@V3EvzHbsLoKm8J!%dT?OabqSvILW5HQgw?Zp0K(1&j*59N zB5g5?W+vn?JGVES_u9U^oT$Qf0(WPzofj0RIwckr&(2ZKqVUv0g4fTy z)ub1h1xOWxC>BkV9Yposx;G(1(D#3=P%H+idiJPQj8A#}#&CSPUy6QM$(-EN%gPA{ zXLWGai4Jqrc!8pWaMl&A0DA9GGKbUmLcA{ya=MmSHnV1}!f(eR{&xMs!*9>;`P;uA zWDA+WEmR=I@$4F9VuUL^83~Firpz9S+R9-A*JLx@A0}}o(8fIfC+I>l!*B9;bSFBJ zOkteA#g}UFTWZhXxBG4Wmb>TSckmPb4iD7fcl1$+pJxgu_*?vS1Aa??-w4Xz(Rb@F zgSWu9z{C_nSh*0N=du(uB)h_C z)giN0B<18}RY-fqmGc1!|4#=AcaU%g33rfi2MKqOa0dx@kZ|ft31rCPf7&6vM_3W? zKcVc>%W6D56cE+lecYRf{rHkf45TVYXy{S|X)j=Y4f@A{Ra5{x7W4uGsSQwQDkbCw zyB6ptd0DLnmPv#Sk9)+;h|JZA~I_LR_QMPz164)k1D>e`%R^dr#Gj1A0jlGkT*&-x-n;L4ty$ zgjR@fxK?3S(bOZsKq^FMBFT}`bkHbCk^u#LqZ(otz3@aUD~2s(R-s|TmVs1g*mzCB zsXkbT+miJJ2AsDRvx#RJZrTIHA%fIA@o;T;YyAeWf*++@*VOUE(UbEvz_kigNVP!B zL`>EK6pW+RdKGM-)_M(|X`u8W9z(`kQbH$^QbN2Vp2}$`f`h_oC{i>HNK>k&Au7$v zNzqIr3u)X2R?abhO&#kKYNy5}n<^#ZlB`ldsSczpG**}$;9gA4>>xfzmC7*N&Tg;D z4bcswLUcrLS%9^O{p+S?T9t!@3%OFt%F$`Wu@n`*s6h{l0g}%G<1+PU3A-L~=caJg zc6(LqRQjtMG&O)xe_D!g@=OqSfy8T_K2zl;n=@mmrCQOb;-N@E#R>@(8QgOA6jTlp z?lcrXOASQ_2{&2>7bhg#K%o8nsUHs3IT^$+V#WhBwnAQQ6$9GSt;IL4dJ1qJt=*0p zyj1{PH5pLrQ#IJ7qPvvcg|v0-^P~HGyAO#c*k?5qGlDIx=BINgYsNj z(F2LImCP@@R6TK8mCK=ze!W=(jynU)&Rq~Fv=cKtI|Osb2_Y#+=0E;EFxix9bZBKL z;DCz0qlRQ7Mx<`9NCq773{p2@&?d+g3U$3h>^d33B$5l(X;ThK39LYJ!pRUC?F=VF zsLoM_Frao@@AXgm%#?i^VyKc5S@Z`|aET|W0`K;Yc(C*Y4J7g>mt3~sET=Viv1$#* zb$x|9dD#2h3Jf~@)g>k<=xD4u5sdqIwQtbdd|* zO$j7I{LhKn$>86K+F6-j1caTaoq!=Y8!%DSE_?qUkj1h|IXc}Htw%fnest8r!VRk^ zPRZp(-ArN?6H&0=Rv+A(0`>!hYm>rN2v5Z!6)0wff;78KP(;;N!0zx0MgIs=qVOC7 z@}=CjPzeFp%VnVw0{EB9LM4Ii!E%;?26I^`l(K97 zP!DLBzNSzpWxKaFg?Aq$g;I9(V3yZE)y(TZw2;@|wTRpQc`<&we!nEVf>0=#s|}Ju zDLdM;6u+2w!ZG?Sx=EpAk0FIpc68^p(A?}-NQ=4*SPTUt*01IS5!0lH8o;Md)c~;w z86qeSF9bYH6Dm?!elcNhh$Wwffk5twe6yDzkQ-J>S+R9eR_0zQ^=YP7%1U*$)c05k zTwz!*^-*)EnED=tT;&a>s+#&JQ#bWJDwT7KW3h8Ac9>dem+Ef$;$8s+w_QwdyO`j1 zF~RL(g4@Lew+lN%yRb7@202!C%OLkMfVAymg3T7kVrMz@r0Z#yuBTnPo_6Va+NJAh zm#(K>x*p4|#|CberG2+c*V8Utk7d$h^R~xS*!$4+9PwcIbpJ2(0KK<9>C3{DU3P_k zj@?!sr)s~zoVwMVdc!8;44CaNY3czQ)%@9#ft;zAzsLzdsGbfViJS>lK2n9QB6~yL zI9U}B3Qm?;(bD56aD4?+u!hrN%5Q}$hwhcb*k4pk0S;pi(u63H!^OoEfZ>jzKzR2~ z`hxfW5yQpKpwJmW+6de{rO{yR7S-nCF9XF89BBcTLUiem{h*>t&*e~;o-0uEccyXas;j{oZ&G-vj%O+_)u~@9qN0M={YkKEl{y&UFGUpcrOre_IEuig zv-W1;q9F}IU%aQ#plRN;bcB8tlV#V@h+RQOuVuI^NZVa+%%65&&mQFUD=$;{FyJfk zJTI+Ra^vs%)9ss_1lSix!qIK`esG41x1YW(o9%xtE}x9cBfSkOu-hM4T-p97L=SMl ziZNuC^lVMwGvoUg?%1C_7!>ZvE)!}yXb2SUDBQK`25u|dB_n1OH_IsY_}?{UTafU# z&6INCWUzmW>h$rKz2OlL<=?e35mu@?)+tC6^gmXQZQ$xUb$uOvS-$xWVUQxr0|}%; zmItm_mIqlbG+@YyvgJz08}n6o#wQ%FnjJl_FO`yzGZ-Vm0=o3Rc*oH?lxi%ax%#BU zV`;sT1IS1!*dMpb*U%MAujC3lbCW7qDUlyMG*+aAR7&W*R=vnMi<^USiGXBw_P&mJV-sb6hU zcy(<=X`g=Zb9=vTR$07+=#WsRnzEwp^HI_G6(bex`Kc=!KV$Xq2-Z|5g#4bs8mBKI9m<5VymGn+iEC9YO0}} zVXL7WWvZbpvl?OnMro5mMbQmSdWKG4x(2W}qI9iEU`IrYzy~d|jz%je-q%<{)GJ)> zHe5~W)!sWQ^@_{3UdeV-uVg#x6&7IeIs-I&?GQ<`Z=wm7jlqe+ z8|ZW74chb@U~B|aKplyQDG*x>tH8zvOY-)W(tJP`X8eFyT9(PID0?8)QQ4(ea_C*9 zW*yxc@rd*a-T=f&N9*)m&7;zu%cHjD@j$vrL)&Co_h_3=E7%~Oin#??<)HwryE7rr zF@+-s%X}LAjW}M~9C?|QTP9UUUy9^qV_;O#F9InU_!0|TIdyg#>?7=M!$oag|?A%^GZ<}Mr$W4 z0|-pCaK<{wH$D8{)~C?EE}2op#RVlgFJqe7ObJGG4y`9_$3ADC3XDuBLa@+;s3yrD zfkT1<7?0` z=E74o{yhAR48(LJhU0`r{JETsTrOizJ&=(uIvjo?B5etsFUgT8V&nGt&w5bmrfM(3 zsoKM>unFRIXc*A6n4ri=ZPsxF7=$S_;M~M8>;yt2^ni6dY{p;;iI8=Nzxi+1r)CpT zIqdY);6FS4G&l2{Vm-JV_0w#`6c!dCWV&2>CK~Jgig{WPMvakv8c{%ozr+LNVX*de z&BO!baoPbaC0g0TPv_!I8+AqSr?tB?sr!#dM7!ExN@WFMf_*;11QM^c5!dM=vt~ZL zE-%xw72PsQziq#>oG?*_izQ&IOP(WEJh*nM`<3|AQuHf6YvC>zBF0o8hR3h~ITs?n zP$A-xktN1iIkg zmlu8b-bi0@0zfv)m-$G8l|;&!1b{w^So}z`l@)!u?sBG3y-8!rL{q)@R;%vV>AXq| ze<0P$0K#LEY`G{jP^!#;RJ;)O3)2d&FJYAuAY`oiEjyIn6`Bqtdpb z@RjWuuke*emaI=jrzBiLnoCH#5Lx|B^aV*-96D`LGNEW@oMocX&Ny?$rUS{chDdZ-nKTcXo+qxDC>J4u61wb=mY?o9mYjHm=$Sa3mnzW>L|C29 zOTq`PhB`6>sgKg*={0pCD0Ol^W~;F^Qu)wjON;FImxPv`d8(!z`cu^C8@-VdeWK?S zic1+OoRVqMt>bCvqJWuWCS4RG95v>5G>T-?SV~Lmh&EnpDM3}J?p+Ij%HX3potHxK z_QH$vn;X{r!=Le!IGq=#^Ky1NFC*a69}1`e(JXFA1O8pj9|+HxKUqxB8u&7jAS>|Y z{ST?`T2T#Iu%V!+C;?PuqUXd_!;jYaBHljQ>^CC+;GAI2x#7(Vpn@c5x?Im8e{f)) zXPL^dU0~3FItzMc^lseEpiX5u2O!RLxSCwy-BkJIdA=a0&swGhQ{n*g$UVk{N8a3C zujG(^B&i2ep{h&o%%6CFl%PRKBkT?=5D=}o^3Q4ue19&F-Uxt>$2|0Gu`l5TTo`?z zK9z_S-mWQ}yfd2{I^Kv}B=fy1@ul=f6u34%Vq3~dzf2|l z@~MlhoIQk`X=`YsUTo~+G~sg}5XH*LW-q^S(XMMgs1thmR_HnYynYh9d<1@;3gPF4 z?+m}-zrOfA^{eaNziUu|p^DgzbChWrek)lTO4O$F56l}4%3a>wyKcqg_D=8p#Rt^u zU-vfu#Q`N6CXf5WZ(=qYa1Y#&Vm4lW_}(`czm$0P9-5JnB&5`yOpZ3ISffCS4YQ^! z=f+poVvpCTaig-!i|du#_#2C{&yVi&?Y@rZJh@``douA2Bc)<-y^`xFHe#P@)Id?W zri$y8+)#jB@#wC&*#$7lCS?>_YV)W5|MtE$IIikiv(KX+>Tb)DeB>96F%Snbcqr00 zsd5H|Vy5O62~NnZl&8oK50%{MqAvGGid2OuSIVXuTOqRSK-=^<0vyvejw54Xlt7G$ zgwq(V(_CvDK@5>&MF>NT9A|t57C`~W%=fLm&*_KW1{`FLUDZ0zefDGRwbovHeQR-M zDXcL-%r~H+`CH)uFqwQUixEcw$xKu)%vPPyUWK@L`)klUlI{(t%CAT40EB+}bwK)^ zrvES^WokY|2`IV%Q!0pzSuvG&#xuP+AD%kVjWsR@J=qHIg zq$eBnbYI<}7=F{n0 z?&_n+k3a%EA7Kh85uW=#L`d(Gpuk+H&ul#&YU+rh^V82*X!M~_p3rATGZ-&oL1u?S zWH-K20vuH*38Xm?fsj2`a!;M0H-X4*sp1!P?wUg(XJvcU4jY3FBAD!8cSQs_&&k84 z)UYk*94~<~){`-ivAJWYN9(U6&fNSWbQWmWduEJzQwxV`Q+e5)7*qqI_VUS50KA;YZ{)svFQ7-7~Q5Omf(eapdB zdqTNqtT#U`BCjg~k>`?{%fuE#WFct^oTVN$PS)lKB4789)%jcTcg@XHOS}>-W#HBM zb+&NtZ81oyx8{Fy7ZqG#hecoTxQ;xLzU1*{13>;;(+?PcoTo48_*&hXYX@zLw0*{_ zt4Xy8&jF4Rt4ikfE0p-@JMe}+eqr9~AV%ySl_X{efoWPEii@m5%2|xG5>;}HIhWK~ zRN~3E<~@H^xAEKC2W_{c(|17L>QWD`+QI_wzn-1kX70Dpe{@7g9d??#ZbXm*2xwGw z*QKZ`8X4oDND~NvT>b4^PfWrA9&LV2#qzj!&PbdTM1A~9l~rS^oOtsoz8EYSR*A<(PWMH*}d<0J*XY%2-`2|&v@aWS|wstaXhw$L~N3Q1%XXe#Zb^`9G%fr`|Z zBVjmVvp_M?5~j`cqNhX@5h0)ni0HN3@hQA2nj|Tx7|#L>wis-PCvH3oId;8^c;W^d zk~duMB6(_*uqu?|V8dF9m&8-SoK!I?PObvZ!V@F+1?zTgGzM*vB>N04NgBh=LBu?u z4St#>J{iZMKQUHPlyi>*lYmtjY}N`8Ff-hsmYrY&jCTutbjQUQ)~9Bh0nAK{~$ znu5eYff&mTOen@Uzl|h$;(17|azTW{xk!hy5tzekK?wJqka+1N{*RuJwDL}}BSsNb z5=TeRMv0>X#=%PPNkkS#%Gom@@xaZ{urmd{EqCi};A#j}j!+XsEdMcs0!ujJ<}!l|ufWWQW!oxtuUK zFJ_@y0u~jp$D6SPbCt7>uXP)1uH9sbDuxsZ(f(VhXnq8dsS=~6c|rXY!Iaex6r+{L zj%JX|UDuj48l!sKEY^=PuCUhNphT1F{_!tyxA#upftGozj6Lkc`>&5Wy)gbCv80=@ zyvI^uz3L`)0{zpAdxiNS#z7Rb(`4Vg@cB)aUVdQp2wieQgbO8mrMkQs%TQNsU+*rD zGuAknU7%c0F`0yNQ9vkB#0-_z<$uN2>1CNZ0hc-VAYELmc#CaxVS76)z1sXTm1Hc7 zlN@9%JSBV=gy$ip?Ft`&7^QU;HGNs=)s=)8hacLn1z{|hsvPWyMN71O0suTA#}ilE zmt$AR@x&E!@`fwqzycpw-~$VMV1W-Ta6JZx zqup;@jM$)bMasutm`O*X?my1ZPDH)26X9XFIEmBcn+u>9)?Z_UdShj7PjoCwxn78# zza2KkSmyQ_8H;as@4S@mO>nvC3~q(LU3-<3`DhGw13uRq>#?@Oh3t`S^)29IEIJm3eakjGn40 z?k5Vy;A?YrRi3CR81$uTc|*DZj)L?*ABL|2s|;tV^wZ+}P>(b135GzVEJ?afm4bJE zRt4`besJ(={k^V8+4q#cnB5i0SZq4hS}@qKUi@Ab-=m`#zNb9J&~ZPj8ixBAYcY7W zuI%C28Hnx;b@O7Eq}wTZ26V+fh#_t8ib0v&6JVT9nuvk8+SenTI!0Fb*-w&^F||VT>lpy zDD7{U{kb?FsIUl1^1L3EG@-i#cP~i{Q zJ#)AL?ttU@9I1-AACxZSbkuz?1WJ@@s6L>xV4GQ>p9rX2@Q{`@l{AL?>x4uP8la}psYFJSes_FSI1dLx z@jGE5YR-b>-G}5WnmV`7C>Z1_pmD`kz(D9iDz#S(1YFmf2a22G8IM8fS(=Y;4GmeG zL7-_LSl|N-L{z;si$qnywdg`WyhU#!*T?lHayg$8I#ztjexfF0k*pw;6lHt#NooZ7 zx0z+KfF*fe&j_-;B?_-+zWL{B79^rk%}bdRS!e|mQ{co9Q=`u@k)m5eDM~$|Z!Lm3 zr_}oy3)Vo2Ihl@ILs{q~QtG23gWJq9C5II+>lIJYse}!H5x8yffdyI#sni=VI*52{ z^w`hCIt0e`290IzfNNbys}1@1U!&N;uO>YWzBNlJrlP!@2#Y$SMh1$6t8@rqfXR(c z7#QuYNSTBr^|hI0YKUT*TsK;`U7G3{sO2!;{hdPT9%Pq<-`TQQIb^1 zijq{XH%O3JZ{U-YUvK!p0v}i~o{~>HeOCm|ewD7KaJm^cpTi_^2TlF%#@%z)n(s^o zXOLe6l8;&R=2Q+Tvek!NX(jIdiQy9?9fokfhyv*UomsiNfPKdjkNrepfDE(T=Lq** z&mB1Ot4sw}Ao(Hr@cWq|5+DcopsG#CgvK-&$gm$MC*wK977{1o3z*vrw5~(NxsetjW;6k!?(JP4L1+k-`se` z*pLI%hFi~NwK?ELAY5}T9QLOx76HS^rp@#^D0khujmCjczR}1n&wmN|GH?NYDs+XD`&btfD0v-w*J(_@7}lhTbhryAowEa@!qR20~i5AUC%wtB!dOxd;~B=B<`(n8yRd()X_-*&FT8wtwZEP zE=XhoYxhJ%g(-gC#i&5I!Z8P25$XsrDZPG{5pWVe%ZQ(4#pVu1V_tDJC<~{WV9y z);3;oG+Y5vgIR@N4JR+!XN-+bD*=DSU5m>{kMf#PHsl9F9P)e@mR1#meqK_aObspu zns^<)p+V3Dyr4l7xo7l zLRFw~sH%v_7a)j=h0xF$91BJ@+`vhjs3^1S8)M#k?OtQI1#D3*REUoV0QDgNp7M+6 zRxcJ*1qu}l>OeTDlbg&-)*V@hb<|XiYekq3#+RSa<6DC3f!zlRmT($&(HJ-$kUn3Z z1I{$%N=y>C1F{utfr8eLpA`jG$LR>Wfi9jJC!33l$_Y3Z1SBU&)5TQ>LOG+tXOfU13p)qUcuEmMlSp_2G!mm|1&BtP z3}i?r!h!ZzG>R7!;T-*M`4GhPi!;VV4vm8InIKJ0DycKnz<{p5Dfp1|PV?a#P5kb< z5%GE4JE{U3uNV^=RLqT|lsYdv&dDDb`Ou3qj)td#41zLDpi$9uU6%Id;b=beP1PA= zK!rXOIFL%21M?h`&U4pp(&(V^^Xh;;#|IA$S*eK>`0Nv%{fFMa>vw|d@vK%GwXQiKr$4|s_KJ3^Ra3UTBZBy%` z`Y)s{IzAUY(cw3){KvGDxVZEvC-D+bF>wLJs#&>ot*vehx75;kw|gj}oNY_o4aEgl zdBqD*LVNp<;=I*nmeoUhDk+B`dS1W&g9+Wgdt;@p{#YoohDEn}sW;mPH-7qBikF>_ zZS%2hKDN!rw)xmLAKO+YEyPDzOG6*q2HEao+k9-BD0x)P#G8L>s1ZKig4=pDR-*bT z%%7-R1RlU2oR~xH4~{7{d4F)i7_vweK!gck3?xDLo41)BDofR+BtpLjFd;PJ%2F8N z^9F#W%qbw4vog2%k?N>cfPHM6p#d7=%Rd}70@a9?M2rDEu#G@iog@anj$3x=E!(}- z`kUEf-a}Mj+%v1wZz6K7D$)$AlSJXC_E`5E#2>BzI$*p!#Hwc6#84B(1UQw4VtU@l z0+vf^!cs?5({r;525A_(Go)cqM-f9Upcw8r`AKt1rxH~KAKT_*+sKR=f`Fj~5Q2aZ zc?bp<$4AUH!gzTsw+uculI8l?HXqw2c|e#5WZ^=p4GJ45p$bM~1A#s)6XM!ptiGAOr*ze!5Dda)G3>a2h$i#r#K8A>yPcISX?4jBq2L2OCki!;~OhHME)4AwD>B zGOVle+$^sUZIkQ_meGY)2NZ-^K2O0f@Z}TXEW)%1d|Bc`fCZK1Hq-E0Y6v<$hWdof zkBZ52=cOfjuV7on;;FE$y7N@*i0=Q23ZEOc?GpfA!+u(EANK=how1Uq@93@;|Ly}J zohs-fLuySkY!omt^k-73KPHAi=?p88j@z@f=f}!!={WkK|s~!;KvnGQj#cy zo{x`Mh)2K4a<}B85}|kMu#=A`oB|7X`u&Uma*TzffPchzp4KRb0f2W~^xS$Jxq`%< zCu4O*09qS?J8v`ln9PVynxW)d^8xc3ErrJXZXdj z&*9_TYmUfb;kzE6f7jyyj z7TKw~wx*JJS4B6+yW@DJLiEy))RuttQ*}Z`5G>X&oIDS{rjvIroydEXq35UM9*Q{e zg06A3&kxNZRN;NZ4O)N`tL{Qz#&N>{d0}#p3IZv$GS();4|B8f;}vZNd0}+~myqW; zjwBJq2ri|;S&)iP!xt-qvq-*I9eky%pknY9XIYyGrV0k(76LDD+}0&d6qN+}&v>y! zGFG4Y2@UT7AXQR_>EJwDhE3Sb7Y9@ZPx$dq`zROFy8##h1l(XYwR!n_zUxdBH{)Hf zNXR&3kLrpXK!O?vumxZiT59Da^O#w@(|*tjI8OH)Hhu@!gzyXa5`Nn|BfQ>I#p^>+ zUf%%k0k@3yM`skv=zhK$_nY3CiTnD4nbozpg!n)uPyKQh%X-;iS*PoH``$VD?cOz) z*MB_^zpZc2FP71sPL#Y&YIFwoF2HZ*rAta`^5(*lr+#=DpW1mjC>Qxfb<_qDv>%H> zF!(|Uk(OGhaNi{XWQ72-LI7?ZIR5T49R92f|K^`n zF`A80quM9!t|@7^zCOiv>k_2hx`dK;>nkPgcAuGSyWLu`-R`@U?RM+HcDv7%wVR=O zj9De^o>kIreSM1U)+I>0bqOWy)>lf}?LITvcDuD=yWMvy+wInY?RK9jwi_10J`-$G zS@k5&LdT7}EjbufX(&cfAmBH}2vB2$G6RANU@3S`W$u?KNtho-piZ6Ovq^lFw!BDE zK30rmz*rB?;!P58Eu?OC+;||Qsss%#`LnA2UoZp|JhGDDh6gP$vdm39>}2+7b(!-` zUq%PxP(OX|9QQK$r1vyfBQyIYon_dA9hk3_=JCOmR8Sc?!IGQ?* znt1ATdsR|F9LvCu1`rFA|NoKiG3JP#4ZZu)TQ)L@@38Qf{G-prCtFvCyvkOy zu81Ou#MQWlPzUzbS7#6jYNYySw!rJyt-d;0j16rmMz*7>@k?=!&!QCd&1(5% z!PsOlS-G@Qo<-JGJd9eRB3pIGsW&9wIad&!!Ey3vLo)W6=bkfr^Eu{rZhbi+eUaa5 zqA%8~p;RZETQ^qWo)Ip&XVmC>0|N%`g-Y**^gXl`nO`5$og;mZ4E5-IBuy=RMFxM* z*NcPCV|Vc7x*mMF&cVk4hHZz9{#NuT{Vn-fD~4FosH#M#pa*H!>sv}lRGs|iAAq<& z5M@I$peWQU;TsP@w;yO)Ps!R>W~kfZMJ}6+sSh$oaSv*@+hy<|gAf%2=a3eXW*F=o zOZ_4{QR9r@1sG>61wgu85$2?K4In&r?H@drb5_#x$&**@f%DVRQ_ ze6E9Z&BY8?Vfc_|wB19>FPc4|dto9gjyQQH+5m8XZ{*WqeE(jtcrgN{Mm|urZ^sKb zFyuh(G=e4ej9`g9LtmewJwulu_6%J@i9JJKDY0j`&rH^y;ns@t*nPLsp5fMk^Vof+ zXwN{i5t!LEZ24w8ZLV|;WVBjO-dyRL%*eJ~lNoK+%3P_hlnl4~%w&h#trdsceYbMB z-8%SYyKHxn%*AX6H4y>}q8l1LMK>5BWjAA>Q@SBWd4LO0CQ+#k(I;QQ z67oDOi6&tt%wT;w&Z=pp7tXEB&}`em59H+s^6~?D`GLIrKwf?zFDCGoxCzAY=i_Uu zv(c7X0^R*UUSS5Jf|Y=A0VEXE3o})~Dhc+3fK_7!`$3GMfiUx`<7Jgs9Z`c+yZ{NR zz$m4NsW2YKUyX+QK?xP0lD}h&PJ|gSg$cVe5{y7+X)YMZj|aVzuXYB@VyLJVl;_)K zC9s|j;BkrUfanRpC}ds=@%=Wl?0r9w*Cow`6y#iPPHC)A?Niv zIW))l@a%#?VGcgwMdjRz3bj{}Gh`l(jY4LOug$^U`^ZHIrA9~sgsQJgQp&4NtplOJUnVVmP>+r+s47Rjhmc#aIeDIret_1%y z0#P|mAb616c|9jdpw%sPl1TqCl7D`NB{y`2#pR9R);FrW<5!Yhn;qs{E! z5tTQFa^d3V)ky+dzTy)&irZ=;_u8z=(vXur$(;1R2wDMT=gI8|rO|0{{mX^6eGi+x zA7aF^aI#~yk)xX|9Bsv27US6*85zQC_~i&jE_P8e_d|Qv8Uwi%e`voJ{FCwXMN`hG zgTd;t)2$8^TdRtFH5|37Ng!R3&fwm3jdUXjq1cV;B+*ifTXwwUmK}9(y)TJjdjo2``qPLl$FSZtT^lU@j@$q=<(mq-9?%{3;@HMfuZ-1 zEU93e3Su@8Ok^fE&fRtCscH#il*0PtCo@OHF&aWJtG!!7q^?LZ{q<*^m+kb|(_ijK z-ZLai2Ysnbl`G+|Ic5yGp3SvRSsYf}|si&L*R2e+@$M zNsx88R)0NW2hz{YO1};dYzG0iBW56-xd~UC8iditH+I+9Gj8ofVDYQ^B4GFGG$?`rhCU$x*T24 z;r*Zfa{ktXS9Mn(ML;irn2QK?VeT!p6DA;;;O;*FM< zW)8?#WV1S47;N80ygr0+=M+QA^3; z26iMt9qcbxv*59(S@6WwEaV|qv+x3dLbE`i8&9)9#6d<>g11ad6+f&SaJ-64E@PqD znhc5BvSv|6^w@x5^wlb=&Kv(Qd&8GV)Va{9APSdMt592NCd9rs2fKeNJS~>YJ!*ih z87EhPt-WLDvyLAjZ=;B5FKh4sN!{ir>N7WqnF-O1u^eJdWBmt{E*I+GdD#fvtjBH+ zLeBsbReA;#FX|bd$~Aa(`<|$2W^RISRAt-i0xPwmvsJZsG z9WuZOrm?>U!#>#oE(sinl;d7Q*Ioa6x6StjyNywz362*V5Q2q)ZYWqpq(C109=*Bt zw&S*vE6>~liPad!YU#nzdgWaMy@C={ul)Oel3qb3K`X%H!K-m=++OKyScMiA0XMe9-0jSWcOa&Xli3br>tEfROU zu!4wG@~CmL7M%T2W8FVi=WoT|H8)Q!!eF#ibX4cp*&;VaMjRX;g1j=d-vT>=tr$W$ zmo{_FX8MxHn+;@dzcu}UA*UZKiF;Ktw;$b;d)7F82TIk)FU*@#R(H&6N{Q;^&!UHn zy2tVJ{8cKCyIbJtJD@Y@!Vj*h$m|Z?hb=@^m}zvMK_vsNLVi{oVpe6E7MYX7w#wcI z7Yggl+*DL|!C=1;9Q6tg;j7Ipum(_~$ zi4$BI=*6ZwNlYzjB<2HESFriwe8B3eI3LDKU|+>)Ia{V9P>F)~gF4sFK%hT2pxY>I zZaMaH02zd8qa|mX*-Q;xsoX-PFN-St+UEP36FbR?L?l^+KC)fJHY|96NnA1on(-z9%4`P2UqOvGm6sSzF-|Bz@ROX&&{U} z{@onJik?&WJ1)ksJ~i76VCqAK`fQ3D=!oqD!GS>%>C}%WBssCcL?+rxy2uDk+h=7j*AI zuq&P{l(Amv)QU1Te+2m0%{>G2T>ib@HEIlmlMM5Kb@9c?y3@<=i6{dPhG0)WV}XSpufi!; zZTqbjawg%1LMQH8jVCTQlw+403I%CyC~vsjP@dA

#tBaX@YeiqhP0{BS1_Lz|42 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 Cw99J% literal 0 HcmV?d00001 diff --git a/testdata/drc/drcSuiteTests_au4.gds b/testdata/drc/drcSuiteTests_au4.gds new file mode 100644 index 0000000000000000000000000000000000000000..e59e510e5ca4d3b2e5d9414e990f1e6f21b261ac GIT binary patch literal 38991 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