From 255d30b0ca6aec8100405b382f66a42fc4e59e1c Mon Sep 17 00:00:00 2001 From: Andrew Bell Date: Fri, 23 Feb 2024 10:31:20 -0500 Subject: [PATCH] Improve handling of dimensions for LAS input (#158) * Rework preflight file-scanning. * Add test for issue 155. * Fix size calc. * Use filesystem::path. * More filesystem::path nonsense. * Close files before we try to delete them cause Windows. --- epf/Epf.cpp | 453 ++++++++++++++++++++++------------------ epf/Epf.hpp | 18 +- epf/EpfTypes.hpp | 2 + lazperf/readers.cpp | 12 ++ lazperf/readers.hpp | 3 + lazperf/utils.hpp | 24 ++- test/Tests.cpp | 187 ++++++++++++----- test/data/eb.laz | Bin 0 -> 70366 bytes untwine/FileDimInfo.hpp | 7 +- 9 files changed, 442 insertions(+), 264 deletions(-) create mode 100644 test/data/eb.laz diff --git a/epf/Epf.cpp b/epf/Epf.cpp index 285eaf7..1691ed5 100644 --- a/epf/Epf.cpp +++ b/epf/Epf.cpp @@ -26,33 +26,13 @@ #include #include #include +#include +#include #include #include #include #include -namespace -{ - -pdal::Dimension::Type getDimensionType(const std::string& name) -{ - using namespace pdal; - - if (name == untwine::UntwineBitsDimName) - return untwine::UntwineBitsType; - - Dimension::Type type = Dimension::Type::Double; - try - { - type = Dimension::defaultType(Dimension::id(name)); - } - catch (pdal_error&) - {} - - return type; -} - -} // unnamed namespace namespace untwine { @@ -77,12 +57,27 @@ void Epf::run(ProgressWriter& progress) m_grid.setCubic(m_b.opts.doCube); - // Create the file infos. As each info is created, the N x N x N grid is expanded to - // hold all the points. If the number of points seems too large, N is expanded to N + 1. - // The correct N is often wrong, especially for some areas where things are more dense. + // Create the file infos. std::vector fileInfos; - point_count_t totalPoints = createFileInfo(m_b.opts.inputFiles, m_b.opts.dimNames, fileInfos); + createFileInfos(m_b.opts.inputFiles, fileInfos); + + pdal::PointLayout outputLayout; + + filterDims(fileInfos, m_b.opts.dimNames); + determineDims(fileInfos, outputLayout); + determineOffset(fileInfos); + m_b.srs = determineSrs(fileInfos); + + // Setup grid and point count. For each file info the N x N x N grid is expanded to + // hold all the points. If the number of points seems too large, N is expanded to N + 1. + // The correct N is often wrong, especially for some areas where things are more dense. + PointCount totalPoints = 0; + for (const FileInfo& info : fileInfos) + { + m_grid.expand(info.bounds, info.numPoints); + totalPoints += info.numPoints; + } if (m_b.opts.level != -1) m_grid.resetLevel(m_b.opts.level); @@ -90,27 +85,7 @@ void Epf::run(ProgressWriter& progress) if (fileInfos.size() > m_b.opts.fileLimit) fileInfos.resize(m_b.opts.fileLimit); - // Stick all the dimension names from each input file in a set. - std::unordered_set allDimNames; - for (const FileInfo& fi : fileInfos) - for (const FileDimInfo& fdi : fi.dimInfo) - allDimNames.insert(fdi.name); - - // Create an OUTPUT layout. - // Register the dimensions, either as the default type or double if we don't know - // what it is. - PointLayoutPtr layout(new PointLayout()); - for (std::string dimName : allDimNames) - { - // If this is a "bit" dimension, don't add it, but instead register the proxy - // untwine bits dimension. - if (isUntwineBitsDim(dimName)) - dimName = UntwineBitsDimName; - layout->registerOrAssignDim(dimName, getDimensionType(dimName)); - } - layout->finalize(); - - // Fill in dim info now that the layout is finalized. + // Fill in dim info. for (FileInfo& fi : fileInfos) { for (FileDimInfo& di : fi.dimInfo) @@ -120,20 +95,20 @@ void Epf::run(ProgressWriter& progress) if (bitPos != -1) { di.type = UntwineBitsType; - di.offset = layout->dimOffset(layout->findDim(UntwineBitsDimName)); + di.offset = outputLayout.dimOffset(outputLayout.findDim(UntwineBitsDimName)); di.shift = bitPos; } else { - Dimension::Id dim = layout->findDim(di.name); - di.type = layout->dimType(dim); - di.offset = layout->dimOffset(dim); + Dimension::Id dim = outputLayout.findDim(di.name); + di.type = outputLayout.dimType(dim); + di.offset = outputLayout.dimOffset(dim); } } } // Make a writer with NumWriters threads. - m_writer.reset(new Writer(m_b.opts.tempDir, NumWriters, layout->pointSize())); + m_writer.reset(new Writer(m_b.opts.tempDir, NumWriters, outputLayout.pointSize())); // Sort file infos so the largest files come first. This helps to make sure we don't delay // processing big files that take the longest (use threads more efficiently). @@ -146,7 +121,7 @@ void Epf::run(ProgressWriter& progress) m_pool.trap(true, "Unknown error in FileProcessor"); for (const FileInfo& fi : fileInfos) { - int pointSize = layout->pointSize(); + int pointSize = outputLayout.pointSize(); m_pool.add([&fi, &progress, pointSize, this]() { FileProcessor fp(fi, pointSize, m_grid, m_writer.get(), progress); @@ -161,7 +136,7 @@ void Epf::run(ProgressWriter& progress) m_writer->stop(); // If the FileProcessors had an error, throw. - std::vector errors = m_pool.clearErrors(); + StringList errors = m_pool.clearErrors(); if (errors.size()) throw FatalError(errors.front()); @@ -179,13 +154,13 @@ void Epf::run(ProgressWriter& progress) // Make a new writer since we stopped the old one. Could restart, but why bother with // extra code... - m_writer.reset(new Writer(m_b.opts.tempDir, 4, layout->pointSize())); + m_writer.reset(new Writer(m_b.opts.tempDir, 4, outputLayout.pointSize())); m_pool.trap(true, "Unknown error in Reprocessor"); for (auto& t : totals) { VoxelKey key = t.first; int numPoints = t.second; - int pointSize = layout->pointSize(); + int pointSize = outputLayout.pointSize(); std::string tempDir = m_b.opts.tempDir; // Create a reprocessor thread. Note that the grid is copied by value and @@ -205,40 +180,40 @@ void Epf::run(ProgressWriter& progress) m_writer->stop(); - fillMetadata(layout); + fillMetadata(outputLayout); } -void Epf::fillMetadata(const pdal::PointLayoutPtr layout) +void Epf::fillMetadata(const pdal::PointLayout& layout) { using namespace pdal; // Info to be passed to sampler. m_b.bounds = m_grid.processingBounds(); m_b.trueBounds = m_grid.conformingBounds(); - if (m_srsFileInfo.valid()) - m_b.srs = m_srsFileInfo.srs; + m_b.pointSize = 0; // Set the pointFormatId based on whether or not colors exist in the file - if (layout->hasDim(Dimension::Id::Infrared)) + if (layout.hasDim(Dimension::Id::Infrared)) m_b.pointFormatId = 8; - else if (layout->hasDim(Dimension::Id::Red) || - layout->hasDim(Dimension::Id::Green) || - layout->hasDim(Dimension::Id::Blue)) + else if (layout.hasDim(Dimension::Id::Red) || + layout.hasDim(Dimension::Id::Green) || + layout.hasDim(Dimension::Id::Blue)) m_b.pointFormatId = 7; else m_b.pointFormatId = 6; - for (Dimension::Id id : layout->dims()) + for (Dimension::Id id : layout.dims()) { FileDimInfo di; - di.name = layout->dimName(id); - di.type = layout->dimType(id); - di.offset = layout->dimOffset(id); + di.name = layout.dimName(id); + di.type = layout.dimType(id); + di.offset = layout.dimOffset(id); di.extraDim = isExtraDim(di.name); m_b.pointSize += pdal::Dimension::size(di.type); m_b.dimInfo.push_back(di); } + auto calcScale = [](double scale, double low, double high) { if (scale > 0) @@ -271,9 +246,7 @@ void Epf::fillMetadata(const pdal::PointLayoutPtr layout) // Preserve offsets if we have them and --single_file with single input is used if (!m_b.preserveHeaderFields() || - std::isnan(m_b.offset[0]) || - std::isnan(m_b.offset[1]) || - std::isnan(m_b.offset[2])) + std::isnan(m_b.offset[0]) || std::isnan(m_b.offset[1]) || std::isnan(m_b.offset[2])) { m_b.offset[0] = calcOffset(m_b.trueBounds.minx, m_b.trueBounds.maxx, m_b.scale[0]); m_b.offset[1] = calcOffset(m_b.trueBounds.miny, m_b.trueBounds.maxy, m_b.scale[1]); @@ -281,25 +254,11 @@ void Epf::fillMetadata(const pdal::PointLayoutPtr layout) } } -PointCount Epf::createFileInfo(const StringList& input, StringList dimNames, - std::vector& fileInfos) +void Epf::createFileInfos(const StringList& input, std::vector& fileInfos) { using namespace pdal; - std::vector tempFileInfos; - std::vector filenames; - PointCount totalPoints = 0; - - // If there are some dim names specified, make sure they contain X, Y and Z and that - // they're all uppercase. - if (!dimNames.empty()) - { - for (std::string& d : dimNames) - d = Utils::toupper(d); - for (const std::string xyz : { "X", "Y", "Z" }) - if (!Utils::contains(dimNames, xyz)) - dimNames.push_back(xyz); - } + StringList filenames; // If any of the specified input files is a directory, get the names of the files // in the directory and add them. @@ -307,17 +266,13 @@ PointCount Epf::createFileInfo(const StringList& input, StringList dimNames, { if (FileUtils::isDirectory(filename)) { - std::vector dirfiles = directoryList(filename); + StringList dirfiles = directoryList(filename); filenames.insert(filenames.end(), dirfiles.begin(), dirfiles.end()); } else filenames.push_back(filename); } - std::vector xOffsets; - std::vector yOffsets; - std::vector zOffsets; - // Determine a driver for each file and get a preview of the file. If we couldn't // Create a FileInfo object containing the file bounds, dimensions, filename and // associated driver. Expand our grid by the bounds and file point count. @@ -328,150 +283,240 @@ PointCount Epf::createFileInfo(const StringList& input, StringList dimNames, if (driver.empty()) throw FatalError("Can't infer reader for '" + filename + "'."); Stage *s = factory.createStage(driver); + pdal::Options opts; opts.add("filename", filename); s->setOptions(opts); - QuickInfo qi = s->preview(); - - // Detect LAS input with LAS version < 4 so that we can handle the legacy - // classification bits. - if (!qi.valid()) - throw FatalError("Couldn't get quick info for '" + filename + "'."); - - // Get scale values from the reader if they exist. - pdal::MetadataNode root = s->getMetadata(); - pdal::MetadataNode m = root.findChild("scale_x"); - if (m.valid()) - m_b.scale[0] = (std::max)(m_b.scale[0], m.value()); - m = root.findChild("scale_y"); - if (m.valid()) - m_b.scale[1] = (std::max)(m_b.scale[1], m.value()); - m = root.findChild("scale_z"); - if (m.valid()) - m_b.scale[2] = (std::max)(m_b.scale[2], m.value()); - m = root.findChild("offset_x"); - if (m.valid()) - xOffsets.push_back(m.value()); - m = root.findChild("offset_y"); - if (m.valid()) - yOffsets.push_back(m.value()); - m = root.findChild("offset_z"); - if (m.valid()) - zOffsets.push_back(m.value()); - - if (m_b.preserveHeaderFields()) + FileInfo fi; + fi.filename = filename; + fi.driver = driver; + if (driver == "readers.las") { - m = root.findChild("global_encoding"); - if (m.valid()) - m_b.globalEncoding = m.value(); - m = root.findChild("creation_doy"); - if (m.valid()) - m_b.creationDoy = m.value(); - m = root.findChild("creation_year"); - if (m.valid()) - m_b.creationYear = m.value(); - m = root.findChild("filesource_id"); - if (m.valid()) - m_b.fileSourceId = m.value(); - m = root.findChild("software_id"); - if (m.valid()) - m_b.generatingSoftware = m.value(); - m = root.findChild("system_id"); - if (m.valid()) - m_b.systemId = m.value(); + const std::vector& infos = processLas(*dynamic_cast(s), fi); + fileInfos.insert(fileInfos.begin(), infos.begin(), infos.end()); } else + fileInfos.push_back(processGeneric(*s, fi)); + } +} + +void Epf::filterDims(std::vector& infos, StringList allowedDims) +{ + if (allowedDims.empty()) + return; + + // If there are some dim names specified, make sure they contain X, Y and Z and that + // they're all uppercase. + for (std::string& d : allowedDims) + d = pdal::Utils::toupper(d); + for (const std::string xyz : { "X", "Y", "Z" }) + if (!pdal::Utils::contains(allowedDims, xyz)) + allowedDims.push_back(xyz); + + // Remove dimensions not in the allowed list. + for (FileInfo& info : infos) + for (auto it = info.dimInfo.begin(); it != info.dimInfo.end(); ++it) { - std::time_t now; - std::time(&now); - std::tm* ptm = std::gmtime(&now); - if (ptm) - { - m_b.creationDoy = ptm->tm_yday + 1; - m_b.creationYear = ptm->tm_year + 1900; - } + FileDimInfo& fdi = *it; + if (!pdal::Utils::contains(allowedDims, fdi.name)) + it = info.dimInfo.erase(it); } +} - FileInfo fi; - fi.bounds = qi.m_bounds; - fi.numPoints = qi.m_pointCount; - fi.filename = filename; - fi.driver = driver; +void Epf::determineDims(std::vector& infos, pdal::PointLayout& layout) +{ + using namespace pdal; - // Detect LAS input with LAS version < 4 so that we can handle the legacy - // classification bits. - if (driver == "readers.las") + StringList untypedDimNames; + + // Register dimensions in the OUTPUT layout, transforming dimensions that are packed + // into the "untwine bits" dimension. + for (FileInfo& info : infos) + for (FileDimInfo dim : info.dimInfo) { - pdal::MetadataNode minor = root.findChild("minor_version"); - pdal::MetadataNode major = root.findChild("major_version"); - if (minor.valid() && major.valid()) - fi.fileVersion = 10 * major.value() + minor.value(); + std::string name = dim.name; + Dimension::Type type = dim.type; + + if (type == Dimension::Type::None) + untypedDimNames.push_back(name); + else + { + if (isUntwineBitsDim(name)) + { + name = UntwineBitsDimName; + type = UntwineBitsType; + } + layout.registerOrAssignDim(name, type); + } } - // Accept dimension names if there are no limits or this name is in the list - // of desired dimensions. - for (const std::string& name : qi.m_dimNames) - if (dimNames.empty() || Utils::contains(dimNames, Utils::toupper(name))) - fi.dimInfo.push_back(FileDimInfo(name)); - - if (m_srsFileInfo.valid() && m_srsFileInfo.srs != qi.m_srs) - std::cerr << "Files have mismatched SRS values. Using SRS from '" << - m_srsFileInfo.filename << "'.\n"; - fi.srs = qi.m_srs; - tempFileInfos.push_back(fi); - if (!m_srsFileInfo.valid() && qi.m_srs.valid()) - m_srsFileInfo = fi; - - m_grid.expand(qi.m_bounds, qi.m_pointCount); - totalPoints += fi.numPoints; + // Register dimensions that didn't come from LAS files if their names don't match + // those that are already in the output layout. + for (std::string name : untypedDimNames) + { + if (layout.findDim(name) == Dimension::Id::Unknown) + { + Dimension::Type type = Dimension::Type::Double; + if (isUntwineBitsDim(name)) + { + name = UntwineBitsDimName; + type = UntwineBitsType; + } + else + { + try + { + type = Dimension::defaultType(Dimension::id(name)); + } + catch (pdal_error&) + {} + } + layout.registerOrAssignDim(name, type); + } } - // If we had an offset from the input, choose one in the middle of the list of offsets. - if (xOffsets.size()) + layout.finalize(); +} + +// Determine a reasonable offset for the output data. +void Epf::determineOffset(const std::vector& infos) +{ + std::vector xOffsets; + std::vector yOffsets; + std::vector zOffsets; + + for (const auto& fi : infos) { - std::sort(xOffsets.begin(), xOffsets.end()); - m_b.offset[0] = xOffsets[xOffsets.size() / 2]; + xOffsets.push_back(fi.offsets[0]); + yOffsets.push_back(fi.offsets[1]); + zOffsets.push_back(fi.offsets[2]); } + std::sort(xOffsets.begin(), xOffsets.end()); + std::sort(yOffsets.begin(), yOffsets.end()); + std::sort(zOffsets.begin(), zOffsets.end()); + if (xOffsets.size()) + m_b.offset[0] = xOffsets[xOffsets.size() / 2]; if (yOffsets.size()) - { - std::sort(yOffsets.begin(), yOffsets.end()); m_b.offset[1] = yOffsets[yOffsets.size() / 2]; - } if (zOffsets.size()) - { - std::sort(zOffsets.begin(), zOffsets.end()); m_b.offset[2] = zOffsets[zOffsets.size() / 2]; +} + +pdal::SpatialReference Epf::determineSrs(const std::vector& infos) +{ + // Here FileInfo is just convenient as it has both SRS and filename. + FileInfo srsFileInfo; + + for (const auto& fi : infos) + { + if (fi.srs.valid()) + { + if (!srsFileInfo.srs.valid()) + srsFileInfo = fi; + else if (srsFileInfo.srs != fi.srs) + std::cerr << "Files have mismatched SRS values. Using SRS from '" << + srsFileInfo.filename << "'.\n"; + } } + return srsFileInfo.srs; +} + +std::vector Epf::processLas(pdal::LasReader& r, FileInfo fi) +{ + using namespace pdal; - // If we have LAS start capability, break apart file infos into chunks of size 5 million. + PointTable t; + r.prepare(t); + + const LasHeader& h = r.header(); + + fi.bounds = h.getBounds(); + fi.numPoints = h.pointCount(); + + m_b.scale[0] = (std::max)(m_b.scale[0], h.scaleX()); + m_b.scale[1] = (std::max)(m_b.scale[0], h.scaleY()); + m_b.scale[2] = (std::max)(m_b.scale[0], h.scaleZ()); + + fi.offsets[0] = h.offsetX(); + fi.offsets[1] = h.offsetY(); + fi.offsets[2] = h.offsetZ(); + + if (m_b.preserveHeaderFields()) + { + m_b.globalEncoding = h.globalEncoding(); + m_b.creationDoy = h.creationDOY(); + m_b.creationYear = h.creationYear(); + m_b.generatingSoftware = h.softwareId(); + m_b.systemId = h.systemId(); + m_b.fileSourceId = h.fileSourceId(); + } + else + calcCreationDay(); + + // Detect LAS input with LAS version < 4 so that we can handle the legacy + // classification bits. + fi.fileVersion = 10 * h.versionMajor() + h.versionMinor(); + + PointLayoutPtr layout = t.layout(); + for (pdal::Dimension::Id id : layout->dims()) + { + const std::string& name = layout->dimName(id); + Dimension::Type type = layout->dimType(id); + fi.dimInfo.push_back(FileDimInfo(name, type)); + } + + // If we have LAS start capability, break apart file info into chunks of size 5 million. #ifdef PDAL_LAS_START PointCount ChunkSize = 5'000'000; - for (const FileInfo& fi : tempFileInfos) + + PointCount remaining = fi.numPoints; + PointId start = 0; + + std::vector fileInfos; + while (remaining) { - if (fi.driver != "readers.las" || fi.numPoints < ChunkSize) - { - fileInfos.push_back(fi); - continue; - } - PointCount remaining = fi.numPoints; - pdal::PointId start = 0; - while (remaining) - { - FileInfo lasFi(fi); - lasFi.numPoints = (std::min)(ChunkSize, remaining); - lasFi.start = start; - fileInfos.push_back(lasFi); + FileInfo lasFi(fi); + lasFi.numPoints = (std::min)(ChunkSize, remaining); + lasFi.start = start; + fileInfos.push_back(lasFi); - start += ChunkSize; - remaining -= lasFi.numPoints; - } + start += ChunkSize; + remaining -= lasFi.numPoints; } #else - fileInfos = std::move(tempFileInfos); + fileInfos.push_back(fileInfo); #endif + return fileInfos; +} + +FileInfo Epf::processGeneric(pdal::Stage& s, FileInfo fi) +{ + pdal::QuickInfo qi = s.preview(); + + if (!qi.valid()) + throw FatalError("Couldn't get quick info for '" + fi.filename + "'."); - return totalPoints; + fi.bounds = qi.m_bounds; + fi.numPoints = qi.m_pointCount; + + for (const std::string& name : qi.m_dimNames) + fi.dimInfo.push_back(FileDimInfo(name)); + + calcCreationDay(); + return fi; +} + +void Epf::calcCreationDay() +{ + std::time_t now; + std::time(&now); + std::tm* ptm = std::gmtime(&now); + if (ptm) + { + m_b.creationDoy = ptm->tm_yday + 1; + m_b.creationYear = ptm->tm_year + 1900; + } } } // namespace epf diff --git a/epf/Epf.hpp b/epf/Epf.hpp index 5e2c669..bd433f7 100644 --- a/epf/Epf.hpp +++ b/epf/Epf.hpp @@ -24,6 +24,12 @@ #include "../untwine/ProgressWriter.hpp" #include "../untwine/ThreadPool.hpp" +namespace pdal +{ +class LasReader; +class Stage; +} + namespace untwine { @@ -45,9 +51,15 @@ class Epf void run(ProgressWriter& progress); private: - PointCount createFileInfo(const StringList& input, StringList dimNames, - std::vector& fileInfos); - void fillMetadata(const pdal::PointLayoutPtr layout); + void createFileInfos(const StringList& input, std::vector& fileInfos); + void filterDims(std::vector& infos, StringList allowedDims); + void determineDims(std::vector& infos, pdal::PointLayout& layout); + void determineOffset(const std::vector& infos); + pdal::SpatialReference determineSrs(const std::vector& infos); + std::vector processLas(pdal::LasReader& reader, FileInfo fi); + FileInfo processGeneric(pdal::Stage& reader, FileInfo fi); + void calcCreationDay(); + void fillMetadata(const pdal::PointLayout& layout); BaseInfo& m_b; Grid m_grid; diff --git a/epf/EpfTypes.hpp b/epf/EpfTypes.hpp index 17d787b..879876d 100644 --- a/epf/EpfTypes.hpp +++ b/epf/EpfTypes.hpp @@ -17,6 +17,7 @@ #include #include +#include #include #include #include @@ -55,6 +56,7 @@ struct FileInfo int untwineBitsOffset; // Currently only set for LAS files. int fileVersion; + std::array offsets {}; // X, Y, Z offsets bool valid() const { return filename.size(); } diff --git a/lazperf/readers.cpp b/lazperf/readers.cpp index 468bcec..c3d7d7a 100644 --- a/lazperf/readers.cpp +++ b/lazperf/readers.cpp @@ -86,6 +86,13 @@ struct named_file::Private Private(const std::string& filename) : f(filename, std::ios::binary) {} + ~Private() + { close(); } + + void close() + { f.close(); } + + std::ifstream f; }; @@ -444,6 +451,11 @@ named_file::named_file(const std::string& filename) : p_(new Private(filename)) named_file::~named_file() {} +void named_file::close() +{ + p_->close(); +} + // Chunk decompressor struct chunk_decompressor::Private diff --git a/lazperf/readers.hpp b/lazperf/readers.hpp index 56a298a..49876f9 100644 --- a/lazperf/readers.hpp +++ b/lazperf/readers.hpp @@ -41,6 +41,7 @@ class basic_file ~basic_file(); bool open(std::istream& in); + void close(); public: LAZPERF_EXPORT uint64_t pointCount() const; @@ -85,6 +86,8 @@ class named_file : public basic_file LAZPERF_EXPORT named_file(const std::string& filename); LAZPERF_EXPORT ~named_file(); + LAZPERF_EXPORT void close(); + private: std::unique_ptr p_; }; diff --git a/lazperf/utils.hpp b/lazperf/utils.hpp index 275f3f2..da87e89 100644 --- a/lazperf/utils.hpp +++ b/lazperf/utils.hpp @@ -2,7 +2,7 @@ =============================================================================== FILE: util.hpp - + CONTENTS: Utility classes @@ -10,9 +10,9 @@ martin.isenburg@rapidlasso.com - http://rapidlasso.com uday.karan@gmail.com - Hobu, Inc. - + COPYRIGHT: - + (c) 2007-2014, martin isenburg, rapidlasso - tools to catch reality (c) 2014, Uday Verma, Hobu, Inc. @@ -22,9 +22,9 @@ This software is distributed WITHOUT ANY WARRANTY and without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - + CHANGE HISTORY: - + =============================================================================== */ @@ -38,6 +38,8 @@ #include #include +#include + #ifdef PRINT_DEBUG #define LAZDEBUG(e) (void)(e) #else @@ -146,6 +148,18 @@ inline uint32_t unpack(const char *in) (b1 & 0xFF)); } +template<> +inline float unpack(const char *in) +{ + uint32_t u; + float f; + + memcpy(&u, in, sizeof(u)); + u = le32toh(u); + memcpy(&f, &u, sizeof(u)); + return f; +} + inline void pack(const uint32_t v, char *out) { out[3] = (v >> 24) & 0xFF; diff --git a/test/Tests.cpp b/test/Tests.cpp index 9f1bfa1..102e0be 100644 --- a/test/Tests.cpp +++ b/test/Tests.cpp @@ -42,15 +42,9 @@ std::filesystem::path temppath(const std::string& path = {}) return out; } - -std::filesystem::path bigfile() -{ - return datapath("autzen_trim.laz"); -} - std::filesystem::path outfile(const std::string& path = {}) { - std::string filename = "untwine_test.tmp"; + std::string filename = "untwine_test.copc.laz"; if (!path.empty()) filename = path; return temppath(filename); @@ -78,16 +72,24 @@ bool verify() return true; } +struct EbInfo +{ + std::string name; + int size; + +}; + struct Stats { int pdrf_; - int eb1size_; - int eb2size_; - int eb3size_; + pdal::Dimension::Type eb1type_; + pdal::Dimension::Type eb2type_; + pdal::Dimension::Type eb3type_; std::array summary_; - Stats(int pdrf, int eb1size = 0, int eb2size = 0, int eb3size = 0) : - pdrf_(pdrf), eb1size_(eb1size), eb2size_(eb2size), eb3size_(eb3size) + Stats(int pdrf, pdal::Dimension::Type eb1type, pdal::Dimension::Type eb2type, + pdal::Dimension::Type eb3type) : + pdrf_(pdrf), eb1type_(eb1type), eb2type_(eb2type), eb3type_(eb3type) {} static const int X = 0; @@ -108,26 +110,55 @@ struct Stats void accumulate(char *buf) { + auto ebSize = [](pdal::Dimension::Type type) + { + switch (type) + { + case pdal::Dimension::Type::Unsigned8: + return 1; + case pdal::Dimension::Type::Signed8: + return 1; + case pdal::Dimension::Type::Unsigned16: + return 2; + case pdal::Dimension::Type::Signed16: + return 2; + case pdal::Dimension::Type::Unsigned32: + return 4; + case pdal::Dimension::Type::Signed32: + return 4; + case pdal::Dimension::Type::Unsigned64: + return 8; + case pdal::Dimension::Type::Signed64: + return 8; + case pdal::Dimension::Type::Float: + return 4; + case pdal::Dimension::Type::Double: + return 8; + default: + return 0; + } + }; + if (pdrf_ > 5) accumulate((lazperf::las::point14 *)buf); else accumulate((lazperf::las::point10 *)buf); buf += lazperf::baseCount(pdrf_); - if (eb1size_) + if (eb1type_ != pdal::Dimension::Type::None) { - summary_[Eb1].insert(fetchEb(buf, eb1size_)); - buf += eb1size_; + summary_[Eb1].insert(fetchEb(buf, eb1type_)); + buf += ebSize(eb1type_); } - if (eb2size_) + if (eb2type_ != pdal::Dimension::Type::None) { - summary_[Eb2].insert(fetchEb(buf, eb2size_)); - buf += eb2size_; + summary_[Eb2].insert(fetchEb(buf, eb2type_)); + buf += ebSize(eb2type_); } - if (eb3size_) + if (eb3type_ != pdal::Dimension::Type::None) { - summary_[Eb3].insert(fetchEb(buf, eb3size_)); - buf += eb3size_; + summary_[Eb3].insert(fetchEb(buf, eb3type_)); + buf += ebSize(eb2type_); } } @@ -181,28 +212,46 @@ struct Stats friend bool operator==(const Stats& s1, const Stats& s2); private: - static double fetchEb(char *buf, int ebsize) + static double fetchEb(char *buf, pdal::Dimension::Type type) { - uint16_t s; - uint32_t i; - double d; + using namespace pdal::Dimension; + using namespace lazperf::utils; - // Not worrying about byte order. - switch (ebsize) + double d = 0; + + switch (type) { - case 1: - d = *buf; + case Type::Unsigned8: + d = (uint8_t)*buf; + break; + case Type::Signed8: + d = (int8_t)*buf; + break; + case Type::Signed16: + d = unpack(buf); + break; + case Type::Unsigned16: + d = unpack(buf); + break; + case Type::Signed32: + d = unpack(buf); + break; + case Type::Unsigned32: + d = unpack(buf); break; - case 2: - memcpy(&s, buf, 2); - d = s; + case Type::Signed64: + d = (double)unpack(buf); break; - case 4: - memcpy(&i, buf, 4); - d = i; + case Type::Unsigned64: + d = (double)unpack(buf); break; - case 8: - memcpy(&d, buf, 8); + case Type::Float: + d = unpack(buf); + break; + case Type::Double: + d = unpack(buf); + break; + default: break; } return d; @@ -223,7 +272,8 @@ void verifyStats(const Stats& s1, const Stats& s2) for (size_t i = 0; i < s1.summary_.size(); ++i) { - if ((s1.pdrf_ == 0 || s1.pdrf_ == 1) && (i == Stats::Red || i == Stats::Green || i == Stats::Blue)) + if ((s1.pdrf_ == 0 || s1.pdrf_ == 1) && + (i == Stats::Red || i == Stats::Green || i == Stats::Blue)) continue; if ((s1.pdrf_ == 0 || s1.pdrf_ == 2) && i == Stats::GpsTime) continue; @@ -231,7 +281,7 @@ void verifyStats(const Stats& s1, const Stats& s2) test(s1.summary_[i].minimum(), s2.summary_[i].minimum(), 1E-7, "minimum", dim); test(s1.summary_[i].maximum(), s2.summary_[i].maximum(), 1E-7, "maximum", dim); test(s1.summary_[i].average(), s2.summary_[i].average(), 1E-5, "average", dim); - test(s1.summary_[i].variance(), s2.summary_[i].variance(), .1, "variance", dim); + test(s1.summary_[i].stddev(), s2.summary_[i].stddev(), 1E-5, "stddev", dim); } } @@ -245,25 +295,29 @@ void verifyStats(const std::filesystem::path& file1, const std::filesystem::path size_t pc2 = f2.header().point_count; ASSERT_EQ(pc1, pc2); - auto ebSize = [](const lazperf::eb_vlr& vlr, size_t pos) -> int + auto ebType = [](const lazperf::eb_vlr& vlr, size_t pos) -> pdal::Dimension::Type { - // This is a list of the sizes of the LAS data types. - static const std::array sizes { 1, 1, 2, 2, 4, 4, 8, 8, 4, 8 }; + using namespace pdal::Dimension; + + static const std::array types { Type::None, Type::Unsigned8, Type::Signed8, + Type::Unsigned16, Type::Signed16, Type::Unsigned32, Type::Signed32, Type::Unsigned64, + Type::Signed64, Type::Float, Type::Double }; if (pos >= vlr.items.size()) - return 0; + return Type::None; + + int itype = vlr.items[pos].data_type; + if (itype < 1 || itype > 10) + return Type::None; - int type = vlr.items[pos].data_type; - if (type < 1 || type > 10) - return 0; - return sizes[--type]; + return types[itype]; }; lazperf::eb_vlr vlr = f1.ebVlr(); - Stats s1(f1.header().point_format_id, ebSize(vlr, 0), ebSize(vlr, 1), ebSize(vlr, 2)); + Stats s1(f1.header().point_format_id, ebType(vlr, 0), ebType(vlr, 1), ebType(vlr, 2)); vlr = f2.ebVlr(); - Stats s2(f2.header().point_format_id, ebSize(vlr, 0), ebSize(vlr, 1), ebSize(vlr, 2)); + Stats s2(f2.header().point_format_id, ebType(vlr, 0), ebType(vlr, 1), ebType(vlr, 2)); char buf[1000]; for (size_t i = 0; i < pc1; ++i) @@ -281,9 +335,40 @@ TEST(Untwine, t1) { std::filesystem::remove(outfile()); - runUntwine(bigfile(), outfile()); + std::filesystem::path filename = datapath("autzen_trim.laz"); + runUntwine(filename, outfile()); // verify(outfile()); - verifyStats(bigfile(), outfile()); + verifyStats(filename, outfile()); + + std::filesystem::remove(outfile()); +} + +// Verfiy extra bytes fields are written correctly and the types match the input. +// (Issue 155) +TEST(Untwine, t2) +{ + std::filesystem::remove(outfile()); + + std::filesystem::path filename = datapath("eb.laz"); + runUntwine(filename, outfile()); + verifyStats(filename, outfile()); + + lazperf::reader::named_file f1(filename.generic_string()); + lazperf::reader::named_file f2(outfile().generic_string()); + lazperf::eb_vlr vlr1 = f1.ebVlr(); + lazperf::eb_vlr vlr2 = f2.ebVlr(); + + EXPECT_EQ(vlr1.items.size(), vlr2.items.size()); + for (size_t i = 0; i < vlr1.items.size(); ++i) + { + const lazperf::eb_vlr::ebfield& f1 = vlr1.items[i]; + const lazperf::eb_vlr::ebfield& f2 = vlr2.items[i]; + + EXPECT_EQ(f1.data_type, f2.data_type); + EXPECT_EQ(f1.name, f2.name); + } + f1.close(); + f2.close(); std::filesystem::remove(outfile()); } diff --git a/test/data/eb.laz b/test/data/eb.laz new file mode 100644 index 0000000000000000000000000000000000000000..166ec329ed211fa75852ba2098e95ea9354360a1 GIT binary patch literal 70366 zcmeFYbC9LY(l5H&wmGe7+qP}nwry+Lw(Xwwv^{OxwteQ^?}xkh-Er^v=lpRZzKr!$ z)|2(C%#1{=iWRk_1(ZYq03^V_5)i1gfReMFowXAIxe*0{p$CDZfxU&XwSkk99f6pQ zp*Y~rl=06)K<;Yd=wxAMOTfrP&(6p|@!y(`Al!gppaB5TKcY}tfG!CDP(lO%v;qMD ze?I?3UZ^!O^0-;)1RM7Zq82yI zh`Y0+0fC^0vxyUdqKT26qw&95hXe>XJ3G?XA?&XB5HX>Imv$k5>bnZ{IT%QyNRfU1(a1}w1~un1^&`TM6Dz* zE}|$WA^bn#P}D8{hvVAe-h~IVr-&kXR2rCXklhy`xl1opD;!ywtxE6TF=JhPckhG{va5b zS^f!OYGCbTqGw{@28;Gr)qqF%xWBv}_KPuvn;(r$^`oE(rpf2&J zDn$O%h=_oa@}C_3Ln!-SYGvgVmBs&rRaHb$`5!g-Z>sJeYyVMD|F!(bYkGRd|IFq8 zwmT;OS=0vB1m-3dX6DXB)GG3S68oo`zPxJiMJO)ml7WM=twnlcwCV$QaovO3|b7yCJPCB~3 zmuT%A&HlT${xJjjGlGErH39(g{Q$t%zYs708h{S~0l)(g|BTbff1iGQeP$7seB2O5 z{Rv7RGY?&3h65=E3p+ zfYKi=^8X*t$lu2QL+k&W5pc?KhN(;<-~oTfMMw(}jiZ0qo5Gty3NyvkybG-ZU`-Nb zmt=t6+!Z*LRCl7DtY!{a#pJo*?DxM9qOE_+%73tm>W6I$P-uO__cVTHAk>ajUU9jk zZ)l4pu2$@z2%ZdBv2TmHm-~Ic!;g2wifw`>u2w$(dk2G6CT4mKlS<4Y=9Hrxdpl};q*-`NkY|n1a4bn+)0eG=O>)Gn*OMRySqoQH)eTKaG=j+lF!)N zy)eFK0UG?}Y^?Bg#oF*+ zpv&s7KpG-(bC46eB@AjP6Gs_($JBTDT7u9;+q29{1VA{Jvg0udt;c#Dy7=7C1dn%< z)LJgab%G|OCxea93ljRHSIWHo)T3@fe28S87 z?lCEd=D23$tKmSHU+<6Lm~)esD?b?}uo%iBPoAv05wLA?zzlvmhwn_rsz$|8FXtXF zDT%P$$F8`owEBuYinv&X)GTV)*yf0%FsI$d*sPoi z-TnH3v~#X zuG0YZ5>}|f>N;-BHe`jR-mWx%b6Tr@hcKjY8`klX*AL>7Wsdl==BAU22lPOntFk@> z-XwPpg1hFKy3{t*)WbBai}1}&OD%}6WNGJE?pD0B^oJxtY8jS*8!XlaLnjt{f`@x~sWFgj{b@^*1izizc@3pNEGr%_te_-@JpH2 zMDSDDwEh?aBv0lzFCB)?8I6yLubf=p#37TOiJ5ZXOsJX4c-Tsz_U*P#bu*9c+lRwW z^Q4(o+MK^Rgu$MJ{5Mxz?R>K$k8gpUx_0oG3Pca4Nhi*1bq0#!2c4;fa7b+>#l6k# zN*tC!)l59W%icwlN=ceb^<6psa!iW#Y8~SvxC-zD1FY~)_Obnk){9duqLB!cZ8exU z<76(gHlIaoLF0RC70%=%PAMD=$V)QVVVgk^6HOI^u~4>Fe&EjFO8FMJO)xSirg1adI?PR`mUqyhY?JO}q zy}Qr`I@rt>-ttxh#HFhQ~QE=&VhO)Ze4mm=1mn|R&S+GSe3x7$ItJO7e`#n zIFE&Ji&kk};O3(ieW1cSkegxMrHw4otUrY?r2EJ))zfnM=c~=1O1-IHC`HwJRFt6} zFgmrDuIqyPd`qpH+uPu`I2Q%FthKU}<5O?NcaUpDM%-0rz7;_HjpcCht)c5ZFhN^sUp<-mQt0cv zT>B`NsLi19oT-zC9uBGMJ$F^IEcvT*|ocs^;!c5w=j=WcbZ=c<~KDy$r91z zm=qVqWeAgKEQQ{H?T6%}nhNSZ&%tG-g6>mnWQIlKG*8NCol<&Y3eEJ5a1k3tMS_c#~x-CQ+<6?<)^`0P!~OS=r!XH9~|RcF{>3H617)5d3k zKS)!^|f0M$}!jD0M9L6H~r9N zielmG6n+c@;+LbdHg&MN{i|8O^#=EZmogeiku6`U;b_SC)7pt zQb9jAblmXV63=`pQ?)|h@ZlE6ouA-Lx>8JfQWG8K<%h7rz#$J;-K}gMts1D=l9aaG zSHUHoCixB@X&nWBsnIT5P-$+up!(2NV?`RGKWGc%edoEfDDICl#553kxkK02UdPG> zg(PdpSYyYiL;4{usw;`~LpnntGE>yvJwytb=K7kcl&-nN=9H}fS8MhDUc4BkVeF3j zSQ3V%b(1GPoT+JfK&i>HQb9-hRHYFqU)n8mPxwneNw7uS+qDA}X}Bm$vB6VwgEssv z#XWB1SIJrBiLY83Q)y`uMrKuc(kzVt0ySjXjj-DV70OfpPCTi_$){*04nL#EZs@w9OzuYN`kRCbZ_XMe=zwr`4s*PPi@i=L=V7ef zgxPHPdzs;We{}r1wR~*GOD-CBgwYLaRSUVG~27H=2spV?i7_+i>=f;zyYyq_h z%*}^}1UC`oNlz_RY3_Xs2Njb`qHka(IFoXt!~0nsY8hmdS!ot;*rW=JdQSfpExe&^yORjpRJpOVmjp(^08omK5dd9H~*yS7f-L)2d zAE#V@uo7>*p4a%a%-~k`=!hMVzBOaebLC3Af^H*E(DZj6yr_X6mf+oNHhia6uuz8Z zcMU2Du%{ZkaLGuG#_E_<#rvG`3_E-kMHb39XAfe(AbwC@dW<96J$c=#I;UWg4=^aQm49)lOOCi`m5L%7_| z$9|wiEYmW##lr{$&A8mR7{Q+ocn|grhdVA`)4iAUqHu4k@qV*=+%g@bPecG2>QWqE zs|~x-l4DyJWSdJJ9pVj?cRh;<0V)T_kW3BWINw*SDffKX6jK1HKJP?S+|l;bu4U46 zs9TbelP*A^&>vN@51~`o`9{i1yr(m6KJbaJ-e8o)B(3hGWSY47fzc@NkhEjcRb-w) z*gkr6Ugt;1wnz@B4gon;Pk7ew3z8NOTK|B81o|lEbR9Mm;wBwO`~+hX%VKryxs5pY z48S{kBO?^wrCrVJq2aE#r$8%mH}8hn42>~-eUgp4QjGJN*tqVcv+X=NX1F?*=(UD{3L{Rp4}*7h1r!`z~m zWs%$xydnlHSlQMmfU8Y zJ8<+R3lk&;(!E1@Tq^U@=A`L%`KU2$11vkQ>}Of%wmg_kx<}fFZUze7h#I@>jan96 zkdDe%Y&4hE@21wVEX;0;0SO{)f-Av;elU5Kc^BrA}ZuoRLPkyo=m6dAXtMBDuFG%ypd1P+9Rj*5^5C|x{WGq=XavY~bq zT{j$tD;K+y?1zX&azstWCG_8w{W*aM%d^M!F@y;y?w;ZD3eQQg2Lkg_T&Swz&nivi z0$EE_Iy}aW20&3RPqp8mn4+F|(CxLJgw3|xw|ur%ao;7(vLkJ#OzEuCqZ?!qH;NY+ zm3!R~A5VSlXRVq;j*CbOdky-Y&3PwIb~zzg8*fzzfZW#*%FD#sMHfFgApGj5(x zQQBB+4W15I28pG3I&=$7L8<9=>!z(z?GkJ}I>yuWj68%7)JDlo8wv9^!f7V&43}u3 ze!d^Fb{55{zjMPpEH@c|GL)&SP@+F~{(_DD5p(ZDAt6v0fE6&J6_3 zb^@ly9PrkYGd7xk^@b)7+DmeVHkT4V=Ub2FYS=&-sD4x>20|3i2ESl0qya~r;Ocv; z2ytPzL2CDN)H@D&EIi;$?kU0VUssKw#le&Ntf1wN9g;~*SHdxT3U?jU&AHu&`+U(Z z<}p@{6^7j(0abqi6}gh+2}sxT`?&2EdbcU}(BxYW{luY#?=d*wu3*~bjF_Mc*pahw z-hmUYA3wLgSsBL`+lp~u>H!vf_CwTrQ97C=c-7~4f>)o)#Ts1sw2ESL*7}m4IiyYk zQyEmnjnvCcg(2@+W#SavP~QZ^{}d8-f=0%$-R^6=w3!@)W@C{YQcs820qsJAo@DQ|LyjLVqgBMO;ly6z1$o@d~MyL;uWIYKpK`K z`+~p?<>2h(VZRWwXA_lThLV`n9R?W%W+AkhB|U81uAKKa#IZ}}<}#38mcS0J^Wu+vNu?}rnJ(AZQ$(_p-*qKV z=|Rf60PkQBMU@+=(zw0NVz?>Qe$;Y}^Av(#pS*d!cC}je^+0nd8MrN>u*eaPHnns| z4Q|&{43Boc3!_-zS|{EayJ;VJIty^xjQ1Y6kO6);{uP%JIR!=$-ic{0v5EUYYTF7n zBk_tQ9)tTl&4Z;*bDA3R3f}Q;H)m0>W2UseUE?Qm35M)W-KMAv)VdTj5@d*aa&Th* z*qMrVi2KBMOc`{2ViM+|RP$kFCN2fWdg+uU=64+&j!2ImPm|g+fy7_{u?nL_>9ib< zexU8U6V#h0Ro9>Xr2-U`rknnOMj^mM*c&($)JkBT$|2zG>4%E zP`ZSk)q~R;P`{z9fXB}VbJ*j#GoYzwn7)8USlR$<R5m z>KdMgJ#pb$h8yy1u0%dX<#>=A!~JPsg>CD0AH5~7wSsn}a?Wb5Sn>=|h!AXKh1=Ob zt|BFX;SfylPmcY#Q0Wyn^W19IaJucr2bF{=@*8tlH6;s;Z3)vq8-%(+Ki7(}fMKQ! zXsj=u)FmZ`Ygd~#GTRSLl8Xi_mnh;d3#~Qpa+-}q+*8K;=3uC;81{UHW$th<*dSRA ze5{#mBnKux}9)HM;upd39ML>q<;CQ#Vs{IM% z()xM(2O5%1?mBf0zNm8_!@l;CN7!>UQqS`sO`W6@qq zYqA}E<34_90h-NU?0+Vn}kX< z^_x`MG(7Q#LTg&X)OlO!P92>AiFUVGGo_Eg!OZgr1;?=mjqRV2sVAQ#gqYmR1?^@+ zo{Jxe+hdH|JYnoyoKb}cJqsYWslG<+WI|nqbF*GSGGnKxft00SK?Gn5B;MznEO@_{ z1;DnclCfFv*i2>OVelud~sV>!{b z?7q$fD!-IamoA`&9KtiNCuBKtx@GYF?z+>R;w$#b_k2>G%J_t8L}rccT6)rM@}ogD z0W_6A=KXFB6^E8cieyM|J6l|Si{m_&f6p2bEdYb#aQ!r}uYL*TtezcHM<2Ln@T*-f z@Pge0{1vExh}Ku2PP?gxv%uXRB*-e*)Z7;g-3so;AB~M6}T2gbOhnXd_PE- zM~l61mvcg!oWQfB%QK=CVtN-F1`4Y(nN?VXc*g6$d zT;aqT4=oe{7pZ_!;sP=^M0Q*M0plS4&uB z=lH^oVv%1J3ERo(;~Zm|^3&kilQ#6#1x)%T*C;nG$k)-aqoyoD6{2ob@A0KL3tJ8IB!<9}h_=ciB#6p^AZx`rz=6Fo6S9nA#E9_9{8uv#d-$-h-r$0;P2!++R z=k(g*qzUs&*QZYwNv;;PX>qEhGvT}E#q>`qvg@(h@6+&9`_ZwzH4A>K36eE8(y@72 zoA^#bAyy_Q(X7zdFI5~T7ZHRnJC5wp#c!(pf=0@$wVoT;xX%6POhv~x!JD)Maz9oq zjKtf9hjj&qtm1ln{SUz2_H7HKihkY#-pMd1qAN!6{=Sz2|2Wg@z2fg0Zqek2B3!h~^7hmN7w{eV z2eEhI)nP=CirM^E)_r>;mgTnHZVNt2`lZ9vUO5WR+!WpHo9tpL#UOQ z!n6j@kOX)8po2ZMzpO`Q!^O4};y;=LMdfQqyB&j2*~H4JQD(jQea(+V%$aV4xr2(exXnNBO7Vbo z2F$1(8_l$yMRp1PL|&Mlka-kzV?chiH!!-2SReH4?uVClq`mM|ivSqdWiOn8g4vAf zP7uf*B-ii@QvP0YhORQYWP2<3g?Dnj)u-d#>uW9}#~a+$=~P@Cg|Urrl+Yihg>6tf zXOjm2W0b6OGyAWR^|WOht{@y2Y_YQjr8FcvHgWzrq&7CIj9_pxN%1Km^leB2V@lR} z`}r?4)6(pe^bO2@4gOMisa__rZ*lqldyAH$Chph9!a1D1D?QWwPWsa|R~qVu!CIA- z>Ra!)&h}roK5M z7ivr#hz`x_1=7<^0xyJ}@;;V8OlI2Gy2rx>Krh|zA%pKTOtdp@?T|5cT_G7$>RFl) zHAQnoMq^xa2%}w&v_Y#Va?8q2GYtevbryGhxUM!2=V}C`!7Z()XB~ZpyG`rMv@RE4OSMbw#~?swyG^!F-KpYRmj{TM&Fj&s@%R zL6Cw_ue3c<%i+o)Zosx;hco!y@llcozcTTG0?i6|C~qfjtwP*H(`lf_7zPDQ^^>+_ z&48*0Q-gqFD=xrGd8K*ano_NtjJ(!{#l(}Q*~!+A`bOA6;W5@Si@$Xic@S;od;;jb)XLY``eA( z5AiB=-5&Q6>T?+2u1wf9#6!)eIyuk@7B7Y#z3g*UsSG`G0+9Vfy^v+OV{?-Vg`K_n zlFqTwx0FQk7~?uzuVfhB1;(PdtQLb}_x|`=xMZ9TD z%=9v|5n)O30yNj$u?WzHb}Ke@HR#esm$YDuFR62$dH@Qkcdzgi)EeVzHznIQ%e-0=pz@p_iftBFcnDon)e&G61MUmc@Fp zu6oAGC{ycA`qA8ITyRP0_sZ`et<{NRJAjp%fA)0U2-N0RM4PGv7E1l5nhqYmBqbPo zxQfbro>nMNs~%Bhal(`#9Ysr+m(<(El-Fybbb4bP`?_1d(%KXfhemSZ@CSfwJV4TG z%b2RZN5$*V;mOcebBIx(QQ>;u3GB$vHEQR%uado+N$b8hOc?C2B#0Q7Eq+Cu!PrJl z`Uk7J>z|2if2M4YdXh~x72@F2cm*ZVQd-fjP=e@MucUfe`qTh7e^Kk5m}7^JC2hy} z2JJW|0*0@j>x5BbXrQ*qC98Lz1-(dX0&h1aRBl6E<}6{tEQ(`GR4uXI`D9yTDR(t^ zNEkjqe@H;#6mPR=0Xuh4iVJO_dL_0~^fISkPBD3!cPFQT68`E;e*zkH>htJAWZc9f z_cgH*C(Px1L3^A|#v2tQ&`PF!2eI7Aph_P9gdIyQvma7W)5VrH+PsENUxA2tR7l4F z{e>}-x@V*kCc|Rrr{ljlucQoAai`&_D%hHdMB6*rbO1+)WObgG+uTgBFB+;HtMCOu zCJ#;biqgD`7cT8xz)w9XKB^Lv{wvX^xT1Wd7bsLbj4aW0nij4XTeLp(U<(MhDdCQ?OI(S%s5S7yE)F z-06Ct*6wS9m#=k<-w8H2R_Pa+1=vjhArfUpmUf!JIx6vAz-Hkf{Xi9(GUX>s&geNb z)HvyeZyAR4H2o$*I_k?JPIOoB2pgp)hXQiXSX zaN0sOdsfb+6sbGUPXZukq9Nd54a#E=k{LSM(#+X9jWITN`O5Zw7L4CKoo9F~ui0^+ z64d(at7@1A9O{b=f{On6#4ND98zQMo#r4- zriWPV^TYCQd zJ*=;oCV|o?(PAg85v%bt3c9gfgG1E6WAaWJYd~9C&gRi)0dUn{jKBx!g=*2dvv}|A zC2DOdt<_ugs$XPUte10+={cL9k=d#d9BTF|*X?ZHGY)DE2Y`O(9AUQ1$Z!ky^R2K5 zE0poqcA%M8m?KIpuE@l!{Ny_UJ4`Z;DIOr+e&&oVh5G)GMnA5JJ3>u;hTB2}a>bW~B!OYcNoYe6-z=W;kL)zI^ z6y6P$zq;eG^y!YUR5I6zMb;}RX(CWO#p=#4;Zc@~=b03iG9ZHnghiGjZuk&#LsFSt z;3$5orgZBPhtg)a*oiGsm2l1!wq%l3yqjBVOc$+#YVJ`{ki&ntA;E}L#cJ3#E2g|M zOw{^@R+J%h-ubm1*##fU5gThz@YrRmr$3thZroatWo;q0(LH|25ubQy)y@Cl&Jz`8 zl#R}V^gZK3CMM>5$$I@Jy`8q_UrzR}l8U)}z)7#FcT|rofh4Z=i9v?Ao%>T7C-<^v z1;Mi>(c?F-u|Hrpy?vF4P-c@4oN}cgGLvI4x8PR@(poU?$VA|N`OPJt;aEy3!!J(@ zD&)vJIWG(>#>#D&UN@kT>K;L@t(`99Np0xpb5dl{Hq^%DfF@}73X;?Micl6ePyBC$ zWHooHzSOO^%xJABYhlNOKq8o~RTVTaD5FQv88wbjucMFYRQq*w|TGVJoN3MvELwo42tce>D`_sM&_z2!nqBcTz%qYa< zqc|t@8#G7Hwuh$JXuL~sKX0Hw5v6xJADLRD=%ClWoUVbFPH*$S0JemEzH|cjeo!vx zxk)gD4Vxd0EQ=bs3eNiBAB*r!aGP|oH0x_ek2XmU6a2olQjb|X1&Dn(A-8+^bFFa0 z2u4ZPX*%#XT`jWk&vy_r>FE6~gOWvd)^bn0+sGxj zp~Dht*u-l4p4Uc-4POh;6zbBnat24KZmyt2tzNXi$DT#K1F!cWMHNfULlVnHz@ZIm z;(r4q^A;bU(OlrXXqS()aoFh4RlZdCWp-lc~f_x`W0u zs1?jgzJLzBXdVgk2jUe_?;Ad_ySCVzT$LAbjNmVv2MjliIsR)d+;m2nos%pKl!dl>to{9(-#x715v$I%n5h0+Ww|~y~RN=_jnuBmUxZ(c%{s?+wgF%2XPU@ zL@xsSs>Sa~2%R`Mfge;iHA!ryvRE(D=%8cGg6$5a>$#7^<}zE{P>=7EG+Ilz65VUlG+74u@y^a~m+*C}wJNRj1hxpX(bPLo6v-^1hZYQOj(;{01Fb7zq425Nj; zQm)&)9Qw`m;-Eu#oUjMOgAqBv?HugFz+cG{RS7ULGN#Cu9%(uzhKDayUUI>F*ApUg z|H&Ix%qWqc^ zNJ zT$<$g%?H*8O5pP3v)2*mPP`_SqxlMu$S>MRLYu)^ilm4-yFWr0peAxR)M@ zoh#9{0`2)^{U5L^JV5}o?3voHL63O)ezuRGFYoD7eRTwjd~D;BBI0RZ6-|~x8hw{y zbTsa{Dm?4vNVn8f@_GGiGo})SvEwn0o$DfW{J3()1;01?k|N!P9;z1}duTZ3KpqG{ zfmT_B{UG3!)w99AtvQM zMVX0p*-f>^)Ti^SvoiiVKYgJWyltnR`&6nyqr$y=C{sx!7=%dvr*1?7oms?&Cy4?6 z(wD32xI^U)TbvNryvv<)w+UH|u!MzS>%KN+*m>sotD!+*OmH?j+Rxa}AM2?lCGS8S zAa_I$h4n#MXogu0=diO6G;#zJTdw4M&L8(4ekD(?qn8F&JBdU-gXr};hwliIksf|G z-2(!Gg;=h~jk#uCE5;8uUWz||S&!nvl`hZ|SU(hV*3hO&9Rj=IfK%-2x{srK(eW!d z!^}I^6J4ytYrrA7-vovY+NOvw6I^W^Eg)b8&DqUm_NJ$>K71!sHes5L*)xjJ41CO^ zDFTw7$6^%2RW56Cp#HTtkx?CgF9RJ2BFP1YyZ;+o^%X*&ZjQ*oriW-j`7nE zBY6xGEx6LK#hkLAG9oD3A(`1P4)l2cBSsrDqu z5@X@X=)MlS(N*D>RG_M@F^7|@^ocKlM*gOcd^S-kx`gJU`Wd$-CEbCdta?1< zZa1CRiI-IZ&Xk{s1na^%fqS72rMaF+XgVU~XNGJ_&84P7e6UoUZ^TF?qpmT{+kV6K zKjBw<8Gb?8f0)9DIClB7Jt@2QnvW{MPukTaFB++{$dBQ8UyO1yF3PIG=!rPaXrd(8 zQa5Pp>djumU3VA5=;@aF4Mi52K5Rc7T_^TC968My$;u5&N*@}=uri!GGe!~I^s5;o zM#_zALHHb9LY+l3ITsmm-T@Paa~&9JYKLem+73h>Fwg7c98g6krT1hu7Tq`G>wl4L zK01`n7t<=0xO(;m6lQ3~f>$8K3l*GYim+)4q_gW!;3jy8fxAc?A(hF7$AW$y#M=yCm~QF(+L83yN9L*^$0IPhSMj|TpP z5*CraubQ&ZvN~W`Vbr{Pb!Il6tTkMipJ_vd`KLZGoF zhk8i?mY6=_m`NZr-b1wB2cBHSj#hD6QvzGbj!JF0P(p3gY^# z={>Hpl>Os`9bAl*PUM#^~>ygE8RrZwFKWA zO_nF4)9+9<;JwM`yK8e`CTa?BOdTT-HClMlbcm|^vQGx%2}M8p^#08c$|#5k%VL;S z-l5wg2Wi9N_A7*8OWEDiHnAB`X|Q7v+#x)`R3zTSoia5#?Bj?u_)-phON{ks6MkM4 zT)hvHVxeqVl!ar8cRE_Zijt#wZH&udUHep}IRcv0aQ5vi%E%~1ll;7(^Q#tD-$mNLC&H+)OE)+>exm>g#Za%sjh6LKP@J)iEzleZVBAh9YDMIeT zgb;D|;x}qmj-r$(9qS-ZKaXN(0{jxgcof68nXPfL#!>1(x(U|m!z%a5uMOPLc)2sQ zJ2y#@nqXV*27$e;^^;H_xvpYmo7VJw8Bh(qd0b@cF$!Sk6xDj+-55q6`%@ z2Q+ldRV#+UAK}tMh%xSSRcfSQMS<&jtj3^&7>xORdY)!G%@hTzZ$D@IR9~$x&78$>njX*Rb77u5h!;d$724aiuYE^`$gW!v_t$z4LS$*hz%MCRe zo8tDscki1LJK=;W%ii_cvZefT9e_|=Oy zK#9~EF=Idiv@ipBjXGphUmOOHx!GM#i|0abrPOM=b{}krqVwudmkkWvD4buagM~d{ ztECDe>$@we{fwdIxH%Tu#x2aKdGMh{4bu5@8^tWIrQ?9YV2Z_>zp^X-T^pPhpx|j$m7B&e_W&`SQA|nYT)UHxPxcr z1?|&5I}KVfQ4crClRxaUhk~a1_3Yh|c1~HeS+UPvl}w16J>B;m&=k6!aeof#&t;g6-_p2lnxLs~O3NyG~bm0bO%HTg&0}Cj%xFDe2ML+B17QJmjdtfRCy7X6On%RflF8JGL_Tgdidv?q= zjHcMl?S#8VFFP9APeU!g@cb)O1AxOofs@mm19hTijoxWcGawDhEJbm8H}~O@bRSBSqYm%9`9hhNhBvVNx(T)Az?}slJt0Hv(Uj zSHLSMqKY<#tzq4il0*(B1h(5d_}!FNGxEB0(2Qa%@SV5&Q&Eq2LSVsku`<=#wvO9e zzZgQ#Kz41q$s*O*G2^Tp&4&r}&3B4dZ=fPB7ijtYy?Gfh<6C3nE5Tfiahem}8p0}_ z4MiI{Ui71IGWc&>!OaiGi{k6z8*rX&+D%gOpN!ZTQ3Im>j4Yy6YRifns%=YcthoK2 zNAs_kw1zzGD?Q6vH5`$zr0|EcRH?M}Zi#xPpiKEzRpb?-OW|r3ocJQ?W3)++K<+uV zJg=Naa488?MV@m&Ii}^c1aYO2^{!Y=TLU0ezuyk&$|z@$k#B4pqg!wa5Q!iu8bO$m zVCk4-mVMaC+I(?n>SMfQ(z4grS!TQ$u8%mB7$5|BRTzUm9%Evo(svLHFk(QBuPRhb zbSu-__P~nU?tAw{yurt?-j8qh3h~b&~m||THN59nRf2R3HWQ9r9i$B(+V+d2tixmUf->_(|nc zjpoDF63i8U4c!E~M_4WshmQNoeZbnYy39-7Won&A22cF^GB4KY~vOZBT+<>SxhatIvH3Pv6qI<=U|zhNA88~Q&^f6! zu=Xcov|bf6hTepeTFx@;IM5r_G|qjYN1jya+r zT`3Kp2ymBow(&!bqK=rBdLyoJIP4N7Ny!pb_c6FROax|1G(DPe&ta~?Oj)w=x=)9o65Q9oiiBz+0YtV;e`LxlIB{wUd2?Wi#w zFhw7ryTrRME>yQ|D^)SqSy=nd$S#j>mjWwH&FGFjf$fP}TD^D{1q6h;9 zMEG?aN?z8)A(AdnQM;fm^E+)oDfPFZE%l5AwwI?#4(0;kNapGBim<}g&1^hIAybbE z?$CSsI9deV_T(unnPD$T_I(2B2~2$F_hVn)!$5|@$%OI*umn9YoBnSAnMzDDZ0wI^$G!v;1q0WN9+OvT7ELr z5Uj8*f^VWGFj8wBgubQBuwPpPG>DGXxAq>7ZR^+5)WNC^)Q}MG?SU-=fwO7mf>=9R z5*VgQ;3>w@p<-H?8sMe$(i*1h5g!xS%;Ux4-$OEVnplg)iST^$m3_V^gIA0!($Dv|7iz;7mf}v?(p$9&p(tMS$_D*twe;GS!AdFB3 zE%<>vroW$53VeY`)Hif6lg?t_>q@9qJ{5GVcpuN|%jZb+VznhwjPTS`sF1WXG?Ia2 zC#EVFuXBL5aAF%J&kucdmJ6$777W>qONb=PWs$qIr`6edr{ygkqm43}5Ny9w)km%X{vn{oU-8#y0UDuEO zHph;B>~$WQ8R617f0@!k#mis|YXOFIY!Zs>t4o4N-Cw_|)#)cZ8^~g{lu@XAc$#g~ zx$kFL0K#?Sxc@aa+&-A#VDF%xRK#V^>bDy|IvfkBCUNu}@zgOWLio?cY{|4TGz(tB+F74o5 z2>qxcTGuyMWb>S1o7ss1PxHk_tdqVkd|hE7l(!$I!OYOD9=p!>^2mdITZdO7qjH4% zuR8#$=YV&)yMDQ*k?y2IH>@P3Ox2g6qz+e(HWHeatfo=#JgLVaY(fczH zLK|iB8R^Tgl{jy{0J|#@;;Ewn4Vv>Fmlx}=yI5w|qKoR8IRWb}hMb{wgU&P7)F!WM z#MmgGD~W_zxB-xq6&6WE;O3a)y<-i&SU!Xlx_Iw#Cgc0K?1dGv=jTFlJO3heh(&QK z1w5a%onW`y!jNCmlCu*e{2kZtP#s)=(sxFG4Gw9uOV4nc!3gVb)hboO>K%L;Uh__R zpn^pu=n8j)dDt_J-!NLW0_Sza~VxjSQ1ekjg&x_OLG& z@E>g284oZwtR<6;dutSB@-p``w3@dUU2m>7D`8D10ru(_Ob$Tg|J+!yuzL#f32i!& z;pK$D_(AO1E+_`e+_}JJyGV6n56QPO5_ZmDc%%a4baitL{%Ro%9qyP|d>3rU!x3Qr zm?5rXV>}fG!g9tB^ zUgf*jmXaj;+J#W>&cnn4dCCl}PQ9p@eV+fOF1%(`ntrw*#5;mT$8DS;c}al*gf%fp z5=692CWKdIljZ#}xn)KdM$QPcwZeLK20J@3?S3;;%go6-%k>gh-|fivGQ=bf8DpT_ znhg@1E;fPwbec|K1H!>|9>ToUcD=Nj)97o_`0JSdqWe|VB9>qgPTL<~3^&W#W94{O z8D{-5oW~Mbkt7Z)zzDQ{2_BxqQj^~N7MUZs%h*;*=8|+Ur~*o`meQCap0$YxsVQ@E z>$;m&fRZOnHB#uj&*z>4#Jds);kJGeu*_3o5DEpl+wv-&Zty9R`wusJApzJVml$vT zrV1G?zu4x9r@;lI3d3JE#XIPd#0h?o@|U&{+!=>2mJ}NYZ9jYGC}pKR!L?dh5x+E# zkaiz>^;fn-P!5?K*?#*otIHDA1oB0@o^J5Wd*xE6{>pp{WhCeQ5o)X!(Emjb5 zqe~0=i)~kPl~3;VVdW}@**NB|{SzcNB8|dveR!Ji4vQxZv<@m?Fzl^VzcV`KrbdXv z5|n+~(>b`)V@K%1{!NvoXvYJ$~de zh8$q=tM$i1v86B)(hcIEH>F}4v>KXF*i&Vn{G{ttZ2i1&{cPkBR%-7EyV1nA3k1|> zyy4x}wU3?_$lC1fWSu9AysTwjH@|X^^wAAsbBD#}6UUHv;K0vMc8|;`W!ui8vCa&_ z!o~9&L24oCr`zN2-sQp%V(;%TAauXa(uj+EgVAk)rMq7+=z_YcQOa)hKp#TQDAvXB zr%dYd2kvEnGqc%v%f>=|GdHon?NdmL0l=Mjr!UjuaxqDw#C2sPoF+PlWk zTH2T`usW@p{ZuIIM6-}MRER`+8Bf^!tOH^``WqJy1hF!|q}@IQ8-p^ggz5ctyU{kx z)oGJq--r^Fv$^F#4qkKlXH+~tz7i>oMQNT!g{ZjWCY4)fez8qBvrA5~`ts#cDhOoa z`B;iYeV_;03%>SM5R%TfI%xtqHK2oyT%dq-@4~x+s18P%rWob7oY$z4fG_8V*wg%>+hd1A}U~J zrJVM?K^=L|zO%DwMlwZ=5^%j6*xfjAMwn_C`=;AU(*|Q9pcAXd%Fy z|7DKZW1$WH+xa2>6`a4|e8uuvTm)7> zo}m~BAkT*v6mU;Fz<6#YWKZzHOWw%`svJ-WMn9a+%!)Y)g($(FIky7Q)&cjMY8xy| ziLz!HMFOpxnnQ`6ibEwLX*m3{RV9Tu>k;+S8>%O)Wq20(tHLGkBQ+#-A~y9u>|LfF zbwtY%GIvH|S``Ok^6y{{OS8=8RV=(h<0lm+!K@w#O$SKXfioUA@r)CP*Iu*uso4JQ zMXJwe@qVBr@nV1L7`VqDOvHkhvA$@1%k`0qk%!W7a~D)J4Pu}GRKo~A8g0mhpl&yt z!Jv+R4Bs26!GQ(oTGEg>t_w_I%zo+LQku`cylQ%L>Sy6Q$}R?%0d@%?NnK7vd#q>P zb+tv2{`RQR4`)gD(<=Ef`XDZXN78Gi!{Y+b@p?0+hbi-aj&Dhy%Rj~q#4Q+kV{Zf! zh#VB^WDVE5Iv#yJ73*Q2w=lUU@n8Grw++4r>r_Mcec~u+!ol3usYB1n>k4dVUM-u) z9f`ayvyP_EE9Dxsct$C5m9=z7;h#^n#ibbMWc^~2+bPv+|B5GG-PO|5xscVx8B&9I z8m`+;^M0lFl;WjORtfoaO~D~;P%-xRvTzd4V-9hs-?-~=5n{&OD162wq>mZ~BB<&C z>%&gJmj!#T^P%JoomI4*FA=ZZQot`A<&^YWVh19JVq?~dH}GzKjuX^dOb?=fS>s#i z0`@}1?xZHyhIm6!May=|28Uuwrs+(hVxsw--Q}KTKub3hT5p!VYv<#X3b|}s;*F-Q zy$S>4j_nYNNeGRm;4z($N1}DwzD+%O9B9Jdlby}y)<|g?m0X<_5%ARBCm&h>skTsJ z2Feslk++Z2U!>|A>N4Qiv03Adj6rqdt~wkTDH=UhZPW&F<#Z)lKp!a&4Kk| zNb_Pp6dYurqRV|47t0LSz5?U!to?(C53JK)zAKB%#;_a1FuIIdz%8}|^kgK=YyrQ= z_bh`%BMNypNG1v`1?Q|t=I!+~EvZ(G3uS`&-nEJa>j4qh26ESkGW;$O&FFsKK4AsS za)eEfPn24g+*Q!XE^h6lhWu_<9P-TI1ZxgSa~ZaHvhS{4RI0BYo4i0Z6~6g*b1o1N zhmm{q@H~ucuke^gN;`k2g4VgUc62L-{av5a^Xed9d%Tis$GjU{C zLZ>?y^U#G9a@44Mbk_$lu4>Os=1g+;jQKAR58P2GKf?)fNo&Bw*LjtoZeTa@E(vh9 z|A^AoYd36FlzA{*ZqM&>VQYO72}}R|l6F*TxmP>3o!&IL*vt4Q$PFO?sFyxkW&#w^ z;MjXpV}Q6f!?Zg8nL-UhRf)kZNpDsLlor)i;b>LbfH zoVx}a7FOw5OySOuup032?IDuqVw7}~@TwES3=@`8dCSW#)uE>g$g-t&yN zdj-C(cm}QBtDfBtT9iq2FNLlH!!7Je6S7bJXq{EZ3dUw*(hxhy@0$)E!_3Vhw|l4d zZDMr9x@*JAb7KinnL;7SA`=~bX>ftJc5s*UOg~HFv{$ffLJRDH_(V8@v`i-ogXx8e z6Nq#$KbELdzv0(jjD^PrdzVq}q2V;QHpmG+*@23buHRYrLhS!4eV1~(aNu8!ZX_Is zoB&7im~6EQ-kYcdPM3u{YiHpp+*4TMvd2=RXg@-qLASO<6ltvqaV#iILw&LW{nd7z z#Iw28!1|Y7tWD;dnC0}6EuAAi=mu~sr48!=j8t?1C-+gNQWv19d0o}BSZk0T6lsiu zQp)-Bt@{wXJrSgfSGtIj(mn|OK+YbS?;~*%Vs|kS4l{K%XK-1tR3N%#EYIVEU98i* z{Xxj1m2G>v^c1$kvrUtjJ7c(o*qhXfJVW&N+VaSpeZfy^$w>R-&m*q9T@P7*Jiq9) z`2;nQi*hIbGc8v~kQjt$vCfWRB&m3c71`|HDD=+9l?5>ycaE{co zI#V!mgl3&=n`n!Nt5asz_YfukSA~Hcp7!WLGH$!eyX??lEw8LeWjK>1Rdr?@eHPTE z9|u{aljBTzlS$SG{`(?(erEnyj9u1`c=Y)MGES(l$D#BtGmJ52H;M#Ndxz9F`Qch- zc+Bs?1b7QBdR^}7k-E*A3M62mP^8tiM@#wU8IZl#a4CypA(xy)mt-f#0>G2H5t;hk z<2vHVg>aQ&^sP!xxLf@>Ij!fDh9l)V^X-ZDvG!KqQ)HCVpNB*J2mC9}`7gv|+qs)5 zRledlNNV^B=9$82mLHY)U58hmy2*MD(ZU|)Sp6({aFOZjun2ZTETm?RLt}ko-x8ac zyF6VJ<^8r9QIP#=ROHmRfl5=G+1gd)OT{?PWuhp1Y0v3h?GBI-EBl?gsXGqKN)^9X zr{T?GO=rG-00<-j<2U>gx7ny8a!K;8!B7NGOiFNve{F&^)L>*#o|u{yMpwQD;SXMi z$L=vEfE5z7VVDcjFhXoX2yaac7nW_hbcpg`w8l_BX*|L$2Nt8hm1}}$@-u3*+w|>> zoFKsfq!ertB^YK_mRO3plWZ38D#I#okva}%zT1b8@mPgCZpN|pU`kvfjh+PqlX@CB zw(Gv8na%FFk7|OWKd08YobqW=6MA_7T+3pz1BnT!Z z?ohRMxs8B5Q%m1hl7x@+kDIr~eoH2>!@y3%6zcv=k1YTdNQx)2WozsyWgW(togq}XWYs_?!SsQSJ7?*;Tf@v1suDWOx zyp?QP0MG3mh0sB{*;0H?A`(e?Q9=8SwXRN^;{r|7nrl=VyQYhO|c$1`J!6R^Y?lj zz>qLj5olr-tkc7 zbxb}n$BHzE9C#BKr2f&r5nawI<6T7@wYw2L3Zn`iFIO{V4F*vwn9LTwExf;Hp|yQ~ zob%D3a-;3iK#KNd{dh8!L%Vk~Ut6=F0g4_I3gOob@CBNY!{ljxSFP*S2} zP7wRWs3X;w4f)6-L*r{0IafFdM^=f6WIV(e8(E{=fq7R30-?pB9} zE0IlImJqBST^8ql#_pUF<1Bh}LHbA|maSg~i|CbdoAb+IVxfL z(U4|0#y`LS?N+&rE-PV7ScgHrjFGww^R+~X_ov>Ua;sce_MQstec<=@s553d<8i7b z)HQ|}hLQc2DWQ zf>RSJHMc91R!wbx>JBK$`9C|(uUvbTot1{^X#~t~gidg6ZFCWYi8;>@8i?`<<*mIB zL$=H~-m%p!F(s`H3HMDVlUqUa(*~yF$dH}h1R9H+a?j$bc9$ZR7AhWolTUs71uLMj z=&Zt=M)1QL;r`@k$mRJZw16MTk3+YGW)cf9l4^@zNlQz;iFPlh>M7RB7mv+R=kR`l zgi+{l1G;6CDqRldsVR(^K9V_JkG%lW-Baf}#R{Pz@%npTkOodr@?VJL0-mNuA5Vv+ zxZtLkq`%{f*dd(pqM>L#EBCEnbH1sXNI^psl80byCJ35}-TI#__yWXO)O%+AV{Xo# z##2D?W}{4oA0HH8uGs_%K1BcNo9N9>tZTDnfUQYMC{}YI&sngDLD*#Q=lg2YgIA(3 zoEG4>mD5D&4ouUw5-oA+oVl|vbwfMH=+|tK72j*;(qk;^G0$E2`#DFXGh&BF(MiT5dvS6i+^T+I z?9yhVJ`z&jxed22q#zZS?jf>Ax51*nINLpH&nxLb+TzncfpE9P_Kian{i$|36*|@4uoENfrdSYYD8F zmo%}NM%eESb{Xj?^(~trRKmqP$V7Ud-R`kG@Gz1_y@dnJt;twCO11*vS+wDk{zq8<57Lx7(_Hm6l}>d3gGM3I_vRR}#EDyC_K_fdz- z&ab*^Ej=DUaL44+J}Uo1#agXxWPvMQA``w17%WD+mtYl}6^^E6<(`wXAriyjW4rBo=AAa{ zX@-tP=?ZzWGn|mdB87pAg2#&BQu;xcV{d>;Kxk=A^2;IiGFcP5r@FlkEsw+yaG@zk zT`ERF$#j~N4+qrGDfZkU$f%k_}Q9$LJpW9}6!^?3cbrY_K3Rv6K(9`Q*zG4O>96Wn^BvUQepL5Fxn1a_iPY28j^iu#S zL*h@vq6_N4LbIL1)e}oN*yE1s2-6~3ugiUmf0Y&`xvLVTGDcXr24i*yrR~$@Q@EE~ zVyp`R*0HBhc?tfUQb|62S<-@_atCyuA034?psEfL`SyKf;}06L!@*zZpC`GAD&WL# zxnjl*g~D!zLE2#PSV8F5A?tY0%dLW*fLdUk3rxNDL&_WcUeMPip2t@O?M^v)%|Ace z%NjaMAdE(z6W7a}mXi4YVTUPL(Ur<^{ll`kp!(H_h-^wif=X6j_Ix%VX)F3F3n{@z z?(%s_q!{#GUf8Gu?4arp*ozQb4N>JY-bNtm%8-ab)y$(sopzzOiCX7GHI}n;&AnoY z$%5(A1&B?vlJu&WpI`OkJC3Y*A}@`LF~50q?%piFe=0*oKYK=_-WISPLT{%pyd0tg zaeAmx;T#&A=C%n&%`^3?AIl#N6SjYcg^8{*9M&ng0K#atzl=)4kAsVwF+HYXC)BK9 z+L9a00hf&_mZmu$^6wV5n|cE^vgA0<`i)!?P@M+#GmkPDA&v`s5=_3Ixtau(Y7v}% z*hJ6jQ-n;rs1E61`Ad54TM(H(fujznQn=>Q)(&h}Qi#UwziQQ@bUacWlGjOnEhJ!U zzBgV9{L4~l3|)hgx&r@QenskKc8Fx*jDh~!5PTJ~;|Ey>kWL%2aE{__ z(|Yt{ZzG&MV+uQdwH63rjGVBriNTxY{U=)$wCp*;GFRFmE5~q8M^&I3C>beY=sLhHtZ>K zmB_{6f}+zAhR-Vjr5yg!A&r~3g&420cxz%1ZO4BXijO!C6ztfBb>=bRm<>1p7&ZZt zYvzjy*LH=qP8sdulhTPZ;p0Q=AXuSI&KL}fER>RT@1emzgpAD*bL1a@I$>{SU3@JZ zWfUnvZJ+@N>3II~#k!6`H<6<%8Lw#rzT-dW+6DXqk62(ZQKIeC@q~m3{6)+D{x7i$ znyGAI`RgJ2LpvnjuqMz~HM8m_)uwC*Bc6KdK)dgEK=EV*R4K-k1({!PPJ3{w8(D&ZucZX;fCTy>!LILI0P;LOcq@ zVu1zwyFa@c5LG-kk6D8JGwoBOltOv2`&Gc|<$T#gNJx_uP{6*unm1_WPDa5C1iYfAMNvCkl0y#>^hEGCyoIjrnhoG)ivGHNDP3r92Uxuuo)J9AGT zU}nPd9g1j^IGZYs34~~k;LrqF|4OMNtTWHE_`Zv{FV7Vw2cEyE_4NdOYuIWMdd;?J zPRr+uOJ~M5gR_XA}gGX)g_Y&U(8DT(*yz2L6L4S zp(``dC+p!F!vUQpk+;Ay2)o6nR&(-@SrHG*oTF2g37U!xZjsfSpzU;XNcVyBg)B|VBG^B{p zq7nV^I!WX7eZ6jq#%j?rgyYD2a09pont|S^(K6MjaU|Ko%etiWe+(=rFi>#NzZMV> z{Gai^pZ~RhpuqqAA|N2}-{*gQ;Qx&OnfsqPfkA;m|2y!1!vF7m!2c^clI@JI(-}df zRy3vF8^X za}t|s##Z`CEB#f&`pd`fTdp#UQLpEZ|ImX|6}dP7n*zLf6rNq>p! z-aO}Q=Y)V;?*&07K-GzLu7L`qSILV95)GrfChizKyWfoSWk{SVz13*`?Q<$xEwCkZ zCRS!B^`ES-;pnyzE<~PJTTzM)v? zTl?0bM_eLNEb;a9MMYxBgq;}vqhy_8ip-hbW{dzi<5HKePJRFbT zq&p|&0}Z6(0~7$$1`|Iw;n5M7rL@l!ui9y$iN_xtW-w=3 zGvCJZiD47BW(QY5pg7d>Qgr}QZ|g+BRGCOqLRU#zLK>Yr3HEJ>(@B{FM4uckT@4gis3{H-mo4-C0`#3e(~Ql0Yp+XpEBVJ zv|yUSES`}su{c$aSIc5ZRQ$TPe;FhRh{}GrH2Q5Kr|U;ZT%F;Z{G|#X#+BXLK?u@! zh)gvEsIA4cIrrc?zqrdBJh!_THkY$}NZ2zks8@ZVXUMki8{t+Fn*OAE<3?i4}dL)M!elor0oHkM7x|xBkLuG)QX z%d(nhz7tFm15W}ez4w7Os{U<$QOeP)C8*^0gAnr?M-M#$0Ca;ykK~6A6rvmL&%1j) zTku*4_1ov=(+BgRdaQ9Q^^7Jex@&ZW5gpw1(zI-CuG-+`-DKYe2M@so?k!()0l*Z$ zO*vY@q^Qe4sIK6no{(IBMb>ci)X+J;JeSH&%H*y=E~oD z=$+iV65 zc=|Z`pRv$XjO}<0F7ZcVfp>#n>v2ZQYJoAv?6L95{O=ekM)eY z9;pk={L%9{#{x`UIAiSvAI!UCJk?gX2ckPOqnA5F9o3O=p1IP9hpymisJjznRn%`C z4Zx6_NF)@LQ^7rECy!sQh#?Y51$Mz%`zmDpX|j{$Mn85{wOL2K#~A@BcN=U^+e=X} zTpu^nM}SggIR|G67My863%9SXKP$_=oQEAtwlkGz;QX<3Qz^*i>ijdRJ$YH_U6k$C zLLo{)r73;#UYCt09DriwyaTH;k#Vlg*bT%m!ALBK=QvZ#w=l4^jr3S+VV@o=cZfLf-{`4dGKBgh7YDr-}!!Cw%`+5S=gKj z>cuvJ%y{s6@v$(b)r+Pog?fF}!T*fDlfF}8@~-9dmw$)s{LJQiRZ4uO{Zp-n7}zJm zEnUm;#aoELtXT;EmSsJvNo`?(jg4Qr~iI4MVwcbd1dTBL-91nbA8KQ4h$@7e5~ zBl|+};l8^3w2TqFXc95A-k3yU$ze~XFHN~ZwoT>t}mRAJ%b zFDsvp&g?lZSc;Q6ci(EOt3Jc-ZDsppb-0R|A>y)>AM|atc)kV;roHg6!mgib|J2!Y zE#;+Jj51h+BUYVFmG!*}80uh};XcUEwr_o#KqpG+i)uGVc z8W|l=otey?1$psU0hXW?7$5DmLubDK^*^TNEuLRCwivXZ=}hn&JCyl>-vi@(eHI|u z({^mnyX=aQkgjy=5eAgZO=R(z7iMpMUnDsS8h5r2L>a(Jm`Z*J6jIp|$@PLl3z>eOIQuk3q6)?LY(2TLg2TDx!LWX+Dby)FA6Ws28XNRwV7py z7;T)iGI8e5+kMGU{7{M1w7FCcQl={5ic`g5eTj)+2xTx}S%r||24xOb#o8-f5ZWr@ zQ3FSd;964-0&D3SG)#b$5V>T+pG7HDWybkq{XEn~3@vab5d?Ww?#Yk=%)i|q@_yqq zi+1j@ieeR5qAIq>BcDUkJTDer{rVlT3wtwPk1bLTY7$NkGkY+sX8^`TT!KmJ`T2b~$< zPr=|I4u%inoYzr3ukCs{@3Tb3PHt`pWX!ErUVa}o|5iVYT5u@g7)};)8zbvXory>T z?B^4Rdh5H6%p=cJUC+nr&M4X@+r0i;VD6Ppn* zA?)duJBb7^lV6xR_ozN#w(g+^Dq&59VUb2fad#f<{RJ*FCNrc-L?$if4x_D*yEVp` z*RIl0A){SYQq^G7pL;;QRe1HM$!xKhbkov+SIG&zB~?-uBTI44tW{TX%83TcWNq~u zNz1=KkjeG#?ic-y%g{soAZ)_uLtA~r0Oh1BhJ||k)gU1RP3gvcJOr)8C!9O5toX;ic}u<7sTWE(T->Rtf_ndS(qMF&O~ znMg)iyukg4x)*YCQ9X^4n_UJ{`i#xkca~4oez$lmYpy1oYY-C2<#ryEJG8#5KzN>> zndq-fSF@@1Q}fyj2V*9@q;sb+X(!H$A*_kNtaSL|z^4nnIRJVvseqspeR6uQ%o$t5 zx8qL)Y^_clOAj+#SPUMu`fO2`W%e(c2PlAV9WoEwz}iFX)Oi8V zt)iW_Icxf5fb>~E+t~eKL>h}BC^6lIIsTvzE!VsnH5Rq-90q3cILh(J)^}T1UQ`kr zA$Y3eUv+)INPv@Q>6n}GgpUfdRrieq(9s=;blcoz#mKEsc5wz%h)l2}7!X_6^u21T zEr{pA5IObQSrms3Q>z=&RrAqLAn?DNw|mg+sLkckAPLmv1TX7}#bSidpwlm(&y2${ zq@$Z3I0Mzl*jE~fHK;^$Z9WIb7}>e9X^`BbuY-p79Vz?vyUBp$5Tdw;ARSoTA39ER_Qb<3lr5S zPZN_fGTtbw^_;+MsS9L3TU!UrO0`_wxr^cYd2G(W6Og@6!^m}os5(> zH8}R(cSl$mH8NMjqg}_{iD_i%x1?H=f<9ru(s72e}ZOabGyVa z0U;Wx(i9=L=wf09%okgi_(+KHihJ+ga^;3JPY88za1_$6JiJGwRz(OaUfPoi&JehE zPU_joOWzgIQ}OESM5+lCN+rocFjq2c&?4OO50ae*NM_1nPM8~(92fq*`aj;gtd5h4 zi?i!dlbwc(Qf+r-@w94(%^ywoWYwZB$X^-e5sh=zGvm%`Kf9rD>;TWgHU0O!pFAu> ztt~tn7>5l9y2t>(O5XC`nL8(JgBTGBz-(XOHFwXA)!dj?j3@9cr6lR@^r0B3gSe9K zLhqhOiM348Z{bQX2Rk+z)u4!kkGp_xrA!oBqwKucnPs_}b)6zaFH^cu8>GB;4+MV; zh`pR#~m}SOhhcw*iIsk+p}nj z+Whf%vD=3)BPodPT|_f6=SkV)GtO6o)N9|m3=Z|4R#ZY!Uto?8<;t8=N5eyZXWjs2 z*&ThU-SMnh0o&VHuWz$Ivp*h-BWRlI0p{&`qe#Hh+jh0luDaz_9!yXs%cJ-^1UB7)M56=q9k<7tjF_>&&pvl&nfXsD z`9Z@D)u_9k-}}ksM_1zwL{)XNT2v(eej|(qs_~-lp>NewKF7?GNnKH8kRVBE==V;s zW3sjN_@Ov_DVHK_)f!Gsv3V2V z795dZQ1>msEIEB)K&rhAU!*C;(sdZCmu2iR3S&;yO%CKgdtcniu zo6Bca9H+vQA2ZZHh14dTzv+bV|MYzUqoHI=?MU&tktl)Ur3|VVg4P;2$8287Q+<>wiMG^#uwWnl7J2TfZ&viUfFpgW`TA z6t!5r;>6{Q%SFa!HKXy+E_RIK30Nh-sBD$Zjp-cl-UHah6ys$F7W?#p9#1>MT|!pl z-fh6nzrnJO5u$&--?X!J0ntk=8iQRDC5bOLLG(#?eL9m;BKS4o4+NkB!czG-{C&$! zn39%^iB>J9aY!Vj{pw&nOu&RwK`#w#B*n?M)@=8T*WLGgAA(++Hm*kgSxCa7ta%yF z@BFnEpXY)l?om`Z*)vAfYbsau@zv{?tN=|dB-(-@T9!*@?;X17GRJEqyt$u#>mw#S z2fS7g#_Nh3-c&c(?1thE2y|q78rQM@I$id(zInxM?l&rB(Ik6tB_>#h9%j9*hV=_q zx%VO5{Sk2~pewp!+y4XGy-g)5bd~e>7Vc!P7>#0Dt=NZ^*zlLNjXZP`lB!BueC#q6 z7kBM?n9dwr`?1p6Bu~4WRL+f+l3s{J#`Q;|eX0m4-RlR9VV|+x`JF(RAgW z8#rXpl)*cha?|xN!DyVsZ6QRZR7C{_O%MSL+~J;C3tDb@p^)(tad?9o`#3-gH=zh9 zDeh~TCPor%?ywu4G-WhbKqaz=ov-T|k-IU{Lm=y11Wqt+}Mr=P98Yb+sXf zy2zS;5@ps8i~2fGhw69TnaXJ1knud>8MH}wmg+J4iX!?xCCo1bL58>hxx0+-)PUN9 z2IrEEur=}`otvB`?_wNG68yYxsXoD=W8sE`n?nj4=LP$=ntM@RIn?>C8qZ#HOGvf6 z=HUxle6sf+$9LNR;!or$`3iVB^*O!wzZ4qna(N;RKT-OWK@;vVNAW=kX-S_%`^-^VU>(6rX zLNM|Bi< zvwbzJ=@BM3LdQ)Bw0d!~#cah(jRSU~1F=BDoxov$fYd%(#4kKyr_y zcZ`ZwT=CL%B1E;<#a(nS>uDF1L{p6&zlh`T%6{=jr8!NJOF#ZukK*K`i;+;I1bHWxCCmG;?MK>X;xc75y~yvapw_V6ZYsKKt{Ce+fyQl9|@ z&>nwoP>Byg4cH0&k;I@WV2LHa)3q$S8!{DC1Wwu~qfmODr$UH9iX;}xwo5Cn`Ey!G zb{;+&66fzNu}6f*7*?Ep(IDwxvLH>CCCRd7@!kY=Cb6R$=Txm&|Gxb6MG^1_n9DkI zetry12{% z+CW{qA@6<@UU1fi{5WcWURdWNP8ccjZ)t}blG}gGFn){|AC@Pf9s82pX>A%9Fb->))a^LyW1#kX`<4f4T z)AcSdxI0*k&H1Ee8G5!cUYnKki#V`7(E0}ytfMA|fjLbj3tl)m4%UEXM$CDUI1)Ft3&*`;w;+`? z$71>3Lk!O!^DxWS1+0P1aYUuCD(+7SI#Jx=#!+_AyyHcf6~RvF|;jYp~@bk!zd3tE0jq z@KB!RhfpAe2uOgFcdiKj#f16ntJ^8mnNO#V{;bF|8H?CRYYztGiD*R}87Bge@vQdE z_drJc>lw(waS!`pcKg$WQQeW;!Z;?A9i=X&0b)e=Rei?m*bEv9+j0kUoy*Wt&^wUZ zw_@RnQknxa()2V=`soq=1$B?=PPMv1&8r)1hpk$xIo6QV{}d;XFc z?r`B4UI6U3SAdn(>0+djN82z_qmvGh(_9%m4694JHWi!m<(wR!nNH2K0CYha2R0zR!j!PAR%{+w zv#mE@ZC}_`0Xqiy`UbpzJGPbIvy{AtT|Q#AjS3}*W&9C4mk*FN+<_DHX5AY7tzvQ zG%R=kh6+15Y~qyHL+YPg z*Vii$r^{870yUo|TDbI~G|V`Ugj3hny-4~hQ^w;a+OJ$6l$?1)Dn+%uC7()GfP?6+ zAr8jC(!M`hW!*zRus_u7R5f^8xVENo$2lts`Q5@^&moB9mY<@rI%$vsb6yv6bN1Z; ztPdF}7>ANa`vqkF+4c+~21iad3;ez&&$f#(js>P_dW`|xXI!nkj#(Km#Y}~rP{STR z=v?3ll+r*^FB_`Ye7tvzZdK+Me`gx_;_dBXrGEL=>K`gk=IgW{-y}tV`=g5oO<3B^ zm{KSR*O_;%KMx4+>gY9$hR@sHlZ68w%DbxCzlUe3J<#dZs%Z>h;`5S;SO2_hY^;P4 z>XGubEd7HoIZ4wu=O_*c>B#RQ>?4OimXc-@ewlOxH$DDsesOf^S&qv8z^Gas6jCnx z!lmq;Kf`Gg_6Cp{6Ph=t`5N#aua8g|3jG!S+qp3YWG+8q{vKe^kJ;Ir*L2$c_nh>d zcHOySLh4~rdb&goJAVNtIZ4SuT(7SIabntdiWFNGA$wVEEvEB~fke7}bW_UY5caJE zl;faWPEcw4vAqF%Q_fhVE~JO}i8lU)tA#fw|4;i0bu6HHwPIpjJ@hQW+?qg*58@M z)L&xCDWx4^Oq8%=58)@jNjHC=6Vd^xLvei_%qDL^x}ZI>T=y+jcxa zSO!TP<*6b38U@r8~3o>ffFt`ktN2qL>r4H;U$f-NB~W+(X-b zaJD;JK>vz8cL$U)Ztmyj_V3*?eZ6CdKK&n zt8W*02(~ID;q0EDe>cWc_L}$5oj5K!e|b2zCK_qRZ{IjZ5qudOD%2;z_1my!n6Zn{ z`?^lEsj0;bfQ4yEEwQRv8j9T%4FS7Sm@XAYUK^OLOv~P_0JKsdDDpJ(eSr}z+;#F? zSl&LkudEQrIdrn!sZ4N5U0FFtDqJjAz#1KT$4AUY`*XUC1qU0L=Ed# z&R2E>d(NglgdZ0u)xa*n z9_8^V9fP)=911fu8QZ_}BtCm9BwCvf(xUuKf5v`-~oTIpzFe<*-?f=OzUSrzA4e2t%ynd{kd0 zkZd`+uB2BNB7{~lqcG8(#L&ve3H}=%@^L`145oV4R<^3o&uinxah&axKbVL##S)KC z6FHT48NA;nYc8CvpGwq(#Ze$X>J2q~L#LW@q`Hss1RZD6otfBxHJAe;(OV8$OwdQK zTYW~<&|*Tyk>(onlyL{ar*GeQ>U37C?W43aPtxl-Oa|-t62d|rFLjYduEAnEvFbm3 znvsfQjODDZ!Yis}@=hnoc(RP-r3*+lg+f-%^(5=D9qy@ z>RnVCz=`a6io=&{&2D9+My)^8nek;Z{JusP`li{=Xv^T(#*?Ja-o+(8Io~ze%Ok|t z;bJqIK848UUs+ThK?zxtI=6F<1?b5%v9!@rw)6wV^fNd~F0FO+F^xu2t+Y5DhO4RJ z8cuQ`K!~X#T#i7BeTiFj4lD{bRs%&smiTH=@v|J7Q=!RTW}yYPs{8mGktN5nsX8sxM=2{Q7b2Zslfb>^eAHVm2Co(tdew@v7e~Em}9J- zboyh$yniA!$wjm05dt@}qVXsyQB+4na6p!}=ugpqQbC$4JemC6d?5Zo|e#e^w);rQ0PzPjH@887i@F}w}NL51O)U~Y#MkI82rA2G6K&KuLWPy z#=Mf-Xv}DXbE2foQAixMMOz^H!@|O^Bd1qmQIwCC`n$8_G6hJ@CcT^f{076F4Y?cS zJJsR3k(~|lh^jS4w95!mobo)Y;$U(Tsq3>wFF;6?6;BNn+q?yjV(U5+0Yj!rqbboAbcK2;>UK!qrL; z)3FYLu_hcDuS3=5GRL;M$jQ$Ys^1Xx#NJL}&ag`-GxgzNtGL3uL5`!}*t(5Dd%9)* z6YB_izaDg|K`|sG_}Y56*s*A%qsaHR7lJgp=Nnp_-JPPF9nNij}Hbc`LSayyq9ePBjuRmyuDaizglo_6zQ> z7BcoO6hrTI=1;$~_-yX&m?eiXZ}nuM`i}k@dce@jeL?gis_Pu08?&VBoj)l7 zM%3wP7?8zDroPVLz0UFFJTYAN;-F)d-TM;#2!y8(6!iA{8t<_0+991xC~sL#gLVC-L45(Z@k zx~d+>OCtrY!{e6nd3KP9f|bpAsh0P2^|Rgm)XxN(=so>lx`S>1WZKZDcD_;j+4Vh4 zH7`?q%oqYup&s{<`DB|Dp2HC6&cdHQ6o4JONOFbsQr5U>UXxDDeou5Pv_tO$wC>w| zuuxBq$HZgkP)Q!IX4W_Bt5YFxmN{vC;xq=z&Bdo z)G0%^@{%1ov+K_hHDkx!F^dw-k5Yc0Y1D#S*yzoAjs8lp6@W@5NA zS>?E(rDfK$&W#V0qt`?%5*GPS(@D52qY>QM{BvHpi?&#&;X~l|GO|MwoV$zEq{ zjo~?gjHNTNx_jXhrg+h-lq_a8NCVkZvV$8@CAIHqA1xL@u$>e*vBE_j;cgGUOn>HP zCVtv>)DE3#IS4WZ7SkB>*{MEXTT zIL8y(;Z*dLEJy&4K+nf~{Wb)jpYG{ke;91Rn6wXV4;w*}piK4AQ6nQr>=m2@@o8wa z7e{pLVCC7bO`5vK-y*wSEr|Ede9mz+kYP>$?M2%&%lznzNT0epeA&W?@wXA$;t3<5 zZPU_fSHwQ{+58U~Oy5LzTSrx-O=s1cu#^1}?Uam`j&Uihf?Yl76x6Sx%!UB)l zn|E({`W;HcwY0aFu^Qj7SrN^5lRE(w?P%|v7)*lKu)`9#XMf4BPB&eq~i#_NF`p8dv@hW01zxj8HWzB43+v5Ix ziJ@6dO}?eoWHmonY53F6XNOO1p0oKrgI61**tLR@@K+(W9%x@t`+e|$wm0+0>-NEz z&!f}(=K{qgjYo;&>1+9XBnfV8N+5(L+Pdi-$y#}%ik!5pF#PfPTiQf&567roX&7s# z2~~!ig*EdjjLT)MOR*s!o&%IvfvGEzJ7WbhSXcJh#RrlMx8vSL7ti3Cy-MY`g8qs7 zO=BX0&S*f?V2y@mM!V8G%G^pxvWTlJ!pAu-o#%^^ky)^HosqvWysBu^CBQbM^*gp- z$-Bwao@F(DsH4o~{_W+-OkWqk=mFS4lm=+CPq+Tc`>Zk+PC@1DP^hW}s|V44d)eS( z_YwxpUs6w4))hb{6&JF$s~(eyd&>3M2%kGGjdCFW?Ncr~jqpMwq`!AzI)cySh3sot zX}uxzVljj8PNsOuk5V^;>yi0 zMsx3(XNF?&X1CfJBi-6eqKyQ$q<Cb%EaT$E}GMHI|XRD+P@%nPoiy=1_M*%SvM^slK1VXNLcaethM zQUn9|#j1?-NTYW)`4;(%<4YBK^CSWb_;|K&2tdH$7d$+PI}-@@(jrZ)O72ZVavxaKH=*g zbG(zqMwh6HpkTD8CI2v(56|6ti7tqpF*>z$g=?Bn65ADY3lk}qkX-Gat;gtfx1hq4zuzZK`4pfT(&<+Ai+YZdlrT6e35QbqDA|j}e5l?b zqg_}mPE$Jc1>VbxYhcK&v&kd*EA;dJGS}gcv%_ z&+qEh-}A?bhuW7yq{^TO4k_fs!>(K)G>K5N;3p(i8L4|NsD`Ms%MoIg68wHIOJSKMIKeRgq=`~C;1Z2=xOt;8wQf@>~+`V$xd-BMS748u-roo8e?OED2j=^J)NELE9?f*^*uWr%U1243OE;!+?EZ}9^dd+lhq$^*RTof-cTJYtdJylU@J^#j%1&~B zLrO=4kHCkLDw5NNfC6wC_WeHK5&mGAx~Ach*Z9HlMSA?k1Q$T59BAyEZuK4rSCcUy zswc=OGc|ZZkwJWlU5#|)JGvB8Ip^calVX+g58AT)W!&k-l*H6`Ei3Q3*W4pQLGa$q)>BiAPM-yhI^ZIgh?o2-wN>FG0hvmai#(SJ|k-h zDZ$DUmf*Cud`j3Ef@p7y%mK4ZB0PEI7j>Vs<9S@`Ty%Xx2CyH!GH_d_yMejH#nQ*+ z?w^(W8?fS&7cR>_Wt;vD0Fr}@s}JZcUZD@Cz z5L}n4+Y~&mHNN%QIV{9p?M=bHfx4|eG)oj9&NJ{d0G+!{>j74{VM1Mnn=*RcIUI~v zPs|jNK?n1eEIUWpos@2?V(FA_<=qc_%ZI|jUHUY`)M>!lV~MO_vaf$p7^I`Q}Jo7o;n zUGfAK2v6L|CsE?!i#!mHHAZW8?e_zY#Q_xc#;fcP5m66k;6WKayU6s;uA3}0X#Zrn zxqNuFhL`t!H)%b8Txdp@P?GAkT+ z?dd{beLA;;@+VC<2Il)a#gX3f1$RG$qFb~wu|lt=G~rw^NzBx_`?`#g<^)Z$oN6sW zhg2?Gt5{^Gq~fL-lf^=9@o{ z@mon@T3_x`RM^YIGd`=D%Rk_E@A3_NK?j5%Xw%qUXcn395)%Cr&g$BM!xX>J))fZq zNJk`om?6?Mw3uAr{EO19WrV{m&pBrE(4(zdaGbTTfj3m1)$Ww|6B6{^qcG0g>x*mx zd!_04I4qc8?0r5_^a}6}QXSq*Es%tqKE8?I`OIKTr%j;p){v<%3|@$l0wer=2emcAWHb8va`3Q-r=SUC>dxWe zSdnjfZ1jqP$4~BXERtS!Ft{%bVXy=O(dSGt<@C7s9AW8VSkghc$^jNcbjPGVgd;iO)Jfi!uQE9a$9Z?J0;#GZ^p4yS?EgKi$2&OqK=+ zdbU!9qe#oS2LcuKc%MT)hyBTa^jZuWmM=sXg=z{ebI--OOTPxr4iJhYrCN=qx0Ixd zehYe`j%y4XgS6dvPs>bzYYuN}UIFb!_~6H)1GgF-`~liYNSLyDNF71Ln~bj8NnY}G zzo9H{2YY*?hK z?LS9-9Yq^_5_DX=U#+Hvqlb9?b+Hj__>jw^*9@uurEmP;1-*mDHNW8zPZ%0X%a=G= z)p6Gy_i#cy_)TC#Uj0pBH{VVRCUq>bqsE}`;NjJ{V@Y(aHtu39+VPOK_x&i#pOsmN z#zn?VD}Wf^TW}?KD|hxOXwOSR9j8}qw!92rJe=Yib^}A;`RargvI17+1sia79QKDU zl(@}$)3&x{L(TiK=ANq^WU*2p)0l&YJ<^6uSM%d~+aegE#P`OJ(sHdy`7`REuj*`yD!_bJb>UCyR8E<(=1>)}|$`s==$xAgFBygiAB z`y4V>BxHvu-bW1cr)1xBdeq1E5_dee-y%HEvx7*ger2g6gan#&xvRE?M9CggJ3Q*0 zYap9|Q_YeX$wuD!9?Q+JW4OOf%HSg?YEOfTQd`kV-S8U=OZIVPm0&03A(F5zXnx=p z4@7#|QD<-SgHd%}2p6%2$jR*pL@UU@2Nv#F%MeBTQXkC$_Wm^YwW^DjD-yF@AOES0 zxK1fSI2whuR8>Kx{Cyb*5^qPI#fF-i4#MFI-~njcF&G!IoxVddZ~qw7i`fEb)E?13(pa@$oUX&8ly)|H0=M8zT7S2WyF=GeD-eozBn%-y5P#`S= z)$NI=ANzznD5OXvGIi=cMlNp2>sMPE`5Zynp=(cA1Q&!0#;Z$GW?odq!dJdat-ndD zC4fr6o*^S}w>dk%mW|^n4{uAX0Y(KXSoY421))IIM~tVX^<#5IDaR=aFb+X$;4F7>Jg`-*E`hK}65%bw z`wq~NN9{+9TXDI+gOp3pA&*rKvn6;vEkMF(ib-Yn$KgFGK`4Qf858VOlp4*y;B#2( zPM+6vd1l8ty1202{1tW$6z;hHroGb6b9^zl;~oJPn6^9>K0fY!tQ;xS_n8n>97P|N z_J@MTNUs_IVV2&Ip_R>4iVm@Tq`Gna*--106rJbdS1f+yGQ{p$*rUpJ$=+oUD9fE* z?R_lwWzRx;g3M(S_9mLONmTRH|J#;m=%18AmMs*Q8$nni>Ku`u14Nay=*hD`c=bnpfxfjU_msjyo~7fhu(%x?muq*rM3Vh0YsuMyO&H73!>XjiT|nQX-D z-5NHnwsLah?a1USJ(GI}IA(=cF46^Ir%M65GI>;^LS4s?ms0Pb8{M{02^apKZ0NCL zt(A^1EFhm=gnxy+#)kT_oyF$(S24f<(w-lkkO)ES*3jF%-%VJ@B3Y#~SKk--W*5%1 zrGUu(0n>MkJpoQ+nHP_s2>MU;+i};MrG}A&Zy-QfS;#;-46cQy2Ux@bK~PtY^xdwX zuzy5Yg>A2!uXn0xQ6Al55F^yt1Q{C4K2WV2z&ccyp0%LM`-^}l>Fig!PAH5wZHP7! zDWpB2%Km-w2PEz%*WSsAY_#rx9&BcN@7)TR5C7s#+Qv3Q(m?uoRq55@*>7t|G;{); zdwt>Ea-Ni6Hd3H#LR2yO>6Q-e*h7!H^40-+-1UFNJUy3kR`ll3zhk_Lyz}YGW&NJI z2PR>TaSR4iguCIH#HFCi(x+bD&otRAjfIidW|4nU2Sy1Op>88+M0Pz9yNi7&+ryFtOim%TYrX$tdD-J~ z=b%V4LHS{h;VMduev(0M3$uJp^$VlGZU;0ZQ$g=AysB!kX9Gj(Y@(Gl+Zj*3u<-p= zb~t!tt*#;vM9qvBm6ZbPr4sVFUL8uMprCHIpN5E)MQ*IjWY=p5k>NC4@ek-g^Lt|D z%?5OULIf8vwM|rP3+!f0kJ=$TXw?ei5db3qhlnTaOyG~o8_!kor^C*(Y1t%JlWCj0D^+@(a-@r?z6s2X7_@-+HzMnSW0agS zO5*|7n7-BVCOy=1cUzqsG9Zl?^a8nQZludR)MQSu;mppyJ^aRXdK!RWDCwgLqOn9F z;$+lrdMETpE7@7Fi^nPHy9}JcW?km z2uR>p;P;OV7#Ixfry>Xd7ytkR1M&?70f2#i|J(*h07y^}kRO=;S0Dg1IOwMeFaS6( zD8MfS?8hERkdMEAFdzUd0O0>44-5(c3G(v`4h;D*=?C(o5d;7N8VuwY0tondv>;I6 z03c8$ZrYw_*k6X(4QP(zPKE6VLYHQiutLWO6+R<5vWH9S0tpzwsk%dxDfIpBTz!IA zVHUbWZlk4*IOR}8cY3w>oH_#C=n{x#V z{;_6-0WFsF-!7{x!5eyb%$SpUb>YiE@Talm;r*ISd(riWtQ|s%VYbP|uosmCn>R;W z{Yu|0svR7^%m45&*4_KZR&g9-uLdmC)o*(W58p}zCf#~7( zQp0>07r!_)S}2pCO9mUTO`mZN^GVCHN!3LSDK|b&X=;H+4wVUOQt+ifA%ePKxw?TC@McQvpU!-C)}~xF%_n;0sC*~b}8f{ zR3Izavyy*NLg}@PqbmA9-%!F(Lh4l*k-7)L_mc*ye%C;l`ey^Vo8i9!qWYz->aelo zX@EeakwV+woS)cj&KAY%TY!d-g(| zBy&M?gnqcLhwdPPLe?bQ#M|^LMvYiP`R;$QxZ=0*A!TmKx_ZdI@YiLo2`14X0QsO4 z2gZGPK^h`-=}AOLoQ5}N#*G1@7{C>(ImE#C$MKZRR1gR~t8eXZOr0nj#{Eb{$I8lN zT3O@%G8~;IYzT#2J69f5)KbVirlyaIttB@q90842pC{}COefAnpZK%Y6SQI+40^6$91( z`6lj7e2dn}F&WnTB1T;Z+YUK-^x{C^2NGmCy$m~KAWz=Rgg~8!eEgo6CEw-=#%K7^ zLY?q1P}kcQWd)`#^=EbfILr1K9s!^i?Cj1Be^Z!&q8Jv)Y9R4|WV7ZsbL3nc?DVWJ zKQb=@a5M!nlOhR8*;FmGGsxWKASn*)`%>iO3OCukwGVTeW)bmrj7X&PUmW(-6_UyM z?v-WSoZsOeB7t2nrqI1>W6%u_?utU*q_S!P2NYGB`_6{0x-S^V+D_mXV;yuu|7GgU zgwa`arZgrM!A6;Y7%c77$~-{tE%aOj+EBnnGD29tIJ}(sBKQQfng(vv^N3jt4Mj29 z1Pa6k1}cS90eK)DVh2_*`Br3tV)RHD^f(W*ZX)hO6sV8SxSU`Rsqo=DLypkoOFASJ zC?Y_KdCwOM`LfKcH{K${|MS~O@xEUW=8E0guI>4fWp+KO?u&9>pNjuyLq#$VnI?lW zJ+uK~vV&SSycIzac@_CBby4Pt{f<5f~%I z7k;OOC{maOt}A|&DkK(_Nl0lJHCkEIq>MXOUtp}NsR&7+o-P_!CHCo7nZ3=RrUcmK zPh6ubqU}0%Ru6{DRLi%;h6_T3q=z-%Elv1JDg7FU0kQ=;bS8hD1EUdX<=!)9lR4dh z+8XC}NV;LuO7T4#W-qQG!lZ6?9|+^Jxcd2>w7ZzVV3fXw6TW@m!}WUJrNL-xk07U1 zLFpc=CR9mfUVV)pAl?7aQ;2Z?_6z3!YywP3_mt5Ur7Tn}c`sRl_&5r#W@lp0yv_YF zv&v}=p8orJGz@B!w)n!>8;tHz6Xu@Rg5pMr{Wl-3v<>59d;1=U&!Uj}`4vH-o37&n zU$J+F5>d9+d1~J^B4f|-1S@*Xm{fxR#pC$bM9ukqLF-slpI-8k`(lI&Je(Wzir?SP>Qrn3`JL?a#;Ythj`>(h5yCjML^4a*rUYWp zLTL{YZH}=F0+NH1ClaYf-m4^<(iQu5QL4$~z2I$lH8eC{E?iiV0{C{}KJRX1?WKJ{ zaMuWfa=)LtOD19S3Sy2S8p=|GdtwL3PPEqUTWhu!j!;m$7Uo^%uUJYH3_htgrXJYB z-)|WDq)5y6V#aR<1J`rhf2HBy$eUQs=!<3`UxxtZ`#+7qh`e8ai!7;Dw~}sfd4riE zk;1nouNs`IuiT6}LSb0&b05Ms<~?77a<>|rAM7m!J^%XG_Aa}1u1qdT^hF-a-Ki3e^36*-G!@epZ9!l7RV%(iIi$sAUM`K;$VGD`fk=^F z%nzH=p_Alm(UiWxm>K?i^{_420JV!Q=y{>mWTb>H_c)72k7= z2;4j!y?f7m2S7Ion9 zMgl`*#s};u7i~@6i`}`G>u8nE`zbpf2IgyRkqhTYnc$FA#(ab*GR0-+L1zk}w8AS^ zB;4>IJIFa-%|xzfMbuVI-~oO-x(xQHN|C(6HM9v@*6;q3CzJF`7ZKJ;4vEhSe}Qr> zlHXt$<1{T2L-6Q+ci5BQJYb#m+p=u9jazR6sp;$wcH)JOz8l_3gvFDAuG4Z;7+nvg z2WNeN;+CQk@rUrgHg}tUMHzVS8z+7Y+{wIuJRc4ayyaJ#nv zjiu0#Fed&dm~mdyuij*0DY-cb9#hBgmkCLsB124-AWR7e{Mu@wl4xr76B0^AaKODo zBQFq#Cc^j6il7?htYCx)HB;~7y~_;xRjblfT#XFeN6Y&!Qz8x5;#i%j-}TVOv=Q9Z zj`EG>{63+YV}lvOa5CxLj&8TL5;CQNj5!I35V74EhOC&F39IqTFfK+2iY^jI zKDZuYF8~`^3DRfaQKnJcC;5tZ6^LQ8e;_`%Ny5Y{8tR!772FmM8TXB%K|bM$=8C39 zwu~_Kqq?8tLX1C4;GZ|T#Bh{NOLfcVF3^$EJb58l~Kavm6P+3YUm1}&tEfQIu&)F$Vv!BXNIZ^^2}u0 zxyiUqX))l80hZLu8|D%6fPeLEp0Owy*ruZ!UeBi@CPZ+qOlabudI!nJ7g>gOxg^kO z$f$**ANFKtPUtk^*;QY4U zSqTjvc)T{uY`Vy!^^okcKUDRMqef1{g`eSyJ~oFS15h2_hBpK?40A+sl8Uxn_f+gR z4QcM4R;5XW?fWn!LhvBbZ3KK3U|g>vb-HY4lNy)|1mz8HA9bX~r(vt$kaK#^Mi*`> zcEQOO%mFBVeDX)}4}et~ER7%IAM(t0NV3c`CCEPP0~CYRQ2r|fsL`GRK}@)pJjzBm z24j4_l2g8jOWQ`J%iarnJY5Mf~Ko_+9 z7%P-0dt#vn_fM9f?riL|K*AXNpJZmt(g4WTvIOIjY5oawrnq;18cG2~4n1_Oc9W%r z?kRqQ@+Gx)a@O#b*Z!@;=GmkQxWKvX)O!Ldj0Cnhy&0eY`*li}T{75l1dy6sz z)-YhI{sk{idE6keFBHBywQ>D@oN!2rcIxx zm-VtIElK@ol+==@bc0KG$3(IBn|Vy4TIcujKy0(PM=u{(j=PhWo9Y9}Nr(?02cQ$Q z5#VbFR>melZ!U#Mf@fR9l%{TY&Z)F0K)-tcFfO|_&K+RtYBFr6(#&*$XgR!Skms)i zwhw^$PKBeGVq2t}??lyRR|v7c=PjSAS%R6?iRbWh!mVN6x!NRs_g~V}m<77w5mD_f2n};_F;0_Cm_uP&D@~9RD<^>1ND$`+;~Q%Q1Ty72zP2Z1|TC&n?l|nk&qlIg=+( z=b-`g!0OQG0*~Gbx#HwOB6g-S<{e1JwqKDbaPj{(5dcXt zhXu%v{ps7lVs8>@Md{Xj&@1PZ87G8-7q|1Pj55dnkG=o@6!@orzMhN|1g+iHUI!jq z2UKFp{D`tY@!@--h`;5Vm#00qy-DgaOtigBzt6eh?%8X(Or8eLikj@G|B3P0gI>p! zBLodGs*;U!rtNj8J2{pS<&2M{U3;_}!${q&kYc)v4z&T^$#eN}Odl7Z3!?}f2hSxx zXA=%MG%Y=+-dDKZg{QW(%VIC^mJh@*o=KL2s0oxvCQ%v91yy$1Q?!8s|D{i*0frc{$7 z4W;goHMe37B5(Sw=!&cqbE~JGsP4&_dzT)s658yinxium4#40nLz$}8+#MoG*^h&Z zDl)w?B2=y23VlKX2Ycva!U`&S6z9%GPv-QGu?o$;kJ}O1jP`m2k)fe;_I65=f_t@- zc2WCXdH}hO@Q>xv38r}0tXd(|hs;2(t;sW>mF;12xELR>yoO_q-dtp;TJ^yXwb>9; zAO}SXn#l5i`2{=_B}L$`IFo@c7Zz|gmJ-O-@el5uC>e_uWh)WQ;&F7m<)L6y;~0Wf z4NMOY-JJSHVLx9~rq}RXIWjs$HN8FWL(Is9uv6lj7ORvn$q_Gvc@B9NX|l zTj#lt>fsZkl)vr!ophv17!=s={EwM_r2-ARo45QLO~UpuR;Yun7#{+Wl@|}S~v7e$&K8A$Bksp9?@a_T>leq8JZ#rQ%*{I!Ip>-0*v|Sek&hdX!gp-nbFbO);uejlG~`0Z1cC* zM#{6X$+^>GaF^M|Aehmlv5uPTr5uqQ!8{K?plzlX8TlP|(5uxPhw>bPTC^lPp%Po+tKkgwBgwF`fFV*7=m=;`A z_?!|LV8(@;McG{Mm5Tcn=*_S@M^#d;FH!8il&K05a2sblq1e!c1bq?u>W ziWfmGSCnLMtj0yyEA3G7BN%%~Xr}#KhI&M$VSvE_V^$Ns&Akn(1jY-EO<1a?3(Um9 zTO-mL2Oj+h$SI-mVo5sK_!fL!NHX#p{5`FFnW$1>#Rq+o$oO$LZ_CrBv%DkGv-)!PByi{N>Z*b=p(thJi_?^ z$Oke5%WwjzayxQvP^CoqAG&>1A9kPtG6qZ?n%4I*x>;u@)UnZ zi^MC{jzS~KI6ie=h@;!4F$-bG!cXf*lk)8{EFIPnF@jxejfu>cX|^rFg>cEP>>@~* z5Z(ntz?M$jAa-fVy+G~J-+%;a*|46YqB>~<@&oWW5u`vWA}%#Sgdm0^;NS4%fxMxw zjynCYkiuOuN#_Z*zeZ2c3Y zz9(Br!|L%HdX0B!PK{17O+){5qG@m)kNF}|kz^B$%lRzCENVjhC1wJcA=jWF2oYqAfILFb!$2IKZ_;Bs3w zPeT9uA`Y=YV!pgs#?oYW3u|*fA>~q-4}g+nY5-2DH%yrL;<_+BuI(R7W##2IQQ1BF zI_iWrjaW@TmJ!qP2lar-A)S=J;W+N|2X!e5woqFcE@pQzvbPIkh`A-Z`F*Lk(o7dC z*-!afc3w4I^i1} zr`01v%_V&-G2xQ9))8Urh2Ocjs!}j^UWT$0i3$Rza5O5*JNN2&Z>npvFhlv%L+8J) zH!iFW)rEMx<2taINtgyFC4*$rbE(ugu;Zx{I?+YzUHKSP`=le@)TQ*XR$wF@+f8SE zW&Pu99FO$yz1u3~-et?DVOCx_@@`=_(2B}{IFv1bm3ZWr;2;&u8V(T9TY=2uv0B)Q zHnSju18G^sK;`HCF}i&aYYoB;REoq?m&Ss<)SA5L%dAylTGasnScUg}RM=L)`rKhQos$4qdmR*r|E!C(@tt>Nh5=Nl^=K7sleC*zJ zb#94!wD7Dv7AEgO-G)0+ZmyL}h`e?GDz`+9aZ;vDyuA1__`uMJ2}Mj-S~jp<1j}2D zNxRl`J|TIpf5@S)kI{ceSFes1!LJyliJ6R<=8Cv}0Ls;|)KDuHp<2*L_Y8DJ7WL7G zW9Nn^ny=(2iiLcMC}v@*On{E(Z5(Lzp}xUiRa=6IevcqbFsY6zj68rDu`>1j)MgFM zItn9P8>SJ)IYYM%qQP)&WJ6hb?!>9cJ%rQSqJDjZP6mn}^<7x!*(nE({6mbr{F3!Y z&L`t*Pt^8pS5B|Q9d+ly7Z<2>3RwjvKbxK~8JFsg=N|F>>mNU-G{IFmG!$dr5_YlJ zKkZfJ%F_`v>xQNhEVSFk^}4({SGPCOzE2oam58i+vEfv(NC>4iiFRp(0PMc>)>bNv zzQ(Jm8Ta_O(QB=gtTpHe2>`BT)l?c@3a_=;kmMQCzJ};7TGNH0A%-6F8H6G#yKGni zA%#6Vn}cNZ5z{-E$VPu_Yc~tVUvL&JBq#BIl4zNoa=QIR0Z#lo9%petu1vX> zyA)Qv@tInNi>8=Aj}(&%Un`sRA*oV}pEe*5Wo2&orX)ezNazHFQyL$KXaqf!utv_R zK0-TLC(VMpOASwmNdBI84;Hw3kAC6g!$u}s z?5uOz4y}eoZ73v>40LzX#aRr}d$3Tz3rI#?&I?47XpoO3^))B4d?I4EJRUG#2&@tY zqRj5r&&>CMr)MydW}3pw%5ZtQmWCuq9c|xGO;H=rH1tezemyikKNW`0p>DEI4qS+n)oR-@vHJvKhe+G_HG~Zfv{m>_VqD z`zLh-;1Gfa8eC%P`nWyA`km9U^VkVBQNCRyTNM3U3LMgp@H^=>SMZM zPwXr;s2?o*T-W6B1UMZMGhJ~>b?*X0WFh`!02@E3qTUsU=*X@MaS*8)P49ssmHi6c zmg~Y#@HnX8Snw>{QPDc<`?y|-y^|o;KGW?_vm~JGv*Mzo>xLl7EENLx|Fc(Q7l+jm zaqhu95ot)!bT5FoM`ANBg>WId!Q_4Oy> z=VLfS6VTjfZ;mmVzaKud8OiEKVX5yq06JQP^VV+@$$re5|*NYz;sKjH~N zc&{?8^)Y_^V`SmB+fDq-%|8s)#|fhS;JT*Yem2oWJF0_NSd{(wo6RAPjU4e{EpD7$ z%q5i7vVN*_{TDGEn>n|{o9*1+_0{V_MYt` z@9HQZ*!GYjp#neEC*mXD1F07n=G|f(;O9@eom+xxf@o=aPlKJVXMZ=lk-_7?#7qc& zY6MGzS=1*kc_>#hZP;j0aEA`;s@uTQz{zQhSF&|}=i?EAeT{Yf_GzC8AtdovChL0~iXSRHVW5L4-5&aNSfpU~TrdCMuO<#28U8O3rggWGF zmZsNZCPaZDs=E}JyT~z~%G}W>YB&0q;5um;*-{*>$R_8A<{nbW?f-FO(s|2`;=@?K zy)W%*7ApOqw_Un%7 zkmxG{npeRDr$Z{-nog|m!UZlin7{Y={i%r7c<5HE{7KT0>A?7XzabA;;roJi5nK+R zY*}II$>F-oCHR~xFb^T|4#Ln=}E z@R*h9u9y?HlkU$W-H)?4{7ybUg>X^e<7cp^=!rhIsI;L-6RYS`O*BYB z>8-%$8Ly`d;)z`1hhpqFRIiA2?D*O}Y$mbI?2Hz$q8e zv}@8LDPq|hUap4FPqYXzqSVrY5h$Tw=R20z@(a^k7I_z>MED7@Jnt=1M2QJ4Q0sV* z>yh*2#yc7>Jyvuxp~AnsrI!0qUvz~Eik+8sk$}Z@vFe0m*IuXI%JE1k7I|Wm-JnXu z%>+Y(H4nL1wpxw*QB)Cu=Kib-#6P{eaPm5d>K?wSI~hfwgZUZ0KHBuZgS}u`BU~NE z`kl*ER=@)Ri3CLDzjoYBpwbdZsAKMLR(;SbaYj{=H7q3shp61@LO5+yU`TIpmKMzC8dv$uvrq`!%!Hfm4n0bxqO^FHl-g!E~2{Dzr3I`#j z>xgiRH@hONINLK*Ac!Rj-cGw#xP#z^|9vEKnu*KdVcPo z+2(GdsFY34culu5VB}DI-}V?;Yf^l&3V3m#F@oKAq8;JGt9nOYFCvt3U>*KKZjyB> z2~^URupd6su8E!(f2B&jt~z|=S|sgQA_ff6=3}5t06om{>!-645Z!CnI_PyrUAZef zSnj4(GBJ^eLi;1ge{l^pFFaz~bg9>&ht-V6d)$O;I(TZJ8r(TUlPf&S6aKIE-XTbo zplh^k+qP}nwr!iIZQHhO+qSz;+qSKDZoGf+-O(GofB5B~VrNCv&WKf$%BY=d4|r`{ zO54Wf%(x0z(nd>QwAook|LH44Szbe+!zaiY-U<@JL^YOiV^I+kA3AdUKY}igM-s zEC^M6q*h4?n7jD5jncrp=#xXL)x}Pc$EtW#2GfdGS|HH9q6brUXPX{GNVZBv~%vN2K{U7p16*#PYjc z&@J*Hn4kV!;%gI?q=JD3wsO>=GNAVX&(E324s9OQUFT5>i_pMRwD7w^V(=T~AMZ$! zomHiaR4`5!n>nU@fsPK7dzLcwwlpF#$wi(v_4*sIw%W1S8gciY+_Mv%ES#?w1cs{4 zNlbG$*+2fvg3O5lsH7@`-G`dYxb_+P>VngS(^ofoC%0ML%tf*;&Pv*eD=B_Y=h+Q> z=@MA*_=yrQ|2%M5D&KfmDS)B+*pAI55zC+c+MCs`;JscGC>~$XtB*Jqx(PHs-rA<- zw0Rg^sLxS##XDJ5n9e=oPbQZ<;k<{5MS_2~Z-fKBQTeibg33_PmK8WsaAcvrj3|wC z9}9u#hterHj%Ky7F|yh-vM+1=&yszGH456p4tPm*!H<}k;Ta%ZYJl=PF6gQysk}Rcq5k5q%wcN>PI%yX(yuROmt846i=4iz>z%a@Iq;Oi{9O$&#(?|~jvceS z_-5C_mxcZk>SO|KXJNYTEsY0{F4P->K{x8+Tqm@bp}I{b!$=CcZu5MbfaV21E%hp@ z5R-OW^-6Ke%-dO|TF!@zoWOS}aL`~M0wi)<09ZYH#IWfWK#=9_{!;fAes~j2R*7FR z$LVc`q`_FoH@et)QmbiV${*J(k-zK0-&mi{M2TyQYqAdD86>)k3VzkJx`#zv^>$IW z-jg*tW2|`MjfFQ?vZ~;p00GU8Eym&B1k4MZ>{O?y(o73sYTxu;&6xsxAKc;6k6H%E z&91Mt4W5#p2Ets+rc$=EW!yI(R?*}+o3Zosi= zPnL$}fNNO_sWFYs7Bz3;qaLUtx)-}{m!&rj<|WXwe=e3n^~cDhnYhhl@#_btj21!f>8c>e{ zg=^|TUaecsVMl5%7^>rZ$l5DQ{jh;6Hdqib1o`lM_~&oPq3uYgR~ID6cvo91<^l5l z){uuMME2YvV#_7)OH5^FjRAlau5s2Jr!L1e-73|1kXtwMO?MeJ2?5Vw)PM20^Ubpw z9gxmnqbb<3cFq>#5ielRI~&R-C@fO#2FAC zd8p|9fl4LT;%djDr?;Z+Ia9HvJoOR+8tKGu9$U#zGDN4O6qGw}c(v*!7D4I={e0hX2rZ-~nCw1w}|9 z=yNn_M^~vOiwGeyI;+U2lupzBBN^^TZu$_4X`;R`phJO1LW)W-`tsH~F?xdGM9L3t z#={6m7J$*srnYSQh9-PB-5&dmQ2oLFcz}dXfrCs$6_W;Yp|7JO8-DB(S>T+=si})F zS!(Q5C0SipfRjEt!Phu^Y>f!D%hrEyC{`#!`;t}!X;~4%p z!x(!i@B0^?4~Cv0e)&L6sj~A5e_-kx`Vp!e)HZH$&4E)R!dBYW2Rm!$W%f%?iZ3m0 zIIyjFpa%2Tp+&Pqh0@0MJ&&JKMG{kiBV>OvmFB_*qJbuHn05Lo4TZCBg^L1!h{Hke zRf$rrv(rmsw2m9DPgRIKe>G`2%Xx`d@89cC!AfLfG-p&I0Vnm}A%{g=d73X5T$E*| ze2<$Cj&`r~Q#}Qm_SY>|P$b2#FH+Bft_9|`p1UP;;kPGeU^P^dn91pz5P?XD%q|r) zQ)MA>6QLK!8;fysAk^#vLA&w-e8m}yh!9qXz2`8ij`?Xw8<33RKCa-3Rg8B1J&`NN>&hVgpeS#U*(EC*#OgK2Y+YT))6-TxU1 z4KQ_PD*&BA@=o@6{eolfhIp=w?Ga_p;=L_?mTePHf&3M^GW{+QV$%9-)F4D2I_=zY%hh9N?`Z|}jS z!P~&g?;9~LEsSki?h_?s{X16?;MkKz+rr!r^ZiQJB#E9ZW(!I_h(`+3=aP$P2H70? zx5PkAIMo^kY_i`KbRzW4H7^H=zmSf;d|#K(qIyT$KzGxOB{Y8gUW2o%u!{5&RyP9f z&Y9?-jEs+A(c~`yr|8)W#9Dr!r5XBdNN=*Q6;n2tqG(!DnF^Wft;dn6v3V`XU+quT z0?L`udUDKz^+UD2)={c68DXW!C=&4x^wj#8U36d?v@73k_U7U zhh~-v6>nDZ>J-ord;Gig7`NQWvKz?;=rvg8bZV(Sbh$HdM&oBj;yDi-OFf?Tw!%9@ zvzVRNK|}4Xs&||fHLNVbvJd_PihA#%c>Ynvli1E-+8_?TFg#vFi5g;8Ln6#MZ7%|G5AJiJDq~jU@?n6xn(l*O*CR9-Qy1uv7KqFi0hHq{d*#GJi}R}c ztg7FL$XPGKc)gds&al_lJ|dc>(SYSY`)BBIi+!RxIVvaehSWo`+?+umal)gQ@(}m_ zEO-K=q)<55SYyJotz5Mv!5OLlnvF*#eK@F=BN?SAklYWEeV^jr=yLShlMcf55ODS+ z-z6$Wlt?=}uw_BT;t+lFInvFPrubrV-RI4h4qlbB)%p_%ud8b~q~+xv@spnuu`gOd zYpn2xfZT!In2P zm)>dMnWUl}&%ahc9f|FraKN^>sq-RhGZ(EM=-1!p3BQ?QhpnE`67sVW! zbKuNPg6Y&b9zAXyX#%vBc6%P7#C?}J-QSfd!wj_Q9h%shsOMw#_YR3~EW9x1r0Z!5 z@Z&$#P8Y`gK$T#7RcdQdS9^h3Aun-Ch$aSOmpr93uf*+&i-s_r!NJl8OuHdY;7(m9 zk-C0&QYp4Hk<*)v&fw`tvwQn(bL8%(t@%(g&+@@FlcZL@tiQ{i;=XEz@#Cj??xPNa zuFWEHDkp|Jz^o}G$eBfc6=ZLgpy)%h0^0oTi9)EUAJIz$l9;aeIRhT9z7UD|7qe_r zr*mYjW~2R%Xv=@5ga%5xKHqa&dK%{_F*;myy9We;io^1i1O2>5)Vc031a2PL@ZG^j zg#0Hz$E4G^0v_Iq>r{kb)J1XnrBHTug^!;wMq;D)gW=-1IG(vFiSpi41_v$!c}c{c zc2le0mQ9QSdK#0E_#$l~hsXBr?~KhU=p*guLzi++Fc3i32?}qa1YGUihRphVLF_o& z1}Co{KM0qmUeK9-Mno)Or)=2F;UL^^^G=txS3bCTSvB%KU>Gl-Wc*%N?p~4Da5`-d zT&xiMpBBfAg^Fo*qRdoTa>(wp_B)?)w`{z{p)#$|FV ztU||5R(O1>K8u|th1Hqd?fSVppAd$V-HnL?Dk}1M#=6Dlqne_5v|5*}0#Q;wLD?t~ z1W8;;W!I|2f&3(<9h&&CjF74LW=d=JnxZKq2u%Jo)xDhF+f_`XtsNtr-L1*l*;8vj zHjM0E5C+HHY>XrIWb*AMu$JsWrD+q(LvS30mJ{s=9zuMG@jp-20lp#bE6XfOliQP3L(~I=+N^d z@OYN>^{qt;gsk6m8d)M=9?oS5>&m%duRDv;QZfb-L$Ia)SUqo4j$D5a%Bk~LIEZf+ z;1T<0ge71J^eUq@L?HL-FGl-x0;$bbQ^;!GIQ5~;omXAC&&ru%>3Y9+3Q|6N!Stk_ zFBoitmr;497NaYv&BpY2=g+h8i#wqL+=IhL#pn;^bThgk(+QENuTg6_zbd^;yG(_~ zn8IV?>9?B}d46q$hN4Fz=ht~SEK|bLkAmG6a!7wCZhgOHk?n-f)b*yFkVIQ<+1HuB ze|6z@fZPi$X-K_qRawp!+!xWK*^ve2S^(afciMC(0sx{hyEJo^0*0BO= zQr~I~TnTsfL2)E=*u7!N^(>s*6xn3 zH~fvWM(CRlH)4&9W=A+uY%lO8P3A4_y`mXm+sVJ51{G5%pamnkIc28fJts|MK5(5f zfGUZxq|yc0QO)(Ab9x8s@nV%YaGss|Hm~*zhgyb|Md-t8u)mDCB1Z1W4NkLF66muI*DTcK3#R@S)AP<;+4nJ>>&;4bp|-rw5;@~Ajv3{B z^|TMqL9Hh3%465vRB$tg0<}-(@xJH0lBtg=@fmcabZCau7rSjcaU| z9VT_g%Q{P#jrESVDbotBd`j-dM4tM{R2HqqBvaGE-Uy5dT_MF;vZSm3>K~FEN`ku0 z1~Kaxb@C-#c?t&Jecf5O;MWJsw`6E4kguF^HZ)2T#;Z=Hi|`I6=@Z1grRUm>gao@p zzgKl@5ho)s8o91g?yH4ZIj8Ir)XI)I~)&6-s}%7-$CNq(P&139w80hJ)NNsAClKQ z>ahS*j8^?HOQ+WKR+QWJrqcDk{zShDf5hS2avK+|Pmo`s-%xm0YcUeEy2&|VI0-5^ zuA5OP?N=(udzFO1(mU|mlCX)V$?GTWjJPTU= z+U449plc<=GE?)9U~Ouu7K43+H~i$u;{?DwO7nS$7_;`<1D*^hR>-;&e9SGa1Nnxj zB6!4h!=hz6EP-Yp4FA>qMv_;-c=3*VWWD}*=m9WTkhJ$D=&GX_i;5~tjY=9YA?7TA z=~`L_5HzG52Q*lU2R5*SN?Kz?^OYYBL1Wmx>H&GRu_2oIUKT|u>r5t0u_IoXUd6}4 zC7P{@Ia8x#Lvi--^JdHh%C<2$wG0>A$T2S78t*UhS8fX);gf-=0loo{>u2RBi~yKB7ta{kwgLEJbQ^g8yuaNa&%}{TgrUSq!RQP*m>Ct8 zLHjuMZbrV_2MvM+5SMKiG8o^sOC9{JI(5I-aHfIop!>I7JEu45*nt1j7&{rJ=e7;QIT#172o z(fiSX1XfP@HGwcYi`^=CW@&rO8OyoIc=9C;V`_j7f^x~YVH+yJR{N|9p%T8Ok zycT+UquMzo*ezx`#GpLJ^^z*4_!OJ-8DQZxoOI*&3i=@(DGz=nHC6BF2pHt`%!c7>(Mp$2V1OxnG)Nym!%zIQb$dC|9PC-U`UO4fv@ z{4;TRW{+itSuFR|76tiVy`;RV8=2;EhevdcffwvGg^GkP#|j;cP|XJcPwqyGp#}Gf zVIZT!^r8|?l9aZ{DbWY5yE&c%$5u0ZWqd|y%&IMRw-aBqTUMpG2FP-6ZnPF#$$Tr2 zw>?7g-jTt{kj?pleaNnalDUw5SV=x|kea z!$WR`6TChswm|BlybHXC?3>WS25M$#rRrz3eLWS>nGoPr&<)W#RJw2C{D=_o8=4Dg z9?sJ#bJ-s4n9C_ki@-9(s?uaV@Zv<+1&w|3LsNe!h9ftN;g~5Q24}tB9z)4&2xp&` zyrUY#Bsk^1CAIRzTh4xP$mL+17z`px{=9kTz)W#+DLB+2TT*v6^lV!yqXiUsRfUy9){$rjsf## zT~r&&>{i$&3A?IG%VrVrx+RP9qSaFq#8%73>N8Ljbl?^T!gxP4?f9r0}qcbFB4~3 zJJX!=!N2e0eUqQRyOze0_x zG0e=aiN+b>`%6xB!l(N*({8$S*e+D#R166o8&I1V!v;Sm8?x$!eqxLie%J7dd$?_d z2B~xrZkmgEOx+$;6`?sfIY+Hm_&f6utG(m&A310e=|^E+&#FMEG{{FMrqjmWr^hc( zZfta-?QOdHBKr$#+daTf!2r|w>7>=4n^|YKZ6^PaZzV0Olz_V^TqE=PgSa6^#?wx9 zNjM9NYiq^&+y_-SQ)VBtc>$IhpZLF zUS$<~AiYeCn>W|SQd8Ql>wBDo zgVS7E-MYOQh}@Y|XQO|6q(f8AXI|(xcgltF89^WSkm!U{QnFsN@7u*{SK~g{@s)~u z#Ajof3p1j=04+US+;yJ8`GZMZM5SE1K8TZ)hjU3?Dk-BjjdK|k@kjD^6 zutj|n?L>|!I9*SZ0gb+scbkkQFS>+PjY7WL#>)uj--u|h$MeDjQ6SonZxcDN?Ago( zP4PRLCKnxl9y=?e8Ne}^mBB_;v>3MGj(_S~jw~ffj9>V)3#VB81s!6ZCB+~U-t?7i z7REaJ4dNAi)&#_r3{OP4ol{4j((cZs^}Sn;-lAm&X`AM1)#d1|jZP^xYrpNk>!gDQ zn1{L_&VZ;DSdjh;RzKmPuJZTvI94Kj^>+@AUcMF-c^e}A1CuJO%>VW;%&*rteC_E7i_NNAZxNBe>urIyEU{03I#Jn zCULzK077E~uDeT>H1h|{p(5i=d3^D=!^2R-Bzf6@d2K8$*KD2dcKOGDXNk&VG^bF3IyfGi0$gOK8>s>xdHt@_A-7x@_kn zXi8vy#o$JFe%TfhIlxlOybN2W*+Y*G*4cP=Vtd{~+K{Z;-n(N>3j+Azj^%^bokZwe{XEQ>2zRV|;= z5U;pS;OMXM+X5tAsFSfFY0I-dWnNi<|PWP_5=f`{Je{eYgf*W z9C~(Z?8FLu{q&r8IkZ{g){ChsJfvvO&{zccZMI8yQ%7-N9pjh@L7;>P+v17HN0Sk}I$IWp6egjf^O5A8j&^)Z?vkFVr%!If_n4-2g)P zyh=lRf;!tg*AwManS&&15HPi9F8U4+4mFkfb^2i|Bl&J%SWqq`s4m9FPXwfb$*Ar@@K)|3mRk2F&EWh(Y*2VEH*aMwJu$^I=DFzDdKtyM-H!y|1r-?G ztgI`lWlOr%A9R$Y`S<580|Y#uN}&BSqpFYingr=PDtFM+Jk=WkIY;U<(y7R@;4rSM6uJFMt|m+a9%#*N$>Q?1PWU@ru7^8zwN^g~C< zCHy%_5LI>5VaH(J)Fy#`ohuND;i1He_UD~?O4KvI_f;6YS^L@)pY)-;0PDe1`6*); zl2FCcqus2ZQHAB*&NC&RSXJ;ko6<(f-U!m72^V?* zf!U2$_4w=ix|A31tj&kO&UzGG{5j~k>LsOVEE*)us`-k2JV(9d4CbR;Sw3heQG4d- zc5rV>FL;&qWK8BT`ue43W@N)Ee6jacGwl@g)#XFXE}GBz*s#GrvssK6>=#&MzKZO9 zFJjNu0W@CHJ|&K7h1H^Bw{Y&Ac);ji{&b$Dm;;D1wGZ&7wo;&1wy$J)ffH>~ec+D) zsx|jh5Y*}s*spN)wZ6`F`-CDEkoaX}9|a(8EmHT_P&Zs3Tu%r#I-ir7$UnLQOa=(_ zZ02A)BzchuW5o*G9eTJyB;Y~?dMJ-uo1{3OKP`z_?T8veBPzPLrw-c)rv8T^OLegU zBtdyU=Ptxvh@1liEaVFYACRtKMLq7f#qH!tI7m-G^;I zcXhO`(2{C_BUevu8{w9wEMSAge3lrFlen=Aw{ZbvPx-GK_KwBrg|lam@SkCUpNPDZ zghIRpO^SfUS!A`(^kpOfX(p!q}JsCYBZ32~~TQr#V zG+kpnxnx55HJxzsKy)}bMZPhz*u>RrCeyMf$kkVQ=+d$;a$rAm&m8O`y;}3X!DP;p zwP^h!DJt&F1V~=w_PahsLQ2r93D{*oBRP%rf;P5ltc}yW5&l3ytED8(#_-{X3^-lw6?p{kh(0UrQdbqsOkx3$7pvUg+%66*#C zln2K=%=IU=UQm`e+df7vxssdt3xJ)90#r3#Iep#}6%e-Wv*H+`#U5yBT}!T8KH8*} zrBri=gtoD>NzS|d%`TqaMKM*^8~7@P@>|#k)CM_p*=3FM`MjnsuuGC*pE-uObGmuB z{9ab)JF{jgfeOZf_L3_AZDjDntS}D5!zBY3?icM`A|8wQEao3j{&oF;aA$L?nlU_F z9`|xc@S~Vp5oL>2Ya$S;C7)M{$|r#AdFqVP=98m)9e3geX4Kr(e+H5V4%4$gP>W8J$?y(#A#;O2 z>{DChi2fZLeB^{?CScA0G?;E56n(QNw8{|mEK#(VLiqGqG$8bvkF%LvY4^#Rm@3{5 z=dm`w!zv&K-T+@+gYVq|2Op%@_1B6gU3Ov0On?>DaChbLs62zj?cJ z$mM8J2f(7Lf$}v*rI!mb8}x#XxiG#HyPeD|j(ut%(j_ebCwNkN1mzVPa!;8hK z&Q1xh1KSh?RS0ioH6<>AND{ixFl8v-#A+i!(pswQza>R|dBItG^#>mzIF>|Z#Z!n; zSksSM5b7xQdA)fF3Wor=ojL1+U*pxFj?oLT=NT~@bs zKOe1HhJbb3q08asV)uGv4&2d+iUAx)#BcV?nNt@1JmD6Qs{J%A#i$EFXzau7%p~Rj zBDgWhmw0_XllZLAE*Kd-otOUH1!hs&bKt?mn3}wXucdO|oe;#vXM;jV(P$o8rgac% zZD9(>V0Juv+>M9|&o`Gu)2D&BEArgB!Q1-BlnTu68b1>FC;J-Bk7#Q}{{g#1w!=#j zdGbjUvbaCG;FZLCJpk@@Bm75w6PoOaHnbn;X#=XY76&?521*W@mHSuOQ*SaK^Hp&T zIz+ot@P4H1WiRza-m4`wjqgVWL5Ji8UIWD2yFyj|G$RnY^!{{Yq309AW~BC8)bc1(A8*fCn`a&-tYnKKTxc zZwgy3SokOg%ZI4MdqtRv@~E#&fJ)Y{$EubQvSHs!8sudKxIs-(G+wxe zPD|x&>F?c@`w3e>O@kg)%o?N@0giLGjy9M&DLH5}zH_>G8Z8~C@;qmH?q3>IitXtA zI(~fL+gAu%bQTwVPsX41YfqQKL}bzcCXGXAg&iTIu5|ON4@+p`SbTiC^w$M(E9er!{K2s~-DFCCQc$n-U<}KvwY#wB3F6Mbu3+>Z2*)AP)>*~t-l9m$RXiz|dShjb zFhPZ!;17|^DYr^+@oYnsm9I1wBp|W2mz}PvoRz37{kT_B$<14U$mgILbLxofSS5)^ zJ#eJ0h(P_d2AZ9sl=@qOqsH~gqt)mT8@c(`2dZOBJ%bA||7ih+M8&+(vpMzGFe@od5oYO4y|zz(%#-^R-B4{ zp&&;I*wyeW2%=I^uN9SLx^A=Qq`)VC;BmAsC}}|hmBfk6nvTEGzAl^IIFuo{!Vc1s zqoHFsW9uXHF0>zok*s%2umeOlH?w4x;Qzr42erW@tkhe6KCw)AI%7(LZ}^VY&y2__ z=uZxU={Y|FdWceTRYNpD>D%>0|8gkwR>MCFX*bjA7=LCz3bEZ(6?TWb|K-p$gIMOAQB8Nn%o)$G*(C=O|piJE7e5q`#x;WJHtzZ(8Rlk{{?@;7lL9V zuJZOKh^b?`h|o~M@sVN+tad!0j`;R$4m+8?F)Eeh4Ka@%3DV%5FBCVROvyM#%2Jj$ zyFGMCA)uX4*rESX*hdLV_FHfL>wl9)jjxn$fAL+t%zje1axY3?Do_br1Anlr&THUo zXd;72E*+)5tyqMEbA(%TP`<^Y++lm6p4H61-`AQt0-~U!b-hY&p{2Dc@Bihtz<2KjQt}=4+dpnhROSc{ZdS6+O~flz@pCF8J!6 zBW8#f6YK6s543Sc$SG`W_JTjlpzx%+GGjn|2w<6#U|jBFQnq$rzdV|)POynxxD_LI zDi0%M^?;ypR}3;8&)@0N)dGdhnjKUmUTui-pwm0G>Us*^KmjRcti%JNTPLfT_GwXL z#A%%tuJ^Qb#-wJEM0Du2CI|WATi2p(1qTqbC&e{UxB1i;i2Yp(Jx>0fb56wV!bo}V z(Pl%bZN+ohZO^tj2C5O3JA?XuxrUwE_&DHYW*^a9aZpv4UCa)i$eOo6;uW99MfiTC ze>98flX%?;Ks7-FMvebGd(|ZV>KxO(`&K#x==xG{l8x3EjDRIfF%Z|Y$LW5s1k}F6<^C55oO`3L?(im!p5Tvd~ z-cvnFQB#W`d=Ex$OMN>B@eD^daBcc9S@VRK3wTJ>O>QZCc`u& zLly7FjJEwDVvUP?>E%Ke4WgM2{T&+=ozwkmld*)8+D-jbqP{Htc^N>9ete|iT8zsT zvM2@;31aEB7Ao$ZD6j~FCGMqAGq07p;4iIv*E)idl?<=SR?ASF&_IUi^xjH^Bk^Ufm3H`7Y z$1zrNun+pLl+ZwGUj1tQMO?MT`#1CU)6s}QG}}{Yq?rN@NTHmLmF?4^;gu}wW?E0F zH{X*w0?Xodg|^6-oP?IJfwL;&?PM7@K3}b~SsJVP1_n9@tzti&*RUPCd%V;!jl)sP zzNl-tyL^UfZqEeG=NrWVN8P8S#mK!PzZQ?WFm?6T_>fa}kAoXMj6a=L^S zrWtCntC_a$8Q;{E=TU>QZ4~DeShit_q6MY}1?Jwm3OmEkAX=?QY1j_;JAR7@qX&2B zGR{mNPI&|xV9ug`iiEK#9XHz1r!qD5wI=_HqYcHeRKp*C(q9gr)!(jJXrZd)4TsyjFEMtIS`Z6fRfG%<}nUV&7V9D`DaY z4@UH(}HBn$^~K3MzRB2P*Ctdo>@iij$|%hvWL(EF4g8CG3i*Y*-hS zpUoWGX$aTc3*4QDCVY98r8L~%5@O6WH>CpIqC$N-vTW6yJf_a}0fTHfpF+nbI-8@N zj7vj#er&r8odVWj&I3Ijh;KG5z5U??W3{PJ3JkR}0l4*0?XDoz4WOP%y6qj{=gh?~ zm0mCG)s0tQqGqLLFXDxC~3dTxbg4U_w=C*T}I%nB&6< zNIh9l<%wfcLsEAp)EdB%98%|4SjFF@Q2x{%J|PmPm7L=;tML~VcPUQ4|I~qZ^vm0$ zRZ?UCUdLy8qy|5M?uKjl;VRh!e8N0}$MqCU2P26xtvVgy7y=y^(#JA zwKL@dEarp>eQ!TtUIMu`p0vo=&9=6#4nxAdgD3jfS4XpxmKDP_%r4D|0FrmWjN)eH z>d=ljya&Sum6~Y2ZCbG5baVFqxNeDf$HI-*`P4fDU3XFvELJU8>G{)QGa} zrbR(eYMsBvTFr|*6%1}_;qw1YU_)RKZDiJJ%S88WMP!P)a-HX7gF8>ebhI)UukH{! ze#1(xn|mIPBpTk)GL7z-wN!k!1hP$+<`GLcvV=y>9Ja{=-c~W7Un#B?8|^c51xlwg zotq<@aeE(1?-N1oY3kG#(IOxb@Ekz@ZW7E3N zx$K8w=l0jJ581%o$09=(w^BU{1T!#otwR~l&iB8^Bj`?g*CIX>=d>YR7i~7S;`{2# z*|PER4;qpGBd5}#8bJRiNWPmfnm?*utnipZrhGeV0``68cW38a^wOa&wPFstGrmJ* z7KmEKGh~+6sx90C4>?enh+!;YHfVEXWos7%^`+8z?A&17RLY4YyX zm4K-%!K6Z>;E(cbXfV5@`MM z$L(gd10avkfwwB_v4dwhXAm}|swp^lXx|hwI2!w%nAOzpip#~(`4)Ey1M%($?dsc8 zWrVnhPph}Pz+b{D+f<+!w zxw*ErLAG{rPsd^dVX+g)k`zX#bV4~a;lHBZD-A0{5Ta&_mM~T6J0=@Ly;q{Ps?K)v ziLMoa;*wYtQ3^VP6Bcv(GB!w{jhc~!1#vf7n!-fBq8^Z;(EvoMm~|U2$YZ+S0s_rT zuE%Xa4WrGZv*bp46bPquWuSk(ciUc4LhdfUUg76_h}xv)&%|v`Go8{NzL&H3`5gL@@78^-ou;KeVQpaRLRA}%aNdG8|5o83R#-851qS7hYf{|O3*UmWOzoR1sKoVcJKG z4qlwUqHD8-3H%04g@ob-abo)>e|mbLH6izr_7zhE3mzMHvP`Y5tcb{kk4j`HS8dL? zm9(URsy0fu!!}^HTy>TyaOu*aS2n%~ zNkt<6si}A)UG1KdOEB2hV8)W(78;0zb%j(JPJ4n%^^gqIeuUYP8850KqPa3=M-wRI z>dz1EDi7Frd&Zi!tVv<7*WIq$UJ?D9Qaw)(;eSZhA|DHP0a# zc`c3JKfW}Apf0ol1Z7(jG6qZ84%JB!d6Y!yWa$d?_eiHeo>g|G_$6+Ug#|p?$jXAp zvVe=0jL{*vJTf2(Av#9)kzJ;Dy8o61jsA{jZuul8K(6U5$Q7el=muF0!?GSd@%jso zFjNBfX?KGrr7xTp)KUYAW#N;=GeiD%6NgQv7ywGG>Unzd2NIc-H`j@AT`WXf8dq|* z#A%EPTs10Y7J=qWg_DdZVPRDJ4Q!`uc%U1A#j~^aDoSk?iQ-g8hm9mP3^ArPW3KrE3Q>_Q90n z5UH|LkImS5=i}OtCJlG7X%aYAUV6ePqEzkP#z2W7Q6rRnd3T|+l-sxI(sJCHeye(^88 z>%w}AyJ8*w)u@QkCo?jmBeoL&Hi$`4DMZs}6#BzAK7LVvR@lA~{|7OX&ab(s@Dgmr zsoHecZT-z@^LGa3Z9!0TB8u!yf54sVd8iQ!Y2I`IP10Dn4w>e=nno|1_ z@k551<^?zvxSJ`fvzl>6d&S&Gd2-%Itg9~%w$M^+H_Kwq zsxQALyv?(9T*3Ow4am6P157oEm8Xii0>#W7&9WH;oEAdOPKZ4T^vrdWlhl`pSakPP zb{C;|OTW$jk+uGHh4Jnt&{KnwoAz6D-s6HNLZA`)X4sg*vEGInGb`2G$G+UD7p#Jo zvv-@5R{w?hs#k{O%|A6to5dN3v{{qDSNbz5u{{iEFA^b1T{{@Ku zk>bBR{{zPVLik^v{|gZRBLz;2=hpjw%)wxr$YV?2yL)ovy>c$L@~rAc1Lm z$QZICZ@O9&FQ?5!_DnlaeRDUD;G1I8ZLb>1;%X*wkAe7)JpWe+Byt~wf8udn^TT3J z&HTI`Yi~JqWlUCDAO;iA0~Ino=}Pza|9yZc$}}M+sUaaoYT1zXozHXcTOk?{`;Ykj kUTs)-t^YW@8UWei6`mhBXc|lB|E%$UzSqJI^!{D{AApeo2LJ#7 literal 0 HcmV?d00001 diff --git a/untwine/FileDimInfo.hpp b/untwine/FileDimInfo.hpp index 057dba1..11b72e9 100644 --- a/untwine/FileDimInfo.hpp +++ b/untwine/FileDimInfo.hpp @@ -22,7 +22,12 @@ struct FileDimInfo FileDimInfo() : shift(-1), extraDim(false) {} - FileDimInfo(const std::string& name) : name(name), shift(-1), extraDim(false) + FileDimInfo(const std::string& name) : name(name), type(pdal::Dimension::Type::None), + shift(-1), extraDim(false) + {} + + FileDimInfo(const std::string& name, pdal::Dimension::Type type) : name(name), type(type), + shift(-1), extraDim(false) {} std::string name;