From 5ad103cba71b9db57254ed65dc9d7dbfbdd0a58b Mon Sep 17 00:00:00 2001 From: Michael Isard Date: Thu, 17 Apr 2014 16:29:34 -0700 Subject: [PATCH] Initial v0.1.2 commit --- ClusterInterface/ClusterInterface.csproj | 60 +- ClusterInterface/Properties/AssemblyInfo.cs | 4 +- ClusterInterface/packages.config | 14 +- .../DryadLinqGraphManager.csproj | 62 +- .../Properties/AssemblyInfo.cs | 4 +- DryadLinqGraphManager/packages.config | 14 +- DryadLinqTests/ApplyAndForkTests.cs | 270 ++++ DryadLinqTests/BasicAPITests.cs | 1087 +++++++++++++++++ DryadLinqTests/DryadLinqTests.csproj | 82 +- DryadLinqTests/GroupByReduceTests.cs | 1003 +++++++++++++++ DryadLinqTests/Program.cs | 81 +- DryadLinqTests/Properties/AssemblyInfo.cs | 4 +- .../RangePartitionAPICoverageTests.cs | 12 + DryadLinqTests/Utils.cs | 157 +++ DryadLinqTests/Validate.cs | 267 ++++ DryadLinqTests/packages.config | 14 +- .../VertexHost/system/channel/channel.vcxproj | 12 +- .../VertexHost/system/channel/packages.config | 6 +- .../system/dprocess/dprocess.vcxproj | 12 +- .../system/dprocess/packages.config | 6 +- .../managedchannel/Properties/AssemblyInfo.cs | 4 +- .../managedchannel/managedchannel.csproj | 62 +- .../system/managedchannel/packages.config | 14 +- .../vertex/vertexHost/VertexHost.vcxproj | 12 +- .../vertex/vertexHost/packages.config | 6 +- GraphManager/GraphManager.vcxproj | 12 +- GraphManager/GraphManager.vcxproj.filters | 4 - GraphManager/packages.config | 2 +- JobBrowser/JOM/ClusterConfiguration.cs | 89 +- JobBrowser/JOM/ClusterStatus.cs | 134 +- JobBrowser/JOM/JobObjectModel.csproj | 101 +- JobBrowser/JOM/Properties/AssemblyInfo.cs | 8 +- JobBrowser/JOM/app.config | 8 +- JobBrowser/JOM/clusteraccess.cs | 31 +- JobBrowser/JOM/{cosmos.cs => dryadlog.cs} | 12 +- JobBrowser/JOM/jobinfo.cs | 268 ++-- JobBrowser/JOM/packages.config | 14 +- JobBrowser/JOM/storage.cs | 6 +- JobBrowser/JobBrowser.sln | 50 +- JobBrowser/JobBrowser/ClusterBrowser.cs | 100 +- .../JobBrowser/ClusterBrowser.designer.cs | 63 +- JobBrowser/JobBrowser/ClusterBrowser.resx | 3 - .../ClusterConfigEditor.Designer.cs | 2 +- JobBrowser/JobBrowser/ClusterConfigEditor.cs | 4 +- JobBrowser/JobBrowser/Diagnosis.cs | 96 +- .../JobBrowser/DiagnosisResult.Designer.cs | 2 +- JobBrowser/JobBrowser/DiagnosisResult.cs | 4 +- JobBrowser/JobBrowser/DryadJobMain.cs | 2 +- JobBrowser/JobBrowser/JobBrowser.Designer.cs | 33 +- JobBrowser/JobBrowser/JobBrowser.cs | 274 ++--- JobBrowser/JobBrowser/JobBrowser.csproj | 149 ++- JobBrowser/JobBrowser/JobBrowser.resx | 3 - .../JobBrowser/LocalDebuggingAndProfiling.cs | 8 +- JobBrowser/JobBrowser/LogViewer.Designer.cs | 4 +- JobBrowser/JobBrowser/LogViewer.cs | 22 +- .../JobBrowser/Properties/AssemblyInfo.cs | 12 +- JobBrowser/JobBrowser/jobschedule.cs | 4 +- JobBrowser/JobBrowser/packages.config | 22 +- .../Microsoft.Research.JobBrowser.nuspec | 48 - JobBrowser/Tools/Graphlayout.cs | 2 +- JobBrowser/Tools/Properties/AssemblyInfo.cs | 8 +- JobBrowser/Tools/Tools.csproj | 29 +- JobBrowser/Tools/drawingSurface.cs | 2 +- JobBrowser/Tools/partitionedTables.cs | 2 +- JobBrowser/Tools/tools.cs | 111 +- .../FilteredDataGridView.Designer.cs | 4 +- .../UsefulForms/FilteredDataGridView.cs | 2 +- .../UsefulForms/PasswordDialog.Designer.cs | 2 +- JobBrowser/UsefulForms/PasswordDialog.cs | 2 +- .../UsefulForms/Properties/AssemblyInfo.cs | 8 +- JobBrowser/UsefulForms/Status.cs | 2 +- JobBrowser/UsefulForms/UrlDialog.Designer.cs | 2 +- JobBrowser/UsefulForms/UrlDialog.cs | 2 +- JobBrowser/UsefulForms/UsefulForms.csproj | 30 +- JobBrowser/UsefulForms/WarningBox.Designer.cs | 2 +- JobBrowser/UsefulForms/WarningBox.cs | 2 +- JobBrowser/doc/clusterBrowser.png | Bin 0 -> 25940 bytes JobBrowser/doc/clusterEditor.png | Bin 0 -> 7595 bytes JobBrowser/doc/clusterManipulation.png | Bin 0 -> 4225 bytes JobBrowser/doc/clusterMenu.png | Bin 0 -> 2267 bytes JobBrowser/doc/dynamicPlan.png | Bin 0 -> 4586 bytes JobBrowser/doc/filteringVertex.png | Bin 0 -> 1982 bytes JobBrowser/doc/jobBrowser.png | Bin 0 -> 507358 bytes JobBrowser/doc/jobBrowserManual.docx | Bin 913804 -> 914025 bytes JobBrowser/doc/jobFiltering.png | Bin 0 -> 1890 bytes JobBrowser/doc/jobMenu.png | Bin 0 -> 3078 bytes JobBrowser/doc/jobPlan.png | Bin 0 -> 10514 bytes JobBrowser/doc/jobSchedule.png | Bin 0 -> 19143 bytes JobBrowser/doc/jobSummary.png | Bin 0 -> 8036 bytes JobBrowser/doc/stageCode.png | Bin 0 -> 5218 bytes JobBrowser/doc/stageFiltering.png | Bin 0 -> 2652 bytes JobBrowser/doc/stageStatistics.png | Bin 0 -> 13048 bytes JobBrowser/doc/stageVertices.png | Bin 0 -> 22319 bytes JobBrowser/doc/staticPlan.png | Bin 0 -> 201883 bytes JobBrowser/doc/vertexState.png | Bin 0 -> 27364 bytes JobBrowser/doc/vertexStatistics.png | Bin 0 -> 14885 bytes JobBrowser/doc/viewMenu.png | Bin 0 -> 4103 bytes ...ft.Research.DryadLINQ.MSAGL.3.0.0.1.nuspec | 27 - .../lib/Microsoft.Msagl.Drawing.dll | Bin 134952 -> 0 bytes .../lib/Microsoft.Msagl.GraphViewerGdi.dll | Bin 171304 -> 0 bytes .../lib/Microsoft.Msagl.dll | Bin 834344 -> 0 bytes LinqToDryad/Attributes.cs | 102 +- LinqToDryad/BitVector.cs | 45 +- LinqToDryad/CodeGenHelper.cs | 4 +- LinqToDryad/Constants.cs | 27 +- LinqToDryad/DataProvider.cs | 295 +++-- LinqToDryad/DryadLinqBinaryReader.cs | 82 +- LinqToDryad/DryadLinqBinaryWriter.cs | 113 +- LinqToDryad/DryadLinqBlockStream.cs | 2 +- LinqToDryad/DryadLinqCodeGen.cs | 26 +- LinqToDryad/DryadLinqCollection.cs | 73 +- LinqToDryad/DryadLinqContext.cs | 693 ++++++----- LinqToDryad/DryadLinqDecomposition.cs | 10 +- LinqToDryad/DryadLinqEnumerable.cs | 11 +- LinqToDryad/DryadLinqException.cs | 47 +- LinqToDryad/DryadLinqExtension.cs | 79 +- LinqToDryad/DryadLinqFactory.cs | 34 + LinqToDryad/DryadLinqFaultCodes.cs | 74 +- LinqToDryad/DryadLinqGlobals.cs | 2 +- LinqToDryad/DryadLinqHelper.cs | 43 +- LinqToDryad/DryadLinqJobExecutor.cs | 23 +- LinqToDryad/DryadLinqLog.cs | 45 +- LinqToDryad/DryadLinqMetaData.cs | 3 + LinqToDryad/DryadLinqNative.cs | 2 +- LinqToDryad/DryadLinqObjectStore.cs | 13 +- LinqToDryad/DryadLinqQuery.cs | 41 +- LinqToDryad/DryadLinqQueryGen.cs | 27 +- LinqToDryad/DryadLinqQueryNode.cs | 4 +- LinqToDryad/DryadLinqQueryRuntime.cs | 52 +- LinqToDryad/DryadLinqQueryable.cs | 885 ++++++++++++-- LinqToDryad/DryadLinqRecordReader.cs | 2 + LinqToDryad/DryadLinqRecordWriter.cs | 2 + LinqToDryad/DryadLinqSampler.cs | 2 + LinqToDryad/DryadLinqSerialization.cs | 21 +- LinqToDryad/DryadLinqTextReader.cs | 47 +- LinqToDryad/DryadLinqTextWriter.cs | 63 +- LinqToDryad/DryadLinqVertex.cs | 23 +- LinqToDryad/DryadLinqVertexParams.cs | 42 + LinqToDryad/DryadLinqVertexReader.cs | 10 +- LinqToDryad/DryadLinqVertexWriter.cs | 10 +- LinqToDryad/DscClientHelper.cs | 553 --------- LinqToDryad/DscStubs.cs | 170 --- LinqToDryad/ForkTuple.cs | 222 +++- LinqToDryad/IAssociative.cs | 31 + LinqToDryad/IDecomposable.cs | 72 ++ LinqToDryad/LineRecord.cs | 81 +- LinqToDryad/LinqToDryad.csproj | 71 +- LinqToDryad/LocalJobSubmission.cs | 36 +- LinqToDryad/MultiEnumerable.cs | 2 + LinqToDryad/MultiQueryable.cs | 88 +- LinqToDryad/NativeBlockStream.cs | 7 +- LinqToDryad/PeloponneseJobSubmission.cs | 54 +- LinqToDryad/Properties/AssemblyInfo.cs | 33 + LinqToDryad/QueryTraceLevel.cs | 26 +- LinqToDryad/VertexEnv.cs | 75 +- LinqToDryad/YarnJobSubmission.cs | 70 +- LinqToDryad/YarnScheduler.cs | 48 - LinqToDryad/packages.config | 14 +- LocalScheduler/LocalScheduler.csproj | 62 +- LocalScheduler/Properties/AssemblyInfo.cs | 4 +- LocalScheduler/packages.config | 14 +- Microsoft.Research.Dryad.nuspec | 25 +- ProcessService/Cache.cs | 3 + ProcessService/ProcessService.cs | 2 +- ProcessService/ProcessService.csproj | 62 +- ProcessService/Properties/AssemblyInfo.cs | 4 +- ProcessService/SpillMachine.cs | 2 + ProcessService/packages.config | 14 +- README.txt | 38 +- XmlDoc/Content/GettingStarted.aml | 85 ++ .../Building the Job Browser.aml | 130 ++ XmlDoc/Content/GettingStarted/QuickStart.aml | 154 +++ .../GettingStarted/SettingUpCluster.aml | 101 ++ XmlDoc/Content/Resources.aml | 68 ++ .../Resources/Running a job on HDInsight.aml | 174 +++ .../Content/VersionHistory/VersionHistory.aml | 29 + XmlDoc/Content/VersionHistory/v0.1.2.aml | 28 + XmlDoc/Content/Welcome.aml | 62 + XmlDoc/ContentLayout.content | 26 + XmlDoc/Media/Dryad on Azure Architecture.png | Bin 0 -> 134100 bytes XmlDoc/XmlDoc.shfbproj | 108 ++ XmlDoc/XmlDoc.sln | 20 + ...icrosoft.Research.DryadLinq.Samples.nuspec | 10 +- samples/WordCount.cs.pp | 56 +- 184 files changed, 8356 insertions(+), 2759 deletions(-) create mode 100644 DryadLinqTests/ApplyAndForkTests.cs create mode 100644 DryadLinqTests/BasicAPITests.cs create mode 100644 DryadLinqTests/GroupByReduceTests.cs create mode 100644 DryadLinqTests/RangePartitionAPICoverageTests.cs create mode 100644 DryadLinqTests/Utils.cs create mode 100644 DryadLinqTests/Validate.cs rename JobBrowser/JOM/{cosmos.cs => dryadlog.cs} (97%) delete mode 100644 JobBrowser/Microsoft.Research.JobBrowser.nuspec create mode 100644 JobBrowser/doc/clusterBrowser.png create mode 100644 JobBrowser/doc/clusterEditor.png create mode 100644 JobBrowser/doc/clusterManipulation.png create mode 100644 JobBrowser/doc/clusterMenu.png create mode 100644 JobBrowser/doc/dynamicPlan.png create mode 100644 JobBrowser/doc/filteringVertex.png create mode 100644 JobBrowser/doc/jobBrowser.png create mode 100644 JobBrowser/doc/jobFiltering.png create mode 100644 JobBrowser/doc/jobMenu.png create mode 100644 JobBrowser/doc/jobPlan.png create mode 100644 JobBrowser/doc/jobSchedule.png create mode 100644 JobBrowser/doc/jobSummary.png create mode 100644 JobBrowser/doc/stageCode.png create mode 100644 JobBrowser/doc/stageFiltering.png create mode 100644 JobBrowser/doc/stageStatistics.png create mode 100644 JobBrowser/doc/stageVertices.png create mode 100644 JobBrowser/doc/staticPlan.png create mode 100644 JobBrowser/doc/vertexState.png create mode 100644 JobBrowser/doc/vertexStatistics.png create mode 100644 JobBrowser/doc/viewMenu.png delete mode 100644 JobBrowser/packages/Microsoft.Research.DryadLINQ.MSAGL.3.0.0.1/Microsoft.Research.DryadLINQ.MSAGL.3.0.0.1.nuspec delete mode 100644 JobBrowser/packages/Microsoft.Research.DryadLINQ.MSAGL.3.0.0.1/lib/Microsoft.Msagl.Drawing.dll delete mode 100644 JobBrowser/packages/Microsoft.Research.DryadLINQ.MSAGL.3.0.0.1/lib/Microsoft.Msagl.GraphViewerGdi.dll delete mode 100644 JobBrowser/packages/Microsoft.Research.DryadLINQ.MSAGL.3.0.0.1/lib/Microsoft.Msagl.dll delete mode 100644 LinqToDryad/DscClientHelper.cs delete mode 100644 LinqToDryad/DscStubs.cs create mode 100644 LinqToDryad/Properties/AssemblyInfo.cs delete mode 100644 LinqToDryad/YarnScheduler.cs create mode 100644 XmlDoc/Content/GettingStarted.aml create mode 100644 XmlDoc/Content/GettingStarted/Building the Job Browser.aml create mode 100644 XmlDoc/Content/GettingStarted/QuickStart.aml create mode 100644 XmlDoc/Content/GettingStarted/SettingUpCluster.aml create mode 100644 XmlDoc/Content/Resources.aml create mode 100644 XmlDoc/Content/Resources/Running a job on HDInsight.aml create mode 100644 XmlDoc/Content/VersionHistory/VersionHistory.aml create mode 100644 XmlDoc/Content/VersionHistory/v0.1.2.aml create mode 100644 XmlDoc/Content/Welcome.aml create mode 100644 XmlDoc/ContentLayout.content create mode 100644 XmlDoc/Media/Dryad on Azure Architecture.png create mode 100644 XmlDoc/XmlDoc.shfbproj create mode 100644 XmlDoc/XmlDoc.sln diff --git a/ClusterInterface/ClusterInterface.csproj b/ClusterInterface/ClusterInterface.csproj index 64a32ea..e2f16ff 100644 --- a/ClusterInterface/ClusterInterface.csproj +++ b/ClusterInterface/ClusterInterface.csproj @@ -1,6 +1,6 @@ - + - + Debug @@ -51,15 +51,36 @@ False - ..\packages\Microsoft.Research.Peloponnese.0.7.1-beta\lib\net45\Microsoft.Research.Peloponnese.HadoopBridge.dll + ..\packages\Microsoft.Research.Peloponnese.0.7.2-beta\lib\net45\Microsoft.Research.Peloponnese.HadoopBridge.dll - + False - ..\packages\Microsoft.Research.Peloponnese.0.7.1-beta\lib\net45\Microsoft.Research.Peloponnese.Utils.dll + ..\packages\Microsoft.Research.Peloponnese.0.7.2-beta\lib\net45\Microsoft.Research.Peloponnese.Utils.dll + + + False + ..\packages\Microsoft.Bcl.Async.1.0.166\lib\net40\Microsoft.Threading.Tasks.dll + + + ..\packages\Microsoft.Bcl.Async.1.0.166\lib\net40\Microsoft.Threading.Tasks.Extensions.dll + + + ..\packages\Microsoft.Bcl.Async.1.0.166\lib\net40\Microsoft.Threading.Tasks.Extensions.Desktop.dll + + + False + ..\packages\Microsoft.WindowsAzure.Common.1.0.1\lib\net45\Microsoft.WindowsAzure.Common.dll + + + False + ..\packages\Microsoft.WindowsAzure.Common.1.0.1\lib\net45\Microsoft.WindowsAzure.Common.NetFramework.dll ..\packages\Microsoft.WindowsAzure.ConfigurationManager.2.0.3\lib\net40\Microsoft.WindowsAzure.Configuration.dll + + ..\packages\Microsoft.WindowsAzure.Management.1.0.0\lib\net40\Microsoft.WindowsAzure.Management.dll + False ..\packages\Microsoft.WindowsAzure.Management.HDInsight.1.1.0.7\lib\net40\Microsoft.WindowsAzure.Management.HDInsight.dll @@ -72,17 +93,29 @@ False ..\packages\Microsoft.Hadoop.Client.1.1.0.7\lib\net40\Microsoft.WindowsAzure.Management.HDInsight.Framework.Core.dll + + False + ..\packages\Microsoft.WindowsAzure.Management.Storage.1.0.0\lib\net40\Microsoft.WindowsAzure.Management.Storage.dll + False ..\packages\WindowsAzure.Storage.3.1.0.1\lib\net40\Microsoft.WindowsAzure.Storage.dll - - False + ..\packages\Newtonsoft.Json.6.0.2\lib\net45\Newtonsoft.Json.dll + + + + ..\packages\Microsoft.Net.Http.2.2.19\lib\net45\System.Net.Http.Extensions.dll + + + ..\packages\Microsoft.Net.Http.2.2.19\lib\net45\System.Net.Http.Primitives.dll + + False ..\packages\System.Spatial.5.6.1\lib\net40\System.Spatial.dll @@ -105,14 +138,19 @@ + + + + + This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - + + - + - + \ No newline at end of file diff --git a/ClusterInterface/Properties/AssemblyInfo.cs b/ClusterInterface/Properties/AssemblyInfo.cs index eab0852..ace9a43 100644 --- a/ClusterInterface/Properties/AssemblyInfo.cs +++ b/ClusterInterface/Properties/AssemblyInfo.cs @@ -51,5 +51,5 @@ using System.Runtime.InteropServices; // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("0.1.0.0")] -[assembly: AssemblyFileVersion("0.1.0.0")] +[assembly: AssemblyVersion("0.1.2.0")] +[assembly: AssemblyFileVersion("0.1.2.0")] diff --git a/ClusterInterface/packages.config b/ClusterInterface/packages.config index 6cc3081..bd29d67 100644 --- a/ClusterInterface/packages.config +++ b/ClusterInterface/packages.config @@ -1,13 +1,21 @@ - + + + + - + + + + + + - + \ No newline at end of file diff --git a/DryadLinqGraphManager/DryadLinqGraphManager.csproj b/DryadLinqGraphManager/DryadLinqGraphManager.csproj index f7e3948..2619ed3 100644 --- a/DryadLinqGraphManager/DryadLinqGraphManager.csproj +++ b/DryadLinqGraphManager/DryadLinqGraphManager.csproj @@ -1,6 +1,6 @@ - + - + Debug AnyCPU @@ -71,15 +71,39 @@ False - ..\packages\Microsoft.Research.Peloponnese.0.7.1-beta\lib\net45\Microsoft.Research.Peloponnese.HadoopBridge.dll + ..\packages\Microsoft.Research.Peloponnese.0.7.2-beta\lib\net45\Microsoft.Research.Peloponnese.HadoopBridge.dll - + False - ..\packages\Microsoft.Research.Peloponnese.0.7.1-beta\lib\net45\Microsoft.Research.Peloponnese.Utils.dll + ..\packages\Microsoft.Research.Peloponnese.0.7.2-beta\lib\net45\Microsoft.Research.Peloponnese.Utils.dll + + + False + ..\packages\Microsoft.Bcl.Async.1.0.166\lib\net40\Microsoft.Threading.Tasks.dll + + + False + ..\packages\Microsoft.Bcl.Async.1.0.166\lib\net40\Microsoft.Threading.Tasks.Extensions.dll + + + False + ..\packages\Microsoft.Bcl.Async.1.0.166\lib\net40\Microsoft.Threading.Tasks.Extensions.Desktop.dll + + + False + ..\packages\Microsoft.WindowsAzure.Common.1.0.1\lib\net45\Microsoft.WindowsAzure.Common.dll + + + False + ..\packages\Microsoft.WindowsAzure.Common.1.0.1\lib\net45\Microsoft.WindowsAzure.Common.NetFramework.dll ..\packages\Microsoft.WindowsAzure.ConfigurationManager.2.0.3\lib\net40\Microsoft.WindowsAzure.Configuration.dll + + False + ..\packages\Microsoft.WindowsAzure.Management.1.0.0\lib\net40\Microsoft.WindowsAzure.Management.dll + False ..\packages\Microsoft.WindowsAzure.Management.HDInsight.1.1.0.7\lib\net40\Microsoft.WindowsAzure.Management.HDInsight.dll @@ -92,6 +116,10 @@ False ..\packages\Microsoft.Hadoop.Client.1.1.0.7\lib\net40\Microsoft.WindowsAzure.Management.HDInsight.Framework.Core.dll + + False + ..\packages\Microsoft.WindowsAzure.Management.Storage.1.0.0\lib\net40\Microsoft.WindowsAzure.Management.Storage.dll + False ..\packages\WindowsAzure.Storage.3.1.0.1\lib\net40\Microsoft.WindowsAzure.Storage.dll @@ -108,6 +136,17 @@ 3.5 + + + + False + ..\packages\Microsoft.Net.Http.2.2.19\lib\net45\System.Net.Http.Extensions.dll + + + False + ..\packages\Microsoft.Net.Http.2.2.19\lib\net45\System.Net.Http.Primitives.dll + + False ..\packages\System.Spatial.5.6.1\lib\net40\System.Spatial.dll @@ -164,14 +203,19 @@ + + + + + This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - + + - + - + \ No newline at end of file diff --git a/DryadLinqGraphManager/Properties/AssemblyInfo.cs b/DryadLinqGraphManager/Properties/AssemblyInfo.cs index ea2eb9c..bc4e5c4 100644 --- a/DryadLinqGraphManager/Properties/AssemblyInfo.cs +++ b/DryadLinqGraphManager/Properties/AssemblyInfo.cs @@ -51,5 +51,5 @@ using System.Runtime.InteropServices; // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("0.1.0.0")] -[assembly: AssemblyFileVersion("0.1.0.0")] +[assembly: AssemblyVersion("0.1.2.0")] +[assembly: AssemblyFileVersion("0.1.2.0")] diff --git a/DryadLinqGraphManager/packages.config b/DryadLinqGraphManager/packages.config index 6cc3081..bd29d67 100644 --- a/DryadLinqGraphManager/packages.config +++ b/DryadLinqGraphManager/packages.config @@ -1,13 +1,21 @@ - + + + + - + + + + + + - + \ No newline at end of file diff --git a/DryadLinqTests/ApplyAndForkTests.cs b/DryadLinqTests/ApplyAndForkTests.cs new file mode 100644 index 0000000..b523841 --- /dev/null +++ b/DryadLinqTests/ApplyAndForkTests.cs @@ -0,0 +1,270 @@ +using Microsoft.Research.DryadLinq; +using Microsoft.Research.Peloponnese.Storage; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace DryadLinqTests +{ + public class ApplyAndForkTests + { + public static IEnumerable NonHomomorphic_Unary_Func(IEnumerable input) + { + return input; + } + + // [DistributiveOverConcat] + public static IEnumerable Homomorphic_Unary_Func(IEnumerable input) + { + return input; + } + + public static IEnumerable NonHomomorphic_Binary_Func(IEnumerable left, IEnumerable right) + { + return left; + } + + // [LeftDistributiveOverConcat] + public static IEnumerable LeftHomomorphic_Binary_Func(IEnumerable left, IEnumerable right) + { + return left; + } + + // Note: an apply function must only consume each enumerable once, and it must produce an enumerable + // So for a simple pass-through function that does a little work, we must enumerate only once. + // Else we get the error: "An HpcLinq channel can't be read more than once." + // [DistributiveOverConcat] + public static IEnumerable FullHomomorphic_Binary_Func(IEnumerable left, IEnumerable right) + { + long cLeft = 0; + foreach (int x in left) + { + cLeft++; + yield return x; + } + + long cRight = 0; + foreach (int x in right) + { + cRight++; + yield return x; + } + + if (cLeft == 0) + throw new Exception("a node received empty left-data"); + + if (cRight == 0) + throw new Exception("a node received empty right-data"); + } + + + public static bool NonHomomorphicUnaryApply() + { + var context = new DryadLinqContext(Config.cluster); + context.LocalExecution = false; + bool passed = true; + try + { + string outFile = "unittest/output/NonHomomorphicUnaryApply.out"; + + IQueryable input = context.FromStore(AzureUtils.ToAzureUri(Config.accountName, Config.containerName, + "unittest/inputdata/SimpleFile.txt")); + + IQueryable> simple = input.Apply(x => DataGenerator.CreateSimpleFileSets()); + IQueryable pt1 = simple.Select(x => x.First()); + + var q1 = pt1.ApplyPerPartition(x => NonHomomorphic_Unary_Func(x)); + var jobInfo = q1.ToStore(outFile).Submit(); + jobInfo.Wait(); + + passed &= Utils.FileExists(Config.accountName, Config.storageKey, Config.containerName, outFile); + } + catch (DryadLinqException) + { + passed &= false; + } + return passed; + } + + public static bool HomomorphicUnaryApply() + { + var context = new DryadLinqContext(Config.cluster); + context.LocalExecution = false; + bool passed = true; + try + { + string outFile = "unittest/output/HomomorphicUnaryApply.out"; + + IQueryable input = context.FromStore(AzureUtils.ToAzureUri(Config.accountName, Config.containerName, + "unittest/inputdata/SimpleFile.txt")); + + IQueryable> simple = input.Apply(x => DataGenerator.CreateSimpleFileSets()); + IQueryable pt1 = simple.Select(x => x.First()); + + var q1 = pt1.ApplyPerPartition(x => Homomorphic_Unary_Func(x)); + var jobInfo = q1.ToStore(outFile).Submit(); + jobInfo.Wait(); + + passed &= Utils.FileExists(Config.accountName, Config.storageKey, Config.containerName, outFile); + } + catch (DryadLinqException) + { + passed &= false; + } + return passed; + } + + public static bool NonHomomorphicBinaryApply() + { + var context = new DryadLinqContext(Config.cluster); + context.LocalExecution = false; + bool passed = true; + try + { + string outFile = "unittest/output/NonHomomorphicBinaryApply.out"; + + IQueryable input = context.FromStore(AzureUtils.ToAzureUri(Config.accountName, Config.containerName, + "unittest/inputdata/SimpleFile.txt")); + + IQueryable> simple = input.Apply(x => DataGenerator.CreateSimpleFileSets()); + IQueryable pt1 = simple.Select(x => x.First()); + + var q1 = pt1.Apply(pt1, (x, y) => NonHomomorphic_Binary_Func(x, y)); + var jobInfo = q1.ToStore(outFile).Submit(); + jobInfo.Wait(); + + passed &= Utils.FileExists(Config.accountName, Config.storageKey, Config.containerName, outFile); + } + catch (DryadLinqException) + { + passed &= false; + } + return passed; + } + + public static bool LeftHomomorphicBinaryApply() + { + var context = new DryadLinqContext(Config.cluster); + context.LocalExecution = false; + bool passed = true; + try + { + string outFile = "unittest/output/LeftHomomorphicBinaryApply.out"; + + IQueryable input = context.FromStore(AzureUtils.ToAzureUri(Config.accountName, Config.containerName, + "unittest/inputdata/SimpleFile.txt")); + + IQueryable> simple = input.Apply(x => DataGenerator.CreateSimpleFileSets()); + IQueryable pt1 = simple.Select(x => x.First()); + + var q1 = pt1.ApplyPerPartition(pt1, (x, y) => LeftHomomorphic_Binary_Func(x, y), true); + var jobInfo = q1.ToStore(outFile).Submit(); + jobInfo.Wait(); + + passed &= Utils.FileExists(Config.accountName, Config.storageKey, Config.containerName, outFile); + } + catch (DryadLinqException) + { + passed &= false; + } + return passed; + } + + public static bool FullHomomorphicBinaryApply_DifferentDataSets() + { + var context = new DryadLinqContext(Config.cluster); + context.LocalExecution = false; + bool passed = true; + try + { + string outFile = "unittest/output/FullHomomorphicBinaryApply_DifferentDataSets.out"; + + IQueryable input = context.FromStore(AzureUtils.ToAzureUri(Config.accountName, Config.containerName, + "unittest/inputdata/SimpleFile.txt")); + + IQueryable> simple = input.Apply(x => DataGenerator.CreateSimpleFileSets()); + IQueryable pt1 = simple.Select(x => x.First()); + IQueryable pt2 = simple.Select(x => x.First()); + + var q1 = pt1.ApplyPerPartition(pt2, (x, y) => FullHomomorphic_Binary_Func(x, y), false); + var jobInfo = q1.ToStore(outFile).Submit(); + jobInfo.Wait(); + + passed &= Utils.FileExists(Config.accountName, Config.storageKey, Config.containerName, outFile); + } + catch (DryadLinqException) + { + passed &= false; + } + return passed; + } + + public static bool FullHomomorphicBinaryApply_IdenticalDataSets() + { + var context = new DryadLinqContext(Config.cluster); + context.LocalExecution = false; + bool passed = true; + try + { + string outFile = "unittest/output/FullHomomorphicBinaryApply_2.out"; + + IQueryable input = context.FromStore(AzureUtils.ToAzureUri(Config.accountName, Config.containerName, + "unittest/inputdata/SimpleFile.txt")); + + IQueryable> simple = input.Apply(x => DataGenerator.CreateSimpleFileSets()); + IQueryable pt1 = simple.Select(x => x.First()); + var q1 = pt1.ApplyPerPartition(pt1, (x, y) => FullHomomorphic_Binary_Func(x, y), false); + var jobInfo = q1.ToStore(outFile).Submit(); + jobInfo.Wait(); + + passed &= Utils.FileExists(Config.accountName, Config.storageKey, Config.containerName, outFile); + } + catch (DryadLinqException) + { + passed &= false; + } + return passed; + } + + [Associative(typeof(AssociativeRecursive1))] + public static string IntToStringCSVAggregator(string agg, int next) + { + return agg + "," + next.ToString(); + } + public class AssociativeRecursive1 : IAssociative + { + public string Seed() + { + return ""; + } + public string RecursiveAccumulate(string first, string second) + { + return first + second; + } + } + + public static bool Aggregate_WithCombiner() + { + var context = new DryadLinqContext(Config.cluster); + context.LocalExecution = false; + bool passed = true; + try + { + IQueryable input = context.FromStore(AzureUtils.ToAzureUri(Config.accountName, Config.containerName, + "unittest/inputdata/SimpleFile.txt")); + + IQueryable> simple = input.Apply(x => DataGenerator.CreateSimpleFileSets()); + IQueryable pt1 = simple.Select(x => x.First()); + + string q1 = pt1.Aggregate("", (str, x) => IntToStringCSVAggregator(str, x)); + + passed &= (q1.Length == 27); // string should have numbers 1..12 separated by commas + } + catch (DryadLinqException) + { + passed &= false; + } + return passed; + } + } +} diff --git a/DryadLinqTests/BasicAPITests.cs b/DryadLinqTests/BasicAPITests.cs new file mode 100644 index 0000000..532a127 --- /dev/null +++ b/DryadLinqTests/BasicAPITests.cs @@ -0,0 +1,1087 @@ +using Microsoft.Research.DryadLinq; +using Microsoft.Research.Peloponnese.Storage; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; + +namespace DryadLinqTests +{ + public class BasicAPITests + { + public static bool ToStoreThrowsForNonQuery() + { + bool passed = true; + try + { + int[] data = new[] { 1, 2, 3 }; + var q1 = data.AsQueryable().Select(x => 100 + x).ToStore(AzureUtils.ToAzureUri(Config.accountName, Config.storageKey, Config.containerName, + "dummy")).ToArray(); + //Should throw as we got into DryadLinq via AsQueryable() rather than via context. + passed &= false; + } + catch (ArgumentException) + { + //expected + } + return passed; + } + + public static bool ToStoreGetEnumeratorThrows() // pass + { + var context = new DryadLinqContext(Config.cluster); + context.LocalExecution = false; + bool passed = true; + try + { + string outFile = "unittest/output/ToStoreGetEnumeratorThrows.txt"; + + IQueryable input = context.FromStore(AzureUtils.ToAzureUri(Config.accountName, Config.containerName, + "unittest/inputdata/SimpleFile.txt")); + IQueryable> simple = input.Apply(x => DataGenerator.CreateSimpleFileSets()); + + IQueryable pt1 = simple.Select(x => x.First()); + IQueryable q1 = pt1.Select(x => 100 + x); + + var output = q1.ToStore(AzureUtils.ToAzureUri(Config.accountName, Config.containerName, outFile), true); + output.GetEnumerator(); + } + catch (DryadLinqException) + { + passed &= false; + } + return passed; + } + + public static bool GetEnumeratorNonToStoreTerminated() + { + var context = new DryadLinqContext(Config.cluster); + context.LocalExecution = false; + bool passed = true; + try + { + IQueryable input = context.FromStore(AzureUtils.ToAzureUri(Config.accountName, Config.storageKey, Config.containerName, + "unittest/inputdata/SimpleFile.txt")); + + IQueryable> simple = input.Apply(x => DataGenerator.CreateSimpleFileSets()); + IQueryable pt1 = simple.Select(x => x.First()); + + IQueryable q1 = pt1.Select(x => 100 + x); + IQueryable q2 = q1.Where(x => true); + foreach (int x in q2) // throws here + { + //Console.WriteLine(x); + } + //@TODO: perform a sequence-equals test. + + //IQueryable format = q2.Select(x => new LineRecord(String.Format("{0}", x))); + //DryadLinqJobInfo output = format.ToStore(AzureUtils.ToAzureUri(Config.accountName, Config.storageKey, Config.containerName, + // "unittest/output/test2.txt")).SubmitAndWait(); + } + catch (DryadLinqException) + { + passed &= false; + } + return passed; + } + + public static bool ToStoreSubmitGetEnumerator() // pass + { + var context = new DryadLinqContext(Config.cluster); + context.LocalExecution = false; + bool passed = true; + try + { + string outFile = "unittest/output/ToStoreSubmitGetEnumerator.txt"; + + IQueryable input = context.FromStore(AzureUtils.ToAzureUri(Config.accountName, Config.storageKey, Config.containerName, + "unittest/inputdata/SimpleFile.txt")); + + IQueryable> simple = input.Apply(x => DataGenerator.CreateSimpleFileSets()); + IQueryable pt1 = simple.Select(x => x.First()); + var q1 = pt1.Select(x => 100 + x).HashPartition(x => x); + var q2 = q1.Where(x => true); + IQueryable output = q2.ToStore(AzureUtils.ToAzureUri(Config.accountName, Config.storageKey, Config.containerName, outFile), true); + DryadLinqJobInfo info = output.SubmitAndWait(); + + foreach (int x in output) // should not run a new dryad job. + { + //Console.WriteLine(x); + } + } + catch (DryadLinqException) + { + passed &= false; + } + return passed; + } + + public static bool SubmitNonToStoreTerminated() + { + var context = new DryadLinqContext(Config.cluster); + context.LocalExecution = false; + bool passed = true; + try + { + IQueryable input = context.FromStore(AzureUtils.ToAzureUri(Config.accountName, Config.storageKey, Config.containerName, + "unittest/inputdata/SimpleFile.txt")); + + IQueryable> simple = input.Apply(x => DataGenerator.CreateSimpleFileSets()); + IQueryable pt1 = simple.Select(x => x.First()); + + var q1 = pt1.Select(x => 100 + x); + var q2 = q1.Where(x => true); + q2.SubmitAndWait(); // throws here + var outPT = q2.ToList(); + foreach (int x in outPT) + { + //Console.WriteLine(x); + } + } + catch (DryadLinqException) + { + passed &= false; + } + return passed; + } + + public static bool MaterializeToStoreTerminated() + { + var context = new DryadLinqContext(Config.cluster); + context.LocalExecution = false; + bool passed = true; + try + { + string outFile_a = "unittest/output/MaterializeToStoreTerminated_a.txt"; + string outFile_b = "unittest/output/MaterializeToStoreTerminated_b.txt"; + + IQueryable input = context.FromStore(AzureUtils.ToAzureUri(Config.accountName, Config.storageKey, Config.containerName, + "unittest/inputdata/SimpleFile.txt")); + + IQueryable> simple = input.Apply(x => DataGenerator.CreateSimpleFileSets()); + IQueryable pt1 = simple.Select(x => x.First()); + IQueryable query = pt1.Select(x => 100 + x); + + var q1 = query.ToStore(AzureUtils.ToAzureUri(Config.accountName, Config.storageKey, Config.containerName, outFile_a), true); //stream name w/o prefixed slash + + var q2 = query.Where(x => true).ToStore(AzureUtils.ToAzureUri(Config.accountName, Config.storageKey, Config.containerName, outFile_b), true); //stream name w/ prefixed slash + + DryadLinqQueryable.Submit(q1, q2); //materialize // throws + + var __unused2 = q1.Select(x => x); // Legal call, but BLOCKS + foreach (int x in q2) + { + //Console.WriteLine(x); + } + + passed &= Utils.FileExists(Config.accountName, Config.storageKey, Config.containerName, outFile_a); + passed &= Utils.FileExists(Config.accountName, Config.storageKey, Config.containerName, outFile_b); + + //@TODO: assert that only one query execution occurred. + } + catch (DryadLinqException) + { + passed &= false; + } + return passed; + } + + public static bool MaterializeNonToStoreTerminated() + { + var context = new DryadLinqContext(Config.cluster); + context.LocalExecution = false; + bool passed = true; + try + { + IQueryable input = context.FromStore(AzureUtils.ToAzureUri(Config.accountName, Config.storageKey, Config.containerName, + "unittest/inputdata/SimpleFile.txt")); + + IQueryable> simple = input.Apply(x => DataGenerator.CreateSimpleFileSets()); + IQueryable pt1 = simple.Select(x => x.First()); + IQueryable query = pt1.Select(x => 100 + x); + + DryadLinqQueryable.Submit(query); //materialize // throws + + foreach (int x in query) + { + //Console.WriteLine(x); + } + + //@TODO: assert that only one query execution occurred. + } + catch (DryadLinqException) + { + passed &= false; + } + return passed; + } + + public static bool EnumeratePlainData() + { + var context = new DryadLinqContext(Config.cluster); + context.LocalExecution = false; + bool passed = true; + try + { + IQueryable input = context.FromStore(AzureUtils.ToAzureUri(Config.accountName, Config.storageKey, Config.containerName, + "unittest/inputdata/SimpleFile.txt")); + + IQueryable> simple = input.Apply(x => DataGenerator.CreateSimpleFileSets()); + IQueryable pt1 = simple.Select(x => x.First()); + foreach (int x in pt1) // throws + { + //Console.WriteLine(x); + } + } + catch (DryadLinqException) + { + passed &= false; + } + return passed; + } + + public static bool CopyPlainDataViaToStoreSubmit() + { + var context = new DryadLinqContext(Config.cluster); + context.LocalExecution = false; + bool passed = true; + try + { + string outFile = "unittest/output/CopyPlainDataViaToStoreSubmit.txt"; + + IQueryable input = context.FromStore(AzureUtils.ToAzureUri(Config.accountName, Config.storageKey, Config.containerName, + "unittest/inputdata/SimpleFile.txt")); + + IQueryable> simple = input.Apply(x => DataGenerator.CreateSimpleFileSets()); + IQueryable pt1 = simple.Select(x => x.First()); + + var q = pt1.ToStore(AzureUtils.ToAzureUri(Config.accountName, Config.storageKey, Config.containerName, outFile), true); + DryadLinqJobInfo info = q.Submit(); + info.Wait(); + + foreach (int x in q) + { + //Console.WriteLine(x); + } + + passed &= Utils.FileExists(Config.accountName, Config.storageKey, Config.containerName, outFile); + } + catch (DryadLinqException) + { + passed &= false; + } + return passed; + } + + public static bool CopyPlainDataViaToStoreMaterialize() + { + var context = new DryadLinqContext(Config.cluster); + context.LocalExecution = false; + bool passed = true; + try + { + string outFile = "unittest/output/CopyPlainDataViaToStoreMaterialize.txt"; + + IQueryable input = context.FromStore(AzureUtils.ToAzureUri(Config.accountName, Config.storageKey, Config.containerName, + "unittest/inputdata/SimpleFile.txt")); + + IQueryable> simple = input.Apply(x => DataGenerator.CreateSimpleFileSets()); + IQueryable pt1 = simple.Select(x => x.First()); + + var q = pt1.ToStore(AzureUtils.ToAzureUri(Config.accountName, Config.storageKey, Config.containerName, outFile), true); + DryadLinqJobInfo info = DryadLinqQueryable.Submit(q); + info.Wait(); + + foreach (int x in q) + { + //Console.WriteLine(x); + } + + passed &= Utils.FileExists(Config.accountName, Config.storageKey, Config.containerName, outFile); + } + catch (DryadLinqException) + { + passed &= false; + } + return passed; + } + /* + public static bool PlainEnumerableAsDryadQueryToStoreSubmit() + { + var context = new DryadLinqContext(Config.cluster); + context.LocalExecution = false; + bool passed = true; + try + { + string outFile = "unittest/output/PlainEnumerableAsDryadQueryToStoreSubmit.txt"; + + int[] plainData = { 5, 6, 7 }; + + var q = context.AsDryadQuery(plainData, CompressionScheme.None).ToStore(AzureUtils.ToAzureUri(Config.accountName, Config.storageKey, Config.containerName, outFile); + DryadLinqJobInfo info = q.Submit(); + info.Wait(); + + foreach (int x in q) + { + //Console.WriteLine(x); + } + + passed &= Utils.FileExists(Config.accountName, Config.storageKey, Config.containerName, outFile); + } + catch (DryadLinqException e) + { + passed &= false; + } + return passed; + } + */ + public static bool RepeatSubmit() + { + var context = new DryadLinqContext(Config.cluster); + context.LocalExecution = false; + bool passed = true; + try + { + string outFile = "unittest/output/RepeatSubmit.txt"; + + IQueryable input = context.FromStore(AzureUtils.ToAzureUri(Config.accountName, Config.storageKey, Config.containerName, + "unittest/inputdata/SimpleFile.txt")); + + IQueryable> simple = input.Apply(x => DataGenerator.CreateSimpleFileSets()); + IQueryable pt1 = simple.Select(x => x.First()); + + + var q = pt1.ToStore(AzureUtils.ToAzureUri(Config.accountName, Config.storageKey, Config.containerName, outFile), true); + DryadLinqJobInfo info1 = null; + DryadLinqJobInfo info2 = null; + try + { + info1 = q.Submit(); + info2 = q.Submit(); // does not throw + + if (!context.LocalDebug) + { + passed &= false; + } + } + catch (ArgumentException) + { + passed &= true; + } + + //wait for any jobs to complete. + if (info1 != null) + { + info1.Wait(); + } + + if (info2 != null) + { + info2.Wait(); + } + } + catch (DryadLinqException) + { + passed &= false; + } + return passed; + } + + public static bool RepeatMaterialize() + { + var context = new DryadLinqContext(Config.cluster); + context.LocalExecution = false; + bool passed = true; + try + { + string outFile = "unittest/output/RepeatMaterialize.txt"; + + IQueryable input = context.FromStore(AzureUtils.ToAzureUri(Config.accountName, Config.storageKey, Config.containerName, + "unittest/inputdata/SimpleFile.txt")); + + IQueryable> simple = input.Apply(x => DataGenerator.CreateSimpleFileSets()); + IQueryable pt1 = simple.Select(x => x.First()); + + var q = pt1.ToStore(AzureUtils.ToAzureUri(Config.accountName, Config.storageKey, Config.containerName, outFile), true); + DryadLinqJobInfo info1 = null; + DryadLinqJobInfo info2 = null; + try + { + info1 = DryadLinqQueryable.Submit(new[] { q }); //materialize + info2 = DryadLinqQueryable.Submit(new[] { q }); //materialize // does not throw + + if (!context.LocalDebug) + { + passed &= false; + } + } + catch (ArgumentException) + { + passed &= true; + } + + //wait for any jobs to complete. + if (info1 != null) + { + info1.Wait(); + } + + if (info2 != null) + { + info2.Wait(); + } + } + catch (DryadLinqException) + { + passed &= false; + } + return passed; + } + + public static bool MaterializeMentionsSameQueryTwice() // pass + { + var context = new DryadLinqContext(Config.cluster); + context.LocalExecution = false; + bool passed = true; + try + { + string outFile = "unittest/output/MaterializeMentionsSameQueryTwice.txt"; + + IQueryable input = context.FromStore(AzureUtils.ToAzureUri(Config.accountName, Config.storageKey, Config.containerName, + "unittest/inputdata/SimpleFile.txt")); + + IQueryable> simple = input.Apply(x => DataGenerator.CreateSimpleFileSets()); + IQueryable pt1 = simple.Select(x => x.First()); + + var q = pt1.ToStore(AzureUtils.ToAzureUri(Config.accountName, Config.storageKey, Config.containerName, outFile), true); + DryadLinqJobInfo info1 = null; + try + { + info1 = DryadLinqQueryable.Submit(q, q); //materialize // throws + passed &= false; // for Config.cluster execution, second materialize should throw; + } + catch (ArgumentException) + { + passed &= true; + } + + //wait for any jobs to complete. + if (info1 != null) + { + info1.Wait(); + } + } + catch (DryadLinqException) + { + passed &= false; + } + return passed; + } + + public static bool QueryOnDataBackedDLQ() + { + var context = new DryadLinqContext(Config.cluster); + context.LocalExecution = false; + bool passed = true; + try + { + string outFile = "unittest/output/QueryOnDataBackedDLQ.txt"; + + IQueryable input = context.FromStore(AzureUtils.ToAzureUri(Config.accountName, Config.containerName, + "unittest/inputdata/SimpleFile.txt")); + + IQueryable> simple = input.Apply(x => DataGenerator.CreateSimpleFileSets()); + IQueryable pt1 = simple.Select(x => x.First()); + var q = pt1.Select(x => 100 + x); + var outPT = q.ToStore(AzureUtils.ToAzureUri(Config.accountName, Config.containerName, outFile), true); + outPT.Submit(); + + var outPT2_dummy_notUsed = outPT.Select(x => x); //BLOCKS HERE until the input is concrete + // source.Expression returns an expression for the backingDataDLQ + // CheckAndInitialize() on the backingData will block. + + passed &= Utils.FileExists(Config.accountName, Config.storageKey, Config.containerName, outFile); + + foreach (int x in outPT) + { + //Console.WriteLine(x); + } + } + catch (DryadLinqException) + { + passed &= false; + } + return passed; + } + + public static bool Bug11781_CountandFirstOrDefault() + { + var context = new DryadLinqContext(Config.cluster); + context.LocalExecution = false; + bool passed = true; + try + { + string outFile = "unittest/output/Bug11781.out"; + + IQueryable input = context.FromStore(AzureUtils.ToAzureUri(Config.accountName, Config.containerName, + "unittest/inputdata/SimpleFile.txt")); + + IQueryable> simple = input.Apply(x => DataGenerator.CreateSimpleFileSets()); + IQueryable pt1 = simple.Select(x => x.First()); + + //Test Count() + var c = pt1.Count(); + + //Test CountAsQuery() + var q = pt1.CountAsQuery().ToStore(outFile); + DryadLinqJobInfo info = q.Submit(); + info.Wait(); + + passed &= Utils.FileExists(Config.accountName, Config.storageKey, Config.containerName, outFile); + + // Also test FirstOrDefault + // the affected code for dlq.Execute() also has a branch for FirstOrDefault() and friends. + int y = pt1.FirstOrDefault(); + } + catch (DryadLinqException) + { + passed &= false; + } + return passed; + } + + public static bool Bug11782_Aggregate() + { + var context = new DryadLinqContext(Config.cluster); + context.LocalExecution = false; + bool passed = true; + try + { + string outFile = "unittest/output/Bug11782_Aggregate.out"; + + IQueryable input = context.FromStore(AzureUtils.ToAzureUri(Config.accountName, Config.containerName, + "unittest/inputdata/SimpleFile.txt")); + + IQueryable> simple = input.Apply(x => DataGenerator.CreateSimpleFileSets()); + IQueryable pt1 = simple.Select(x => x.First()); + + //test Aggregate() + var c = pt1.Select(x => x).Aggregate((x, y) => x + y); + + //test AggregateAsQuery() + var q = pt1.Select(x => x).AggregateAsQuery((x, y) => x + y).ToStore(outFile); + DryadLinqJobInfo info = DryadLinqQueryable.Submit(q); + info.Wait(); + + passed &= Utils.FileExists(Config.accountName, Config.storageKey, Config.containerName, outFile); + } + catch (DryadLinqException) + { + passed &= false; + } + return passed; + } + + public static bool Bug11782_LowLevelQueryableManipulation() + { + var context = new DryadLinqContext(Config.cluster); + context.LocalExecution = false; + bool passed = true; + try + { + IQueryable input = context.FromStore(AzureUtils.ToAzureUri(Config.accountName, Config.containerName, + "unittest/inputdata/SimpleFile.txt")); + + IQueryable> simple = input.Apply(x => DataGenerator.CreateSimpleFileSets()); + IQueryable pt1 = simple.Select(x => x.First()); + + Expression lambda = Expression.Lambda>( + Expression.Constant(1), + new[] { Expression.Parameter(typeof(int), "x") }); + var z = pt1.Provider.CreateQuery( + Expression.Call( + typeof(Queryable), "Select", + new Type[] { pt1.ElementType, pt1.ElementType }, + pt1.Expression, Expression.Quote(lambda))); + + passed &= false; // the use of non-generic Provider.CreateQuery() should have thrown + } + catch (DryadLinqException) + { + passed &= true; + } + return passed; + } + + public static bool Bug11638_LongWhere() + { + var context = new DryadLinqContext(Config.cluster); + context.LocalExecution = false; + bool passed = true; + try + { + string outFile = "unittest/output/BasicAPITests_LongWhere.out"; + + IQueryable input = context.FromStore(AzureUtils.ToAzureUri(Config.accountName, Config.containerName, + "unittest/inputdata/SimpleFile.txt")); + + IQueryable> simple = input.Apply(x => DataGenerator.CreateSimpleFileSets()); + IQueryable pt1 = simple.Select(x => x.First()); + + var q = pt1.Select(x => 100 + x); + var outPT = q.LongWhere((x, i) => true).ToStore(outFile); + var info = outPT.Submit(); + info.Wait(); + + passed &= Utils.FileExists(Config.accountName, Config.storageKey, Config.containerName, outFile); + + } + catch (DryadLinqException) + { + passed &= false; + } + return passed; + } + + public static bool AssumeRangePartition() + { + var context = new DryadLinqContext(Config.cluster); + context.LocalExecution = false; + bool passed = true; + try + { + string outFile = "unittest/output/BasicAPITests_AssumeRangePartition.out"; + + IQueryable input = context.FromStore(AzureUtils.ToAzureUri(Config.accountName, Config.containerName, + "unittest/inputdata/SimpleFile.txt")); + + IQueryable> simple = input.Apply(x => DataGenerator.CreateSimpleFileSets()); + IQueryable pt1 = simple.Select(x => x.First()); + + var q = + pt1 + .AssumeRangePartition(x => x, false) + .Select(x => 100 + x).ToStore(outFile); + var info = q.Submit(); + info.Wait(); + + passed &= Utils.FileExists(Config.accountName, Config.storageKey, Config.containerName, outFile); + + } + catch (DryadLinqException) + { + passed &= false; + } + return passed; + } + + public static bool Bug11638_LongMethods() + { + var context = new DryadLinqContext(Config.cluster); + context.LocalExecution = false; + bool passed = true; + try + { + string outFile = "unittest/output/Bug11638_LongMethods.out"; + + IQueryable input = context.FromStore(AzureUtils.ToAzureUri(Config.accountName, Config.containerName, + "unittest/inputdata/SimpleFile.txt")); + + IQueryable> simple = input.Apply(x => DataGenerator.CreateSimpleFileSets()); + IQueryable pt1 = simple.Select(x => x.First()); + + var q = + pt1 + .LongSelect((x, i) => x) + .LongWhere((x, i) => true) + .LongSelectMany((x, i) => new[] { x }) + .LongSelectMany((x, i) => new[] { x }, (i, seq) => seq) //overload#2 + .LongTakeWhile((x, i) => true) + .LongSkipWhile((x, i) => false) + .ToStore(outFile); + var info = q.Submit(); + info.Wait(); + + passed &= Utils.FileExists(Config.accountName, Config.storageKey, Config.containerName, outFile); + + } + catch (DryadLinqException) + { + passed &= false; + } + return passed; + } + + public static bool ContextConfigIsReadOnly() + { + var context = new DryadLinqContext(Config.cluster); + context.LocalExecution = false; + bool passed = true; + + try + { + string jobName = context.JobFriendlyName; + context.JobFriendlyName = "bob"; + context.JobFriendlyName = jobName; + } + catch (NotSupportedException) + { + passed &= false; // "an exception should not thrown"; + } + + try + { + context.JobMinNodes = 120; + passed &= false; // "an exception should not thrown"; + } + catch (NotSupportedException) + { + //expected + } + + try + { + context.ResourcesToAdd.Add("blah"); + passed &= false; // "an exception should not thrown"; + } + catch (NotSupportedException) + { + //expected + } + + try + { + context.ResourcesToRemove.Add("blah"); + passed &= false; // "an exception should not thrown"; + } + catch (NotSupportedException) + { + //expected + } + + try + { + context.JobEnvironmentVariables.Add("bob", "bob"); + passed &= false; // "an exception should not thrown"; + } + catch (NotSupportedException) + { + //expected + } + + try + { + context.EnableSpeculativeDuplication = false; + passed &= false; // "an exception should not thrown"; + } + catch (NotSupportedException) + { + //expected + } + + return passed; + } + + public static bool ToggleSpeculativeDuplication() + { + var context = Utils.MakeBasicConfig(Config.cluster); + context.LocalExecution = false; + bool passed = true; + try + { + passed &= !context.EnableSpeculativeDuplication; // "Speculative Duplication enabled by default" + context.EnableSpeculativeDuplication = true; + passed &= context.EnableSpeculativeDuplication; // "Failed to enable speculative duplication" + context.EnableSpeculativeDuplication = false; + passed &= !context.EnableSpeculativeDuplication; // "Failed to disable speculative duplication" + context.EnableSpeculativeDuplication = false; + // ??? DryadLinqContext testContext = new DryadLinqContext(context); + // ??? passed &= !testContext.EnableSpeculativeDuplication; // "Speculative Duplication enabled after copy" + + } + catch (DryadLinqException) + { + passed &= false; // "Enabling and disabling speculative duplication should not throw" + } + return passed; + } + + public static bool Bug15068_ConfigResourcesAPI() + { + var context = new DryadLinqContext(Config.cluster); + context.LocalExecution = false; + bool passed = true; + try + { + context.HeadNode = "MIKELID7"; // ??? + passed &= (context.ResourcesToAdd.IsReadOnly == false); // "isReadOnly should be false" + passed &= (context.ResourcesToRemove.IsReadOnly == false); // "isReadOnly should be false" + + //clear + context.ResourcesToAdd.Clear(); + context.ResourcesToRemove.Clear(); + + //add + context.ResourcesToAdd.Add("abc"); + context.ResourcesToRemove.Add("def"); + context.ResourcesToRemove.Add("ghi"); + + //index, count, getEnumerator + passed &= (context.ResourcesToAdd[0] == "abc"); // "wrong value" + passed &= (context.ResourcesToAdd.Count == 1); // "wrong value" + + passed &= (context.ResourcesToRemove[0] == "def"); // "wrong value" + passed &= (context.ResourcesToRemove.Where((x, i) => (i == 1)).First() == "ghi"); // "wrong value" + passed &= (context.ResourcesToRemove.Count == 2); // "wrong value" + + // ??? + //// read-only. + //DryadLinqContext ctx = new DryadLinqContext(context); + //passed &= (ctx.ResourcesToAdd.IsReadOnly == true); // "isReadOnly should be true" + //passed &= (ctx.ResourcesToRemove.IsReadOnly == true); // "isReadOnly should be true" + + // clone was taken. + context.ResourcesToAdd.Clear(); + context.ResourcesToRemove.Clear(); + // ??? + //passed &= (ctx.ResourcesToAdd.Count == 1); // "should be unaffected" + //passed &= (ctx.ResourcesToRemove.Count == 2); // "should be unaffected" + } + catch (DryadLinqException) + { + passed &= false; + } + return passed; + } + + public static bool Bug14449_ContextShouldExposeVersionIDs() + { + var context = new DryadLinqContext(Config.cluster); + context.LocalExecution = false; + bool passed = true; + try + { + // ??? + //passed &= (context.Major >= 3); // "problem with HpcLinq client version" + //passed &= (context.Major >= 3); // "problem with HpcLinq server version" + + //passed &= (context.ClientVersion.Major >= 3); // "problem with Dsc client version" + //passed &= (context.ServerVersion.Major >= 3); // "problem with Dsc server version" + + } + catch (DryadLinqException) + { + passed &= false; + } + return passed; + } + + public static bool Bug_16341_SubmitThrowsForDifferentContexts() + { + var context = new DryadLinqContext(Config.cluster); + context.LocalExecution = false; + var context2 = new DryadLinqContext(Config.cluster); + context2.LocalExecution = false; + bool passed = true; + try + { + IQueryable input = context.FromStore(AzureUtils.ToAzureUri(Config.accountName, Config.containerName, + "unittest/inputdata/SimpleFile.txt")); + + IQueryable> simple = input.Apply(x => DataGenerator.CreateSimpleFileSets()); + IQueryable pt1 = simple.Select(x => x.First()); + + IQueryable input2 = context2.FromStore(AzureUtils.ToAzureUri(Config.accountName, Config.containerName, + "unittest/inputdata/SimpleFile.txt")); + + IQueryable> simple2 = input2.Apply(x => DataGenerator.CreateSimpleFileSets()); + IQueryable pt2 = simple2.Select(x => x.First()); + + DryadLinqQueryable.Submit(pt1, pt2); + passed &= false; + } + catch (DryadLinqException) + { + } + + try + { + IQueryable input = context.FromStore(AzureUtils.ToAzureUri(Config.accountName, Config.storageKey, Config.containerName, + "unittest/inputdata/SimpleFile.txt")); + + IQueryable> simple = input.Apply(x => DataGenerator.CreateSimpleFileSets()); + IQueryable pt1 = simple.Select(x => x.First()); + + IQueryable input2 = context2.FromStore(AzureUtils.ToAzureUri(Config.accountName, Config.storageKey, Config.containerName, + "unittest/inputdata/SimpleFile.txt")); + + IQueryable> simple2 = input2.Apply(x => DataGenerator.CreateSimpleFileSets()); + IQueryable pt2 = simple2.Select(x => x.First()); + + DryadLinqQueryable.SubmitAndWait(pt1, pt2); + passed &= false; + } + catch (DryadLinqException) + { + } + + return passed; + } + + public static bool Bug_16341_VariousTestsForSubmit() + { + var context = new DryadLinqContext(Config.cluster); + context.LocalExecution = false; + bool passed = true; + try + { + int[] data = new[] { 1, 2, 3 }; + var badQ1 = data.AsQueryable().Select(x => 100 + x); + var badQ2 = data.AsQueryable().Select(x => 100 + x); + + IQueryable input = context.FromStore(AzureUtils.ToAzureUri(Config.accountName, Config.containerName, + "unittest/inputdata/SimpleFile.txt")); + + IQueryable> simple = input.Apply(x => DataGenerator.CreateSimpleFileSets()); + IQueryable goodQ1 = simple.Select(x => x.First()); + + IQueryable input_copy = context.FromStore(AzureUtils.ToAzureUri(Config.accountName, Config.containerName, + "unittest/inputdata/SimpleFile.txt")); + + IQueryable> simple_copy = input_copy.Apply(x => DataGenerator.CreateSimpleFileSets()); + IQueryable goodQ2 = simple_copy.Select(x => x.First()); + + + try + { + badQ1.Submit(); + passed &= false; // "should throw as input isn't a L2H query" + } + catch (ArgumentException) + { + } + + try + { + DryadLinqQueryable.Submit((IQueryable)null); //this-Query overload + passed &= false; // "should throw ArgNull as input is null" + } + catch (ArgumentException) + { + //although we pass null, it goes to params[] overload which creates an actual array[1] containing one null + //hence we throw ArgumentException rather than ArgumentNullException. + } + + try + { + DryadLinqQueryable.Submit((IQueryable[])null); //multi-query overload + passed &= false; // "should throw ArgNull as input is null" + } + catch (ArgumentNullException) + { + } + + try + { + DryadLinqQueryable.Submit(goodQ1, null); //multi-query overload + passed &= false; // "should throw ArgEx as one of the inputs is null" + } + catch (ArgumentException) + { + } + + try + { + DryadLinqQueryable.Submit(goodQ1, badQ1); //multi-query overload + passed &= false; // "should throw ArgEx as one of the inputs is not a L2H" + } + catch (ArgumentException) + { + } + + //---------- + // same tests again for SubmitAndWait + + try + { + badQ1.SubmitAndWait(); + passed &= false; // "should throw as input isn't a L2H query" + } + catch (ArgumentException) + { + } + + try + { + DryadLinqQueryable.SubmitAndWait((IQueryable)null); //this-Query overload + passed &= false; // "should throw ArgNull as input is null" + } + catch (ArgumentException) + { + //although we pass null, it goes to params[] overload which creates an actual array[1] containing one null + //hence we throw ArgumentException rather than ArgumentNullException. + } + + try + { + DryadLinqQueryable.SubmitAndWait((IQueryable[])null); //multi-query overload + passed &= false; // "should throw ArgNull as input is null" + } + catch (ArgumentNullException) + { + } + + try + { + DryadLinqQueryable.SubmitAndWait(goodQ1, null); //multi-query overload + passed &= false; // "should throw ArgEx as one of the inputs is null" + } + catch (ArgumentException) + { + } + + try + { + DryadLinqQueryable.SubmitAndWait(goodQ1, badQ1); //multi-query overload + passed &= false; // "should throw ArgEx as one of the inputs is not a L2H" + } + catch (ArgumentException) + { + } + + } + catch (DryadLinqException) + { + } + return passed; + } + + + public static bool template() + { + var context = new DryadLinqContext(Config.cluster); + context.LocalExecution = false; + bool passed = true; + try + { + string outFile = "unittest/output/x.txt"; + + IQueryable input = context.FromStore(AzureUtils.ToAzureUri(Config.accountName, Config.containerName, + "unittest/inputdata/SimpleFile.txt")); + + IQueryable> simple = input.Apply(x => DataGenerator.CreateSimpleFileSets()); + IQueryable pt1 = simple.Select(x => x.First()); + + passed &= Utils.FileExists(Config.accountName, Config.storageKey, Config.containerName, outFile); + + } + catch (DryadLinqException) + { + passed &= false; + } + return passed; + } + + } + +} diff --git a/DryadLinqTests/DryadLinqTests.csproj b/DryadLinqTests/DryadLinqTests.csproj index 74f321f..c88877b 100644 --- a/DryadLinqTests/DryadLinqTests.csproj +++ b/DryadLinqTests/DryadLinqTests.csproj @@ -1,6 +1,6 @@ - + - + Debug @@ -63,37 +63,78 @@ False ..\packages\Microsoft.Data.Services.Client.5.6.1\lib\net40\Microsoft.Data.Services.Client.dll - + + False ..\packages\Microsoft.Hadoop.Client.1.1.0.7\lib\net40\Microsoft.Hadoop.Client.dll - - ..\packages\Microsoft.Research.Peloponnese.0.7.1-beta\lib\net45\Microsoft.Research.Peloponnese.HadoopBridge.dll + + False + ..\packages\Microsoft.Research.Peloponnese.0.7.2-beta\lib\net45\Microsoft.Research.Peloponnese.HadoopBridge.dll - - ..\packages\Microsoft.Research.Peloponnese.0.7.1-beta\lib\net45\Microsoft.Research.Peloponnese.Utils.dll + + False + ..\packages\Microsoft.Research.Peloponnese.0.7.2-beta\lib\net45\Microsoft.Research.Peloponnese.Utils.dll + + + False + ..\packages\Microsoft.Bcl.Async.1.0.166\lib\net40\Microsoft.Threading.Tasks.dll + + + ..\packages\Microsoft.Bcl.Async.1.0.166\lib\net40\Microsoft.Threading.Tasks.Extensions.dll + + + ..\packages\Microsoft.Bcl.Async.1.0.166\lib\net40\Microsoft.Threading.Tasks.Extensions.Desktop.dll + + + False + ..\packages\Microsoft.WindowsAzure.Common.1.0.1\lib\net45\Microsoft.WindowsAzure.Common.dll + + + False + ..\packages\Microsoft.WindowsAzure.Common.1.0.1\lib\net45\Microsoft.WindowsAzure.Common.NetFramework.dll ..\packages\Microsoft.WindowsAzure.ConfigurationManager.2.0.3\lib\net40\Microsoft.WindowsAzure.Configuration.dll - + + ..\packages\Microsoft.WindowsAzure.Management.1.0.0\lib\net40\Microsoft.WindowsAzure.Management.dll + + + False ..\packages\Microsoft.WindowsAzure.Management.HDInsight.1.1.0.7\lib\net40\Microsoft.WindowsAzure.Management.HDInsight.dll - + + False ..\packages\Microsoft.Hadoop.Client.1.1.0.7\lib\net40\Microsoft.WindowsAzure.Management.HDInsight.Framework.dll - + + False ..\packages\Microsoft.Hadoop.Client.1.1.0.7\lib\net40\Microsoft.WindowsAzure.Management.HDInsight.Framework.Core.dll + + False + ..\packages\Microsoft.WindowsAzure.Management.Storage.1.0.0\lib\net40\Microsoft.WindowsAzure.Management.Storage.dll + False ..\packages\WindowsAzure.Storage.3.1.0.1\lib\net40\Microsoft.WindowsAzure.Storage.dll - + + False ..\packages\Newtonsoft.Json.6.0.2\lib\net45\Newtonsoft.Json.dll + + + + ..\packages\Microsoft.Net.Http.2.2.19\lib\net45\System.Net.Http.Extensions.dll + + + ..\packages\Microsoft.Net.Http.2.2.19\lib\net45\System.Net.Http.Primitives.dll + + False ..\packages\System.Spatial.5.6.1\lib\net40\System.Spatial.dll @@ -106,8 +147,14 @@ + + + + + + @@ -137,14 +184,19 @@ + + + + + This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - + + - + - + \ No newline at end of file diff --git a/DryadLinqTests/GroupByReduceTests.cs b/DryadLinqTests/GroupByReduceTests.cs new file mode 100644 index 0000000..e7c7d69 --- /dev/null +++ b/DryadLinqTests/GroupByReduceTests.cs @@ -0,0 +1,1003 @@ +using Microsoft.Research.DryadLinq; +using Microsoft.Research.Peloponnese.Storage; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; + +namespace DryadLinqTests +{ + public static class GroupByReduceTests + { + public static bool Decomposition_Average() + { + var context = new DryadLinqContext(Config.cluster); + context.LocalExecution = false; + bool passed = true; + try + { + IQueryable input = context.FromStore(AzureUtils.ToAzureUri(Config.accountName, Config.containerName, + "unittest/inputdata/SimpleFile.txt")); + + IQueryable> simple = input.Apply(x => DataGenerator.CreateSimpleFileSets()); + IQueryable pt1 = simple.Select(x => x.First()); + + double[] aggregates = pt1.GroupBy(x => x % 2).Select(g => g.Average()).ToArray(); + //int[] expected = new[] { 1 + 3 + 5 + 7 + 9 + 11, 2 + 4 + 6 + 8 + 10 + 12 }; + + ////note the order of the result elements is not guaranteed, so order them before testing + //int[] aggregatesOrdered = aggregates.OrderBy(x => x).ToArray(); + //int[] expectedOrdered = expected.OrderBy(x => x).ToArray(); + + //passed &= aggregatesOrdered.SequenceEqual(expectedOrdered); + } + catch (DryadLinqException) + { + passed &= false; + } + return passed; + } + + public static bool DistributiveResultSelector_1() + { + var context = new DryadLinqContext(Config.cluster); + context.LocalExecution = false; + bool passed = true; + try + { + IQueryable input = context.FromStore(AzureUtils.ToAzureUri(Config.accountName, Config.containerName, + "unittest/inputdata/SimpleFile.txt")); + + IQueryable> simple = input.Apply(x => DataGenerator.CreateSimpleFileSets()); + IQueryable pt1 = simple.Select(x => x.First()); + + // this result selector satisfies "DistributiveOverConcat" + int[] aggregates = pt1.GroupBy(x => x % 2, (key, seq) => seq.Sum()).ToArray(); + int[] expected = new[] { 1 + 3 + 5 + 7 + 9 + 11, 2 + 4 + 6 + 8 + 10 + 12 }; + + //note the order of the result elements is not guaranteed, so order them before testing + int[] aggregatesOrdered = aggregates.OrderBy(x => x).ToArray(); + int[] expectedOrdered = expected.OrderBy(x => x).ToArray(); + + passed &= aggregatesOrdered.SequenceEqual(expectedOrdered); + } + catch (DryadLinqException) + { + passed &= false; + } + return passed; + } + + public static bool DistributiveSelect_1() + { + var context = new DryadLinqContext(Config.cluster); + context.LocalExecution = false; + bool passed = true; + try + { + IQueryable input = context.FromStore(AzureUtils.ToAzureUri(Config.accountName, Config.containerName, + "unittest/inputdata/SimpleFile.txt")); + + IQueryable> simple = input.Apply(x => DataGenerator.CreateSimpleFileSets()); + IQueryable pt1 = simple.Select(x => x.First()); + + // this result selector satisfies "DistributiveOverConcat" + int[] aggregates = pt1.GroupBy(x => x % 2).Select(group => group.Sum()).ToArray(); + int[] expected = new[] { 1 + 3 + 5 + 7 + 9 + 11, 2 + 4 + 6 + 8 + 10 + 12 }; + + //note the order of the result elements is not guaranteed, so order them before testing + int[] aggregatesOrdered = aggregates.OrderBy(x => x).ToArray(); + int[] expectedOrdered = expected.OrderBy(x => x).ToArray(); + + passed &= aggregatesOrdered.SequenceEqual(expectedOrdered); + } + catch (DryadLinqException) + { + passed &= false; + } + return passed; + } + + public static bool BuiltInCountIsDistributable() + { + var context = new DryadLinqContext(Config.cluster); + context.LocalExecution = false; + bool passed = true; + try + { + IQueryable input = context.FromStore(AzureUtils.ToAzureUri(Config.accountName, Config.containerName, + "unittest/inputdata/SimpleFile.txt")); + + IQueryable> simple = input.Apply(x => DataGenerator.CreateSimpleFileSets()); + IQueryable pt1 = simple.Select(x => x.First()); + + // Built in Count is Distributable as built-in logic knows to use Sum() as the combiner function. + // Count(a,b,c,d) = Sum(Count(a,b), Count(c,d)) + int[] aggregates = pt1.GroupBy(x => x % 2, (key, seq) => seq.Count()).ToArray(); + int[] expected = new[] { 6, 6 }; // six elements in each full group. + + //note the order of the result elements is not guaranteed, so order them before testing + int[] aggregatesOrdered = aggregates.OrderBy(x => x).ToArray(); + int[] expectedOrdered = expected.OrderBy(x => x).ToArray(); + + passed &= aggregatesOrdered.SequenceEqual(expectedOrdered); + } + catch (DryadLinqException) + { + passed &= false; + } + return passed; + } + + public static bool Bug12078_GroupByReduceWithResultSelectingAggregate() + { + var context = new DryadLinqContext(Config.cluster); + context.LocalExecution = false; + bool passed = true; + try + { + IQueryable input = context.FromStore(AzureUtils.ToAzureUri(Config.accountName, Config.containerName, + "unittest/inputdata/SimpleFile.txt")); + + IQueryable> simple = input.Apply(x => DataGenerator.CreateGroupByReduceDataSet()); + IQueryable data = simple.Select(x => x.First()); + + double[] aggregates = data + .Select(x => (double)x) + .GroupBy(x => 0, (key, seq) => seq.Aggregate((double)0, (acc, item) => acc + item, val => val / 100)).ToArray(); + double[] expected = new[] { Enumerable.Range(1, 200).Sum() / 100.0 }; + + //note the order of the result elements is not guaranteed, so order them before testing + double[] aggregatesOrdered = aggregates.OrderBy(x => x).ToArray(); + double[] expectedOrdered = expected.OrderBy(x => x).ToArray(); + + passed &= aggregatesOrdered.SequenceEqual(expectedOrdered); + } + catch (DryadLinqException) + { + passed &= false; + } + return passed; + } + + #region GroupByReduceWithCustomDecomposableFunction_DistributableCombiner + + [Decomposable(typeof(Decomposer_1))] + public static double DecomposableFunc(IEnumerable seq) + { + // hard to test with context system.. TestUtils.Assert(HpcLinq.LocalDebug, "This method should only be called during LocalDebug"); + return seq.Aggregate((double)0, (acc, item) => acc + item, val => val / 100); + } + public class Decomposer_1 : IDecomposable + { + public void Initialize(object state) { } + + public double Seed(double source) + { + return source; + } + + public double Accumulate(double a, double x) + { + return a + x; + } + + public double RecursiveAccumulate(double a, double x) + { + return a + x; + } + + public double FinalReduce(double a) + { + return a / 100; + } + } + + public static bool GroupByReduceWithCustomDecomposableFunction_DistributableCombiner() + { + var context = new DryadLinqContext(Config.cluster); + context.LocalExecution = false; + bool passed = true; + try + { + IQueryable input = context.FromStore(AzureUtils.ToAzureUri(Config.accountName, Config.containerName, + "unittest/inputdata/SimpleFile.txt")); + + IQueryable> simple = input.Apply(x => DataGenerator.CreateGroupByReduceDataSet()); + IQueryable data = simple.Select(x => x.First()); + + double[] aggregates = data + .Select(x => (double)x) + .GroupBy(x => 0, (k, g) => DecomposableFunc(g)) + .ToArray(); + double[] expected = new[] { Enumerable.Range(1, 200).Sum() / 100.0 }; + + //note the order of the result elements is not guaranteed, so order them before testing + double[] aggregatesOrdered = aggregates.OrderBy(x => x).ToArray(); + double[] expectedOrdered = expected.OrderBy(x => x).ToArray(); + + passed &= aggregatesOrdered.SequenceEqual(expectedOrdered); + } + catch (DryadLinqException) + { + passed &= false; + } + return passed; + } + + #endregion GroupByReduceWithCustomDecomposableFunction_DistributableCombiner + + #region GroupByReduceWithCustomDecomposableFunction_DistributableCombiner_DifferingTypes + // Tests a fully decomposed function whose reducer changes types. + [Decomposable(typeof(Decomposer_2))] + public static string DecomposableFunc2(IEnumerable seq) + { + //TestUtils.Assert(HpcLinq.LocalDebug, "This method should only be called during LocalDebug"); + return seq.Aggregate((double)0, (acc, item) => acc + item, val => ("hello:" + val.ToString())); + } + public class Decomposer_2 : IDecomposable + { + public void Initialize(object state) { } + + public double Seed(double source) + { + return source; + } + + public double Accumulate(double a, double x) + { + return a + x; + } + + public double RecursiveAccumulate(double a, double x) + { + return a + x; + } + + public string FinalReduce(double a) + { + return ("hello:" + a.ToString()); + } + } + + public static bool GroupByReduceWithCustomDecomposableFunction_DistributableCombiner_DifferingTypes() + { + var context = new DryadLinqContext(Config.cluster); + context.LocalExecution = false; + bool passed = true; + try + { + IQueryable input = context.FromStore(AzureUtils.ToAzureUri(Config.accountName, Config.containerName, + "unittest/inputdata/SimpleFile.txt")); + + IQueryable> simple = input.Apply(x => DataGenerator.CreateGroupByReduceDataSet()); + IQueryable data = simple.Select(x => x.First()); + + string[] aggregates = data + .Select(x => (double)x) + .GroupBy(x => 0, (key, seq) => DecomposableFunc2(seq)).ToArray(); + string[] expected = new[] { "hello:" + Enumerable.Range(1, 200).Sum() }; + + //note the order of the result elements is not guaranteed, so order them before testing + string[] aggregatesOrdered = aggregates.OrderBy(x => x).ToArray(); + string[] expectedOrdered = expected.OrderBy(x => x).ToArray(); + + passed &= aggregatesOrdered.SequenceEqual(expectedOrdered); + } + catch (DryadLinqException) + { + passed &= false; + } + return passed; + } + + #endregion GroupByReduceWithCustomDecomposableFunction_DistributableCombiner_DifferingTypes + + #region GroupByReduceWithCustomDecomposableFunction_DistributableCombiner_NoFinalizer + // Tests a decomposed function with no need for a particular reduce. + // The combiner changes type, and the recursive-combiner operators on the altered type + // The reducer just calls combiner again. + [Decomposable(typeof(Decomposer_3))] + public static string DecomposableFunc3(IEnumerable seq) + { + // TestUtils.Assert(HpcLinq.LocalDebug, "This method should only be called during LocalDebug"); + return seq.Aggregate("0", (acc, item) => (double.Parse(acc) + item).ToString()); + } + public class Decomposer_3 : IDecomposable + { + public void Initialize(object state) { } + + public string Seed(double source) + { + return source.ToString(); + } + + public string Accumulate(string a, double x) + { + return (double.Parse(a) + x).ToString(); + } + + public string RecursiveAccumulate(string a, string x) + { + return (double.Parse(a) + double.Parse(x)).ToString(); + } + + public string FinalReduce(string a) + { + return a; + } + } + + public static bool GroupByReduceWithCustomDecomposableFunction_DistributableCombiner_NoFinalizer() + { + var context = new DryadLinqContext(Config.cluster); + context.LocalExecution = false; + bool passed = true; + try + { + IQueryable input = context.FromStore(AzureUtils.ToAzureUri(Config.accountName, Config.containerName, + "unittest/inputdata/SimpleFile.txt")); + + IQueryable> simple = input.Apply(x => DataGenerator.CreateGroupByReduceDataSet()); + IQueryable data = simple.Select(x => x.First()); + + string[] aggregates = data + .Select(x => (double)x) + .GroupBy(x => 0, (key, seq) => DecomposableFunc3(seq)).ToArray(); + string[] expected = new[] { Enumerable.Range(1, 200).Sum().ToString() }; + + //note the order of the result elements is not guaranteed, so order them before testing + string[] aggregatesOrdered = aggregates.OrderBy(x => x).ToArray(); + string[] expectedOrdered = expected.OrderBy(x => x).ToArray(); + + passed &= aggregatesOrdered.SequenceEqual(expectedOrdered); + } + catch (DryadLinqException) + { + passed &= false; + } + return passed; + } + + #endregion GroupByReduceWithCustomDecomposableFunction_DistributableCombiner_NoFinalizer + + #region GroupByReduceWithCustomDecomposableFunction_NonDistributableCombiner + // Tests simplified pattern where the Combiner is not recursively applied. + // Note: Func4 can be represented as a decomposable with distributive-combiner and a finalizer.. but here we choose not to. + // Because of the form of the Combiner, it is critical that it not be used recursively. + [Decomposable(typeof(Decomposer_4))] + public static double DecomposableFunc4(IEnumerable seq) + { + // TestUtils.Assert(HpcLinq.LocalDebug, "This method should only be called during LocalDebug"); + return seq.Aggregate(0.0, (acc, item) => acc + item, acc => acc / 100); + } + public class Decomposer_4 : IDecomposable + { + public void Initialize(object state) { } + + public double Seed(double source) + { + return source; + } + + public double Accumulate(double a, double x) + { + return a + x; + } + + public double RecursiveAccumulate(double a, double x) + { + return a + x; + } + + public double FinalReduce(double a) + { + return a / 100; + } + } + + public static bool GroupByReduceWithCustomDecomposableFunction_NonDistributableCombiner() + { + var context = new DryadLinqContext(Config.cluster); + context.LocalExecution = false; + bool passed = true; + try + { + IQueryable input = context.FromStore(AzureUtils.ToAzureUri(Config.accountName, Config.containerName, + "unittest/inputdata/SimpleFile.txt")); + + IQueryable> simple = input.Apply(x => DataGenerator.CreateGroupByReduceDataSet()); + IQueryable data = simple.Select(x => x.First()); + + double[] aggregates = data + .Select(x => (double)x) + .GroupBy(x => 0, (key, seq) => DecomposableFunc4(seq)).ToArray(); + double[] expected = new[] { Enumerable.Range(1, 200).Sum() / 100.0 }; + + //note the order of the result elements is not guaranteed, so order them before testing + double[] aggregatesOrdered = aggregates.OrderBy(x => x).ToArray(); + double[] expectedOrdered = expected.OrderBy(x => x).ToArray(); + + passed &= aggregatesOrdered.SequenceEqual(expectedOrdered); + } + catch (DryadLinqException) + { + passed &= false; + } + return passed; + } + + #endregion GroupByReduceWithCustomDecomposableFunction_NonDistributableCombiner + + public static bool GroupByReduce_BuiltIn_First() + { + var context = new DryadLinqContext(Config.cluster); + context.LocalExecution = false; + bool passed = true; + try + { + IQueryable input = context.FromStore(AzureUtils.ToAzureUri(Config.accountName, Config.containerName, + "unittest/inputdata/SimpleFile.txt")); + + IQueryable> simple = input.Apply(x => DataGenerator.CreateGroupByReduceDataSet()); + IQueryable data = simple.Select(x => x.First()); + + int[] aggregates = data + .GroupBy(x => 0, (key, seq) => seq.First()) + .ToArray(); + + // the output of First can be the first item of either partition. + passed &= aggregates.SequenceEqual(new[] { 1 }) || aggregates.SequenceEqual(new[] { 101 }); + } + catch (DryadLinqException) + { + passed &= false; + } + return passed; + } + + public static bool GroupByReduce_ResultSelector_ComplexNewExpression() + { + var context = new DryadLinqContext(Config.cluster); + context.LocalExecution = false; + bool passed = true; + try + { + IQueryable input = context.FromStore(AzureUtils.ToAzureUri(Config.accountName, Config.containerName, + "unittest/inputdata/SimpleFile.txt")); + + IQueryable> simple = input.Apply(x => DataGenerator.CreateGroupByReduceDataSet()); + IQueryable data = simple.Select(x => x.First()); + + var aggregates = data.GroupBy(x => 0, (key, seq) => new KeyValuePair>(key, new KeyValuePair(seq.Average(), seq.Average()))).ToArray(); + + var expected = new KeyValuePair>[] { new KeyValuePair>(0, new KeyValuePair(100.5, 100.5)) }; + + passed &= aggregates.SequenceEqual(expected); + } + catch (DryadLinqException) + { + passed &= false; + } + return passed; + } + + #region GroupByReduce_ProgrammingManualExample + + public static bool GroupByReduce_ProgrammingManualExample() + { + var context = new DryadLinqContext(Config.cluster); + context.LocalExecution = false; + bool passed = true; + try + { + string filesetName = "DevUnitTest/0to999integers"; + Utils.DeleteFile(Config.accountName, Config.storageKey, Config.containerName, filesetName, true); + + IEnumerable> rawdata = new[] { Enumerable.Range(0, 334), Enumerable.Range(334, 333), Enumerable.Range(667, 333) }; + // ??? DscIngressHelpers.AsDryadQueryPartitions(context, rawdata, filesetName, DscCompressionScheme.None); + var data = context.FromStore(filesetName); + + var count = data.AsEnumerable().Count(); + var sum = data.AsEnumerable().Sum(); + var min = data.AsEnumerable().Min(); + var max = data.AsEnumerable().Max(); + var uniques = data.AsEnumerable().Distinct().Count(); + + //Console.WriteLine("DATA:: count:{0} uniques:{1} sum:{2}, min:{3}, max:{4}", count, uniques, sum, min, max); + + // ??? + //var results = data + // .GroupBy(x => x % 10, (key, seq) => new KeyValuePair(key, seq.MyAverage())) + // .OrderBy(y => y.Key) + // .ToArray(); + + ////foreach (var result in results) + //// Console.WriteLine("For group {0} the average is {1}", result.Key, result.Value); + + //passed &= (results.Count() == 10); + //passed &= (results[0].Key == 0); // "first element should be key=0"); + //passed &= (results[0].Value == 495); // "first element should be value=495 ie avg(0,10,20,..,990)"); + + } + catch (DryadLinqException) + { + passed &= false; + } + return passed; + } + + [Decomposable(typeof(Decomposer_5))] + public static double MyAverage(this IEnumerable recordSequence) + { + int count = 0, sum = 0; + foreach (var r in recordSequence) + { + sum += r; + count++; + } + if (count == 0) throw new Exception("Can't average empty sequence"); + return (double)sum / (double)count; + } + + [Serializable] + public struct Partial + { + public int PartialSum; + public int PartialCount; + } + + public class Decomposer_5 : IDecomposable + { + public void Initialize(object state) { } + + public Partial Seed(int x) + { + Partial p = new Partial(); + p.PartialSum = x; + p.PartialCount = 1; + return p; + } + + public Partial Accumulate(Partial a, int x) + { + Partial p = new Partial(); + p.PartialSum = a.PartialSum + x; + p.PartialCount = a.PartialCount + 1; + return p; + } + + public Partial RecursiveAccumulate(Partial a, Partial x) + { + Partial p = new Partial(); + p.PartialSum = a.PartialSum + x.PartialSum; + p.PartialCount = a.PartialCount + x.PartialCount; + return p; + } + + public double FinalReduce(Partial a) + { + if (a.PartialCount == 0) throw new Exception("Can't average empty sequence"); + return (double)a.PartialSum / (double)a.PartialCount; + } + } + + #endregion GroupByReduce_ProgrammingManualExample + + + #region GroupByReduce_SameDecomposableUsedTwice + + public static bool GroupByReduce_SameDecomposableUsedTwice() + { + var context = new DryadLinqContext(Config.cluster); + context.LocalExecution = false; + bool passed = true; + try + { + IQueryable input = context.FromStore(AzureUtils.ToAzureUri(Config.accountName, Config.containerName, + "unittest/inputdata/SimpleFile.txt")); + + IQueryable> simple = input.Apply(x => DataGenerator.CreateGroupByReduceDataSet()); + IQueryable pt1 = simple.Select(x => x.First()); + + var results = pt1.GroupBy(x => x % 2, (k, g) => MyFunc(k, DecomposableFunc5(g), DecomposableFunc5(g), g.Average())).ToArray(); + + //key0: count = 6, av = av(2,4,6,8,10,12) = 7 + //key1: count = 6, av = av(1,3,5,7,9,11) = 6 + + //local sort.. so that keys are in order. + var results_sorted = results.OrderBy(x => x.Key).ToArray(); + + passed &= (results_sorted.Length == 2); // "wrong results" + + passed &= (results_sorted[0].Key == 0); // "wrong results" + passed &= (results_sorted[0].A == 6); // "wrong results" + passed &= (results_sorted[0].B == 6); // "wrong results" + passed &= (results_sorted[0].Av == 7.0); // "wrong results" + + passed &= (results_sorted[1].Key == 1); // "wrong results" + passed &= (results_sorted[1].A == 6); // "wrong results" + passed &= (results_sorted[1].B == 6); // "wrong results" + passed &= (results_sorted[1].Av == 6.0); // "wrong results" + } + catch (DryadLinqException) + { + passed &= false; + } + return passed; + } + + public static MyStruct3 MyFunc(int key, int a, int b, double av) + { + return new MyStruct3(key, a, b, av); + } + [Decomposable(typeof(Decomposer_6))] + private static int DecomposableFunc5(IEnumerable g) + { + return g.Count(); + } + public class Decomposer_6 : IDecomposable + { + public void Initialize(object state) { } + + public int Seed(int source) { return 1; } + + public int Accumulate(int a, int x) + { + return a + 1; + } + + public int RecursiveAccumulate(int a, int x) + { + return a + x; + } + + public int FinalReduce(int a) + { + return a; + } + } + [Serializable] + public struct MyStruct3 + { + public int Key; + public int A; + public int B; + public double Av; + + public MyStruct3(int key, int a, int b, double av) + { + Key = key; A = a; B = b; Av = av; + } + } + + #endregion GroupByReduce_SameDecomposableUsedTwice + + #region API_Misuse + internal static bool GroupByReduce_APIMisuse() + { + var context = new DryadLinqContext(Config.cluster); + context.LocalExecution = false; + bool passed = true; + try + { + if (context.LocalDebug) + { + // "decomposition logic doesn't run in LocalDebug.. skipping"; + return true; + } + + IQueryable input = context.FromStore(AzureUtils.ToAzureUri(Config.accountName, Config.containerName, + "unittest/inputdata/SimpleFile.txt")); + + IQueryable> simple = input.Apply(x => DataGenerator.CreateGroupByReduceDataSet()); + IQueryable pt1 = simple.Select(x => x.First()); + + // internal-visibility decomposable type should fail. + try + { + pt1.GroupBy(x => x, (k, g) => BadDecomposable1(g)).ToArray(); + passed &= false; // "exception should be thrown" + } + catch (DryadLinqException) + { + //??? passed &= (Ex.ErrorCode == DryadLinqErrorCode.DecomposerTypeMustBePublic); // "error code is wrong" + } + + // decomposable type doesn't implement IDecomposable or IDecomposableRecursive + try + { + pt1.GroupBy(x => x, (k, g) => BadDecomposable2(g)).ToArray(); + passed &= false; //"exception should be thrown"); + } + catch (DryadLinqException) + { + //??? passed &= (Ex.ErrorCode == DryadLinqErrorCode.DecomposerTypeDoesNotImplementInterface); + } + + // decomposable type implements more than one IDecomposable or IDecomposableRecursive + try + { + pt1.GroupBy(x => x, (k, g) => BadDecomposable3(g)).ToArray(); + passed &= false; + } + catch (DryadLinqException) + { + //??? passed &= (Ex.ErrorCode == DryadLinqErrorCode.DecomposerTypeImplementsTooManyInterfaces); + } + + // decomposable type doesn't have public default ctor + try + { + pt1.GroupBy(x => x, (k, g) => BadDecomposable4(g)).ToArray(); + passed &= false; + } + catch (DryadLinqException) + { + //??? passed &= (Ex.ErrorCode == DryadLinqErrorCode.DecomposerTypeDoesNotHavePublicDefaultCtor); + } + + // decomposable type input type doesn't match + try + { + pt1.GroupBy(x => x, (k, g) => BadDecomposable5(g)).ToArray(); + passed &= false; + } + catch (DryadLinqException) + { + //??? passed &= (Ex.ErrorCode == DryadLinqErrorCode.DecomposerTypesDoNotMatch); + } + + // decomposable type output type doesn't match + try + { + pt1.GroupBy(x => x, (k, g) => BadDecomposable6(g)).ToArray(); + passed &= false; + } + catch (DryadLinqException) + { + //??? passed &= (Ex.ErrorCode == DryadLinqErrorCode.DecomposerTypesDoNotMatch); + } + } + catch (DryadLinqException) + { + passed &= false; + } + return passed; + } + + [Decomposable(typeof(BadDecomposerType1))] + private static int BadDecomposable1(IEnumerable g) + { + throw new NotImplementedException(); + } + internal class BadDecomposerType1 : IDecomposable + { + public void Initialize(object state) { } + public int Seed(int x) { return x; } + public int Accumulate(int a, int x) { throw new NotImplementedException(); } + public int RecursiveAccumulate(int a, int x) { throw new NotImplementedException(); } + public int FinalReduce(int a) { throw new NotImplementedException(); } + } + [Decomposable(typeof(BadDecomposerType2))] + private static int BadDecomposable2(IEnumerable g) + { + throw new NotImplementedException(); + } + public class BadDecomposerType2 + { + } + [Decomposable(typeof(BadDecomposerType3))] + private static int BadDecomposable3(IEnumerable g) + { + throw new NotImplementedException(); + } + public class BadDecomposerType3 : IDecomposable + { + public void Initialize(object state) { } + public int Seed(int x) { return x; } + public int Accumulate(int a, int x) { throw new NotImplementedException(); } + public int RecursiveAccumulate(int a, int x) { throw new NotImplementedException(); } + public int FinalReduce(int a) { throw new NotImplementedException(); } + } + [Decomposable(typeof(BadDecomposerType4))] + private static int BadDecomposable4(IEnumerable g) + { + throw new NotImplementedException(); + } + public class BadDecomposerType4 : IDecomposable + { + internal BadDecomposerType4() { } + public BadDecomposerType4(int x) { } + public void Initialize(object state) { } + public int Seed(int x) { return x; } + public int Accumulate(int a, int x) { throw new NotImplementedException(); } + public int RecursiveAccumulate(int a, int x) { throw new NotImplementedException(); } + public int FinalReduce(int a) { throw new NotImplementedException(); } + } + [Decomposable(typeof(BadDecomposerType5))] + private static int BadDecomposable5(IEnumerable g) + { + throw new NotImplementedException(); + } + public class BadDecomposerType5 : IDecomposable + { + public void Initialize(object state) { } + public int Seed(double s) { throw new NotImplementedException(); } + public int Accumulate(int a, double x) { throw new NotImplementedException(); } + public int RecursiveAccumulate(int a, int x) { throw new NotImplementedException(); } + public int FinalReduce(int a) { throw new NotImplementedException(); } + } + [Decomposable(typeof(BadDecomposerType6))] + private static int BadDecomposable6(IEnumerable g) + { + throw new NotImplementedException(); + } + public class BadDecomposerType6 : IDecomposable + { + public void Initialize(object state) { } + public int Seed(int s) { throw new NotImplementedException(); } + public int Accumulate(int a, int x) { throw new NotImplementedException(); } + public int RecursiveAccumulate(int a, int x) { throw new NotImplementedException(); } + public double FinalReduce(int a) { throw new NotImplementedException(); } + } + + #endregion API_Misuse + + public static bool GroupByReduce_ListInitializerReducer() + { + var context = new DryadLinqContext(Config.cluster); + context.LocalExecution = false; + bool passed = true; + try + { + IQueryable input = context.FromStore(AzureUtils.ToAzureUri(Config.accountName, Config.containerName, + "unittest/inputdata/SimpleFile.txt")); + + IQueryable> simple = input.Apply(x => DataGenerator.CreateGroupByReduceDataSet()); + IQueryable pt1 = simple.Select(x => x.First()); + + var results = pt1.GroupBy(x => x % 2, (k, g) => new List() { k, g.Count(), g.Sum() }).ToArray(); + + //local sort.. so that keys are in order. + var resultsSorted = results.OrderBy(list => list[0]).ToArray(); + + //key0: count = 6, sum = 42 + //key1: count = 6, sum = 36 + + passed &= (resultsSorted[0][0] == 0); // "incorrect results.1" + passed &= (resultsSorted[0][1] == 6); // "incorrect results.2" + passed &= (resultsSorted[0][2] == 42); // "incorrect results.3" + + passed &= (resultsSorted[1][0] == 1); // "incorrect results.4" + passed &= (resultsSorted[1][1] == 6); // "incorrect results.5" + passed &= (resultsSorted[1][2] == 36); // "incorrect results.6" + } + catch (DryadLinqException) + { + passed &= false; + } + return passed; + } + + public static bool GroupByReduce_CustomListInitializerReducer() + { + var context = new DryadLinqContext(Config.cluster); + context.LocalExecution = false; + bool passed = true; + try + { + IQueryable input = context.FromStore(AzureUtils.ToAzureUri(Config.accountName, Config.containerName, + "unittest/inputdata/SimpleFile.txt")); + + IQueryable> simple = input.Apply(x => DataGenerator.CreateGroupByReduceDataSet()); + IQueryable pt1 = simple.Select(x => x.First()); + + var results = pt1.GroupBy(x => x % 2, (k, g) => new MultiParamInitializerClass() { + {k, g.Count(), g.Sum()} , // one item, comprising three components + }).ToArray(); + //local sort.. so that keys are in order. + var resultsSorted = results.OrderBy(list => list.Key).ToArray(); + + //key0: count = 6, sum = 42 + //key1: count = 6, sum = 36 + + passed &= (resultsSorted[0].Key == 0); // "incorrect results.1" + passed &= (resultsSorted[0].Count() == 6); // "incorrect results.2" + passed &= (resultsSorted[0].Sum() == 42); // "incorrect results.3" + + passed &= (resultsSorted[1].Key == 1); // "incorrect results.4" + passed &= (resultsSorted[1].Count() == 6); // "incorrect results.5" + passed &= (resultsSorted[1].Sum() == 36); // "incorrect results.6" + } + catch (DryadLinqException) + { + passed &= false; + } + return passed; + } + + // note: must be IEnumerable<> to be allowed to participate in list-initializer syntax. + // we are cheating here and only supporting one "add" call, just as an example. + [Serializable] + public class MultiParamInitializerClass : IEnumerable + { + public int Key; + public int Sum; + public int Count; + + public void Add(int key, int count, int sum) + { + Key = key; + Count = count; + Sum = sum; + } + public IEnumerator GetEnumerator() + { + yield return Key; + yield return Count; + yield return Sum; + } + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } + + public static bool GroupByReduce_BitwiseNegationOperator() + { + var context = new DryadLinqContext(Config.cluster); + context.LocalExecution = false; + bool passed = true; + try + { + IQueryable input = context.FromStore(AzureUtils.ToAzureUri(Config.accountName, Config.containerName, + "unittest/inputdata/SimpleFile.txt")); + + IQueryable> simple = input.Apply(x => DataGenerator.CreateGroupByReduceDataSet()); + IQueryable pt1 = simple.Select(x => x.First()); + + var results = pt1.GroupBy(x => x % 2, (k, g) => new KeyValuePair(k, ~g.Sum())).ToArray(); + + //local sort.. so that keys are in order. + var resultsSorted = results.OrderBy(list => list.Key).ToArray(); + + //key0: count = 6, sum = 42 + //key1: count = 6, sum = 36 + + passed &= (resultsSorted[0].Key == 0); // "incorrect results.1" + passed &= (resultsSorted[0].Value == ~42); // "incorrect results.2" + + passed &= (resultsSorted[1].Key == 1); // "incorrect results.3" + passed &= (resultsSorted[1].Value == ~36); // "incorrect results.4" + } + catch (DryadLinqException) + { + passed &= false; + } + return passed; + } + + public static bool template() + { + var context = new DryadLinqContext(Config.cluster); + context.LocalExecution = false; + bool passed = true; + try + { + IQueryable input = context.FromStore(AzureUtils.ToAzureUri(Config.accountName, Config.containerName, + "unittest/inputdata/SimpleFile.txt")); + + IQueryable> simple = input.Apply(x => DataGenerator.CreateGroupByReduceDataSet()); + IQueryable data = simple.Select(x => x.First()); + + //passed &= aggregatesOrdered.SequenceEqual(expectedOrdered); + } + catch (DryadLinqException) + { + passed &= false; + } + return passed; + } + } +} diff --git a/DryadLinqTests/Program.cs b/DryadLinqTests/Program.cs index c6813bd..c3b6dc9 100644 --- a/DryadLinqTests/Program.cs +++ b/DryadLinqTests/Program.cs @@ -21,6 +21,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Text; +using System.IO; using System.Threading.Tasks; using Microsoft.Research.DryadLinq; @@ -32,64 +33,32 @@ namespace DryadLinqTests { public static void Main(string[] args) { - //Test1(args); - //Test2(args); - //Test3(args); - //Test4(args); - //Test5(args); - } + //BasicAPITests.ToStoreThrowsForNonQuery(); + //BasicAPITests.ToStoreGetEnumeratorThrows(); + //BasicAPITests.GetEnumeratorNonToStoreTerminated(); + //BasicAPITests.ToStoreSubmitGetEnumerator(); + //BasicAPITests.SubmitNonToStoreTerminated(); + //BasicAPITests.MaterializeToStoreTerminated(); + //BasicAPITests.MaterializeNonToStoreTerminated(); + //BasicAPITests.EnumeratePlainData(); - public static void Test1(string[] args) - { - DryadLinqContext context = new DryadLinqContext(1, "partfile"); - //context.PartitionUncPath = "DryadLinqTemp/PartFiles"; - var input = context.FromStore("partfile:///d:/DryadLinqTemp/PartFiles/foo.pt"); - var lines = input.Where(x => x.Line.Contains("white")); - var result = lines.ToStore("partfile://svc-yuanbyu-3/DryadLinqTemp/PartFiles/res1.pt", true); - result.SubmitAndWait(); - } + //BasicAPITests.RepeatSubmit(); + //BasicAPITests.RepeatMaterialize(); + //BasicAPITests.MaterializeMentionsSameQueryTwice(); + //BasicAPITests.QueryOnDataBackedDLQ(); + //BasicAPITests.Bug11781_CountandFirstOrDefault(); + //BasicAPITests.Bug11782_Aggregate(); + //BasicAPITests.Bug11782_LowLevelQueryableManipulation(); + //BasicAPITests.Bug11638_LongWhere(); + //BasicAPITests.AssumeRangePartition(); + //BasicAPITests.Bug11638_LongMethods(); + //BasicAPITests.ContextConfigIsReadOnly(); + //BasicAPITests.ToggleSpeculativeDuplication(); + //BasicAPITests.Bug15068_ConfigResourcesAPI(); + //BasicAPITests.Bug_16341_SubmitThrowsForDifferentContexts(); + //BasicAPITests.Bug14449_ContextShouldExposeVersionIDs(); + //BasicAPITests.Bug_16341_VariousTestsForSubmit(); - public static void Test2(string[] args) - { - DryadLinqContext context = new DryadLinqContext(1, "partfile:///d:/DryadLinqTemp/PartFiles"); - var input = context.FromStore("partfile:///d:/DryadLinqTemp/PartFiles/foo.pt"); - var q1 = input.Where(x => x.Line.Contains("white")); - var q2 = input.Where(x => x.Line.Contains("the")); - var res1 = q1.ToStore("partfile:///d:/DryadLinqTemp/PartFiles/res1.pt", true); - var res2 = q2.ToStore("partfile:///d:/DryadLinqTemp/PartFiles/res2.pt", true); - DryadLinqQueryable.SubmitAndWait(res1, res2); - } - - public static void Test3(string[] args) - { - DryadLinqContext context = new DryadLinqContext(1, "partfile:///d:/DryadLinqTemp/PartFiles"); - var input = context.FromStore("partfile:///d:/DryadLinqTemp/PartFiles/foo.pt"); - var words = input.SelectMany(x => x.Line.Split(' ')); - var groups = words.GroupBy(x => x); - var counts = groups.Select(x => new KeyValuePair(x.Key, x.Count())); - var toOutput = counts.Select(x => new LineRecord(String.Format("{0}: {1}", x.Key, x.Value))); - var result = toOutput.ToStore("partfile:///d:/DryadLinqTemp/PartFiles/res2.pt", true); - result.SubmitAndWait(); - } - - public static void Test4(string[] args) - { - DryadLinqContext context = new DryadLinqContext("svc-d2-01"); - var input = context.FromStore("hdfs://svc-d2-01:8033/user/misard/foo.txt"); - var lines = input.Where(x => x.Line.Contains("white")); - var result = lines.ToStore("hdfs://svc-d2-01:8033/user/yuanbyu/foo.txt", true); - result.SubmitAndWait(); - } - - public static void Test5(string[] args) - { - DryadLinqContext context = new DryadLinqContext(1); - Uri dataUri = AzureUtils.ToAzureUri("msrsvc", "I4JPlk0bZ6YWypg+RJamyq0us1b+kCcuoeKlPhfiHTcVW7P4xvuzURvlRShSo1O3UDhcL2LiY4kMaarD+p1lKg==", "test", "testwrite"); - IEnumerable lines = DataProvider.ReadData(context, dataUri); - foreach (var x in lines) - { - Console.WriteLine(x); - } } } } diff --git a/DryadLinqTests/Properties/AssemblyInfo.cs b/DryadLinqTests/Properties/AssemblyInfo.cs index effbff7..db85f70 100644 --- a/DryadLinqTests/Properties/AssemblyInfo.cs +++ b/DryadLinqTests/Properties/AssemblyInfo.cs @@ -51,5 +51,5 @@ using System.Runtime.InteropServices; // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("0.1.0.0")] -[assembly: AssemblyFileVersion("0.1.0.0")] +[assembly: AssemblyVersion("0.1.2.0")] +[assembly: AssemblyFileVersion("0.1.2.0")] diff --git a/DryadLinqTests/RangePartitionAPICoverageTests.cs b/DryadLinqTests/RangePartitionAPICoverageTests.cs new file mode 100644 index 0000000..3581390 --- /dev/null +++ b/DryadLinqTests/RangePartitionAPICoverageTests.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace DryadLinqTests +{ + public class RangePartitionAPICoverageTests + { + } +} diff --git a/DryadLinqTests/Utils.cs b/DryadLinqTests/Utils.cs new file mode 100644 index 0000000..5aec8aa --- /dev/null +++ b/DryadLinqTests/Utils.cs @@ -0,0 +1,157 @@ +using Microsoft.Research.DryadLinq; +using Microsoft.WindowsAzure.Storage; +using Microsoft.WindowsAzure.Storage.Blob; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + + +namespace DryadLinqTests +{ + public static class Config + { + public static string accountName = @"MyAccountName"; + public static string storageKey = @"MyStorageKey"; + public static string containerName = @"MyContainerName"; + public static string cluster = "MyCcluster"; + } + + public class DataGenerator + { + public DataGenerator() + { + } + + public static IEnumerable> CreateSimpleFileSets() + { + IEnumerable> data = new int[][] + { + new[] { 1, 2, 3, 4 }, + new[] { 5, 6, 7, 8 }, + new[] { 9, 10, 11, 12 }, + }; + return data; + } + + public static IEnumerable> CreateGroupByReduceDataSet() + { + // we need quite a few elements to ensure the combiner will be activated in Stage#1 groupBy. + // 33 elements per partition should suffice, but 100 per partition is safer. + IEnumerable> data = new int[][] + { + Enumerable.Range(1,100).ToArray(), + Enumerable.Range(101,100).ToArray(), + }; + return data; + } + + + } + + public class Utils + { + public static bool DeleteFile(string accountName, string accountKey, string containerName, string fileName, bool delSubDirs) + { + try + { + CloudStorageAccount storageAccount = CloudStorageAccount.Parse("DefaultEndpointsProtocol=http;AccountName=" + accountName + ";AccountKey=" + accountKey); + CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient(); + CloudBlobContainer container = blobClient.GetContainerReference(containerName); + container.CreateIfNotExists(); + BlobContainerPermissions containerPermissions = new BlobContainerPermissions(); + containerPermissions.PublicAccess = BlobContainerPublicAccessType.Blob; + container.SetPermissions(containerPermissions); + + if (false == delSubDirs) + { + CloudBlockBlob remoteFile = container.GetBlockBlobReference(fileName); + remoteFile.DeleteIfExists(); + } + + if (true == delSubDirs) + { + foreach (IListBlobItem item in container.ListBlobs(fileName, true)) + { + CloudBlockBlob blob = (CloudBlockBlob)item; + blob.DeleteIfExists(); + } + } + } + catch (Exception) + { + return false; + } + return true; + } + public static bool FileExists(string accountName, string accountKey, string containerName, string fileName) + { + try + { + CloudStorageAccount storageAccount = CloudStorageAccount.Parse("DefaultEndpointsProtocol=http;AccountName=" + accountName + ";AccountKey=" + accountKey); + CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient(); + CloudBlobContainer container = blobClient.GetContainerReference(containerName); + container.CreateIfNotExists(); + BlobContainerPermissions containerPermissions = new BlobContainerPermissions(); + containerPermissions.PublicAccess = BlobContainerPublicAccessType.Blob; + container.SetPermissions(containerPermissions); + + CloudBlockBlob remoteFile = container.GetBlockBlobReference(fileName); + if (!remoteFile.Exists()) + return false; + } + catch (Exception) + { + return false; + } + return true; + } + + internal static DryadLinqContext MakeBasicConfig(string cluster) //??? + { + var context = new DryadLinqContext(cluster); + try + { + context.JobFriendlyName = "DryadLinq_DevUnitTests"; + context.CompileForVertexDebugging = true; + context.JobEnvironmentVariables.Add("DummyEnvVar", "hello"); //note: this is consumed by a unit-test. + + if (File.Exists("Microsoft.Hpc.Linq.pdb")) // TODO: fix references + { + context.ResourcesToAdd.Add("Microsoft.Hpc.Linq.pdb"); + } + + if (File.Exists("Microsoft.Hpc.Dsc.Client.pdb")) // TODO: fix references + { + context.ResourcesToAdd.Add("Microsoft.Hpc.Dsc.Client.pdb"); + } + + // To prevent job from running forever, and blocking other test + context.JobRuntimeLimit = (int)TimeSpan.FromMinutes(30).TotalSeconds; + + + //config.AllowConcurrentUserDelegatesInSingleProcess = false; + + // If we are on Azure, we have to set the nodeGroup to "NodeRole" so that the default of "ComputeNodes" is not used + // This fixes "FromEnumerableTests" on Azure which queries the active node-group. + // Note also, the headnode for an azure deployment defaults to "HPCCluster" (at least from James' script) + int onAzureInt = 0; + string onAzureString = Environment.GetEnvironmentVariable("CCP_SCHEDULERONAZURE"); + if (onAzureString != null) + { + int.TryParse(onAzureString, out onAzureInt); + } + + if (onAzureInt == 1) + { + context.NodeGroup = "NodeRole"; + } + + } + catch (DryadLinqException) + { + } + return context; + } + } +} diff --git a/DryadLinqTests/Validate.cs b/DryadLinqTests/Validate.cs new file mode 100644 index 0000000..f14904f --- /dev/null +++ b/DryadLinqTests/Validate.cs @@ -0,0 +1,267 @@ +///------------------------------------------------------------------------------------------------- +// file: Validate.cs +// +// summary: Implements the validate class +///------------------------------------------------------------------------------------------------- + +using System; +using System.Collections.Generic; +using System.Linq; + +namespace BenchmarkFramework { + + ///------------------------------------------------------------------------------------------------- + /// Validation utils + ///------------------------------------------------------------------------------------------------- + + public class Validate { + + public static void + Check( + IEnumerable[] ss, + IComparer comparer = null, + bool sort = true, + bool verbose = false, + IComparer sortcomparer = null + ) { + + if(ss.Length == 0) return; + + if(comparer == null) { + comparer = Comparer.Default; + if(comparer == null) { + throw new ArgumentNullException("Can't not be null."); + } + } + if(sortcomparer == null) + sortcomparer = comparer; + + T[][] aa = new T[ss.Length][]; + for(int i = 0; i < aa.Length; i++) { + aa[i] = ss[i].ToArray(); + if(sort) Array.Sort(aa[i], sortcomparer); + } + int len = aa[0].Length; + for(int i = 1; i < aa.Length; i++) { + if(aa[i].Length != len) { + throw new Exception("Wrong number of elements."); + } + } + for(int i = 0; i < len; i++) { + T elem = aa[0][i]; + for(int j = 1; j < aa.Length; j++) { + if(verbose) { + //TestOutput.WriteLine("Comparing {0} to {1}", elem.ToString(), aa[j][i].ToString()); + } + if(comparer.Compare(elem, aa[j][i]) != 0) { + throw new Exception("Elements failed to match: " + elem + " != " + aa[j][i]); + } + } + } + } + + //public static void + //GroupCheck( + // IEnumerable>[] ss, + // IComparer kComparer = null, + // IComparer vComparer = null + // ) { + + // if(ss.Length == 0) return; + + // if(kComparer == null) { + // kComparer = Comparer.Default; + // if(kComparer == null) { + // throw new ArgumentNullException("Can't not be null."); + // } + // } + // if(vComparer == null) { + // vComparer = Comparer.Default; + // if(vComparer == null) { + // throw new ArgumentNullException("Can't not be null."); + // } + // } + // IGrouping[][] aa = new IGrouping[ss.Length][]; + // for(int i = 0; i < aa.Length; i++) { + // aa[i] = ss[i].ToArray(); + // K[] keys = aa[i].Select(x => x.Key).ToArray(); + // Array.Sort(keys, aa[i], kComparer); + // } + // int len = aa[0].Length; + // for(int i = 1; i < aa.Length; i++) { + // if(aa[i].Length != len) { + // throw new Exception("Wrong number of elements."); + // } + // } + // for(int i = 0; i < len; i++) { + // IEnumerable elem = aa[0][i]; + // for(int j = 1; j < aa.Length; j++) { + // Check(new IEnumerable[] { elem, aa[j][i] }, vComparer); + // } + // } + //} + } + + /* + ///------------------------------------------------------------------------------------------------- + /// Tolerant float comparer. Floating point differences between + /// GPU and CPU cause the default comparer to fail sometimes even when + /// the result is correct. Use this comparer to introduce some tolerance + /// for this + /// + /// + /// Crossbac, 2/19/2013. + ///------------------------------------------------------------------------------------------------- + + public class TolerantDoubleComparer : IComparer { + Double EPSILON; + public TolerantDoubleComparer(Double _epsilon = 0.000001f) { + EPSILON = _epsilon; + } + public int Compare(Double a, Double b) { + Double delta = a - b; + if(Math.Abs(delta) <= EPSILON) + return 0; + return delta < 0.0f ? -1 : 1; + } + } + + ///------------------------------------------------------------------------------------------------- + /// Tolerant float comparer. Floating point differences between + /// GPU and CPU cause the default comparer to fail sometimes even when + /// the result is correct. Use this comparer to introduce some tolerance + /// for this + /// + /// + /// Crossbac, 2/19/2013. + ///------------------------------------------------------------------------------------------------- + + public class TolerantFloatComparer : IComparer { + float EPSILON; + public TolerantFloatComparer(float _epsilon = 0.000001f) { + EPSILON = _epsilon; + } + public int Compare(float a, float b) { + float delta = a - b; + if(Math.Abs(delta) <= EPSILON) + return 0; + return delta < 0.0f ? -1 : 1; + } + } + + ///------------------------------------------------------------------------------------------------- + /// Tolerant float comparer. Floating point differences between + /// GPU and CPU cause the default comparer to fail sometimes even when + /// the result is correct. Use this comparer to introduce some tolerance + /// for this + /// + /// + /// Crossbac, 2/19/2013. + ///------------------------------------------------------------------------------------------------- + + public class TolerantVectorComparer : IComparer { + float EPSILON; + public TolerantVectorComparer(float _epsilon = 0.0001f) { + EPSILON = _epsilon; + } + public int Compare(Vector a, Vector b) { + for(int i = 0; i < a.m_elems.Length; i++) { + float delta = a.m_elems[i] - b.m_elems[i]; + if(Math.Abs(delta) > EPSILON) + return delta < 0.0f ? -1 : 1; + } + return 0; + } + } + + ///------------------------------------------------------------------------------------------------- + /// Interface for epsilon comparable single. + /// + /// Crossbac, 1/16/2014. + /// + /// Generic type parameter. + ///------------------------------------------------------------------------------------------------- + + public interface IEpsilonComparableSingle { + int EpsilonCompare(T a, T b, float epsilon); + } + + ///------------------------------------------------------------------------------------------------- + /// Interface for epsilon comparable double. + /// + /// Crossbac, 1/16/2014. + /// + /// Generic type parameter. + ///------------------------------------------------------------------------------------------------- + + public interface IEpsilonComparableDouble { + int EpsilonCompare(T a, T b, double epsilon); + } + + + ///------------------------------------------------------------------------------------------------- + /// Tolerant float comparer. Floating point differences between + /// GPU and CPU cause the default comparer to fail sometimes even when + /// the result is correct. Use this comparer to introduce some tolerance + /// for this + /// + /// + /// Crossbac, 2/19/2013. + ///------------------------------------------------------------------------------------------------- + + public class EpsilonComparer : IComparer where T : IEpsilonComparableSingle { + float EPSILON; + public EpsilonComparer(float _epsilon = 0.0001f) { + EPSILON = _epsilon; + } + public int Compare(T a, T b) { + return a.EpsilonCompare(a, b, EPSILON); + } + } + + + ///------------------------------------------------------------------------------------------------- + /// Tolerant float comparer for images. Floating point differences between + /// GPU and CPU cause the default comparer to fail sometimes even when + /// the result is correct. Use this comparer to introduce some tolerance + /// for this + /// + /// + /// Crossbac, 2/19/2013. + ///------------------------------------------------------------------------------------------------- + + public class TolerantImageComparer : IComparer { + float EPSILON; + public TolerantImageComparer(float _epsilon = 0.0001f) { + EPSILON = _epsilon; + } + public int Compare(Image a, Image b) { + for(int i = 0; i < a.m_elems.Length; i++) { + float delta = a.m_elems[i] - b.m_elems[i]; + if(Math.Abs(delta) > EPSILON) + return delta < 0.0f ? -1 : 1; + } + return 0; + } + } + + /// + /// Compare two instances of Pair. + /// Implement for concrete type as Pair cannot straighforwardly implement IComparable. + /// + /// + /// jcurrey, 3/11/2013. + public class PairIntIntComparer : IComparer> { + public int Compare(Pair a, Pair b) { + int keyComparison = a.Key.CompareTo(b.Key); + if(keyComparison == 0) { + return a.Value.CompareTo(b.Value); + } else { + return keyComparison; + } + } + } + +*/ + +} diff --git a/DryadLinqTests/packages.config b/DryadLinqTests/packages.config index 6cc3081..bd29d67 100644 --- a/DryadLinqTests/packages.config +++ b/DryadLinqTests/packages.config @@ -1,13 +1,21 @@ - + + + + - + + + + + + - + \ No newline at end of file diff --git a/DryadVertex/VertexHost/system/channel/channel.vcxproj b/DryadVertex/VertexHost/system/channel/channel.vcxproj index 763d09f..d68bcfd 100644 --- a/DryadVertex/VertexHost/system/channel/channel.vcxproj +++ b/DryadVertex/VertexHost/system/channel/channel.vcxproj @@ -1,6 +1,6 @@ - + - + Debug @@ -149,13 +149,13 @@ - + This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - + + - + \ No newline at end of file diff --git a/DryadVertex/VertexHost/system/channel/packages.config b/DryadVertex/VertexHost/system/channel/packages.config index 7868d60..cb331ea 100644 --- a/DryadVertex/VertexHost/system/channel/packages.config +++ b/DryadVertex/VertexHost/system/channel/packages.config @@ -1,4 +1,4 @@ - + - - + + \ No newline at end of file diff --git a/DryadVertex/VertexHost/system/dprocess/dprocess.vcxproj b/DryadVertex/VertexHost/system/dprocess/dprocess.vcxproj index dbe63b4..67ff44b 100644 --- a/DryadVertex/VertexHost/system/dprocess/dprocess.vcxproj +++ b/DryadVertex/VertexHost/system/dprocess/dprocess.vcxproj @@ -1,6 +1,6 @@ - + - + Debug @@ -123,13 +123,13 @@ - + This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - + + - + \ No newline at end of file diff --git a/DryadVertex/VertexHost/system/dprocess/packages.config b/DryadVertex/VertexHost/system/dprocess/packages.config index 7868d60..cb331ea 100644 --- a/DryadVertex/VertexHost/system/dprocess/packages.config +++ b/DryadVertex/VertexHost/system/dprocess/packages.config @@ -1,4 +1,4 @@ - + - - + + \ No newline at end of file diff --git a/DryadVertex/VertexHost/system/managedchannel/Properties/AssemblyInfo.cs b/DryadVertex/VertexHost/system/managedchannel/Properties/AssemblyInfo.cs index 2b7c6ee..bb0b4db 100644 --- a/DryadVertex/VertexHost/system/managedchannel/Properties/AssemblyInfo.cs +++ b/DryadVertex/VertexHost/system/managedchannel/Properties/AssemblyInfo.cs @@ -51,5 +51,5 @@ using System.Runtime.InteropServices; // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("0.1.0.0")] -[assembly: AssemblyFileVersion("0.1.0.0")] +[assembly: AssemblyVersion("0.1.2.0")] +[assembly: AssemblyFileVersion("0.1.2.0")] diff --git a/DryadVertex/VertexHost/system/managedchannel/managedchannel.csproj b/DryadVertex/VertexHost/system/managedchannel/managedchannel.csproj index 4a6e56b..fc6d77f 100644 --- a/DryadVertex/VertexHost/system/managedchannel/managedchannel.csproj +++ b/DryadVertex/VertexHost/system/managedchannel/managedchannel.csproj @@ -1,6 +1,6 @@ - + - + Debug @@ -53,15 +53,39 @@ False - ..\..\..\..\packages\Microsoft.Research.Peloponnese.0.7.1-beta\lib\net45\Microsoft.Research.Peloponnese.HadoopBridge.dll + ..\..\..\..\packages\Microsoft.Research.Peloponnese.0.7.2-beta\lib\net45\Microsoft.Research.Peloponnese.HadoopBridge.dll - + False - ..\..\..\..\packages\Microsoft.Research.Peloponnese.0.7.1-beta\lib\net45\Microsoft.Research.Peloponnese.Utils.dll + ..\..\..\..\packages\Microsoft.Research.Peloponnese.0.7.2-beta\lib\net45\Microsoft.Research.Peloponnese.Utils.dll + + + False + ..\..\..\..\packages\Microsoft.Bcl.Async.1.0.166\lib\net40\Microsoft.Threading.Tasks.dll + + + False + ..\..\..\..\packages\Microsoft.Bcl.Async.1.0.166\lib\net40\Microsoft.Threading.Tasks.Extensions.dll + + + False + ..\..\..\..\packages\Microsoft.Bcl.Async.1.0.166\lib\net40\Microsoft.Threading.Tasks.Extensions.Desktop.dll + + + False + ..\..\..\..\packages\Microsoft.WindowsAzure.Common.1.0.1\lib\net45\Microsoft.WindowsAzure.Common.dll + + + False + ..\..\..\..\packages\Microsoft.WindowsAzure.Common.1.0.1\lib\net45\Microsoft.WindowsAzure.Common.NetFramework.dll ..\..\..\..\packages\Microsoft.WindowsAzure.ConfigurationManager.2.0.3\lib\net40\Microsoft.WindowsAzure.Configuration.dll + + False + ..\..\..\..\packages\Microsoft.WindowsAzure.Management.1.0.0\lib\net40\Microsoft.WindowsAzure.Management.dll + False ..\..\..\..\packages\Microsoft.WindowsAzure.Management.HDInsight.1.1.0.7\lib\net40\Microsoft.WindowsAzure.Management.HDInsight.dll @@ -74,6 +98,10 @@ False ..\..\..\..\packages\Microsoft.Hadoop.Client.1.1.0.7\lib\net40\Microsoft.WindowsAzure.Management.HDInsight.Framework.Core.dll + + False + ..\..\..\..\packages\Microsoft.WindowsAzure.Management.Storage.1.0.0\lib\net40\Microsoft.WindowsAzure.Management.Storage.dll + False ..\..\..\..\packages\WindowsAzure.Storage.3.1.0.1\lib\net40\Microsoft.WindowsAzure.Storage.dll @@ -85,6 +113,17 @@ + + + + False + ..\..\..\..\packages\Microsoft.Net.Http.2.2.19\lib\net45\System.Net.Http.Extensions.dll + + + False + ..\..\..\..\packages\Microsoft.Net.Http.2.2.19\lib\net45\System.Net.Http.Primitives.dll + + False ..\..\..\..\packages\System.Spatial.5.6.1\lib\net40\System.Spatial.dll @@ -112,14 +151,19 @@ + + + + + This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - + + - + - + \ No newline at end of file diff --git a/DryadVertex/VertexHost/system/managedchannel/packages.config b/DryadVertex/VertexHost/system/managedchannel/packages.config index 6cc3081..bd29d67 100644 --- a/DryadVertex/VertexHost/system/managedchannel/packages.config +++ b/DryadVertex/VertexHost/system/managedchannel/packages.config @@ -1,13 +1,21 @@ - + + + + - + + + + + + - + \ No newline at end of file diff --git a/DryadVertex/VertexHost/vertex/vertexHost/VertexHost.vcxproj b/DryadVertex/VertexHost/vertex/vertexHost/VertexHost.vcxproj index 6e7a3ca..bef34dd 100644 --- a/DryadVertex/VertexHost/vertex/vertexHost/VertexHost.vcxproj +++ b/DryadVertex/VertexHost/vertex/vertexHost/VertexHost.vcxproj @@ -1,6 +1,6 @@ - + - + Debug @@ -124,13 +124,13 @@ - + This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - + + - + \ No newline at end of file diff --git a/DryadVertex/VertexHost/vertex/vertexHost/packages.config b/DryadVertex/VertexHost/vertex/vertexHost/packages.config index 7868d60..cb331ea 100644 --- a/DryadVertex/VertexHost/vertex/vertexHost/packages.config +++ b/DryadVertex/VertexHost/vertex/vertexHost/packages.config @@ -1,4 +1,4 @@ - + - - + + \ No newline at end of file diff --git a/GraphManager/GraphManager.vcxproj b/GraphManager/GraphManager.vcxproj index 252ea35..5304f03 100644 --- a/GraphManager/GraphManager.vcxproj +++ b/GraphManager/GraphManager.vcxproj @@ -1,6 +1,6 @@ - + - + Debug @@ -223,13 +223,13 @@ - + This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - + + - + \ No newline at end of file diff --git a/GraphManager/GraphManager.vcxproj.filters b/GraphManager/GraphManager.vcxproj.filters index 38cadcf..5c493af 100644 --- a/GraphManager/GraphManager.vcxproj.filters +++ b/GraphManager/GraphManager.vcxproj.filters @@ -5,10 +5,6 @@ {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hpp;hxx;hm;inl;inc;xsd - - {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} - rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx diff --git a/GraphManager/packages.config b/GraphManager/packages.config index 3674cc4..cb331ea 100644 --- a/GraphManager/packages.config +++ b/GraphManager/packages.config @@ -1,4 +1,4 @@  - + \ No newline at end of file diff --git a/JobBrowser/JOM/ClusterConfiguration.cs b/JobBrowser/JOM/ClusterConfiguration.cs index 4d1d1c9..2b897fa 100644 --- a/JobBrowser/JOM/ClusterConfiguration.cs +++ b/JobBrowser/JOM/ClusterConfiguration.cs @@ -19,30 +19,31 @@ limitations under the License. */ -using Microsoft.Research.Calypso.Tools; +using System.Security.Cryptography.X509Certificates; +using System.Xml.Linq; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; -using System.Text.RegularExpressions; using System.Net; -using System.Diagnostics; using Microsoft.Research.Peloponnese.Storage; +using Microsoft.Research.Tools; +using Microsoft.WindowsAzure.Management.HDInsight; -namespace Microsoft.Research.Calypso.JobObjectModel +namespace Microsoft.Research.JobObjectModel { /// /// Error during conversation with cluster. /// - public sealed class CalypsoClusterException : Exception + public sealed class ClusterException : Exception { /// /// Create an exception about handling a cluster. /// /// Exception message. - public CalypsoClusterException(string message) : base(message) { } + public ClusterException(string message) : base(message) { } } /// @@ -606,13 +607,22 @@ namespace Microsoft.Research.Calypso.JobObjectModel /// public abstract string Initialize(); + /// + /// Enumerate all clusters this user is subscribed to. + /// + /// A list of clusters. + public static IEnumerable EnumerateSubscribedClusters() + { + return AzureDfsClusterConfiguration.EnumerateAzureDfsSubscribedClusters(); + } + /// /// Create serialization data structure for this configuration. /// /// The corresponding serialization. public ClusterConfigurationSerialization ExtractData() { - ClusterConfigurationSerialization result = new ClusterConfigurationSerialization() + ClusterConfigurationSerialization result = new ClusterConfigurationSerialization { Type = this.TypeOfCluster, Name = this.Name, @@ -925,7 +935,7 @@ namespace Microsoft.Research.Calypso.JobObjectModel IClusterResidentObject dir = this.ProcessWorkDirectory(job.ManagerProcessGuid, true, job.Machine, job); // immutable var matchingfiles = dir.GetFilesAndFolders("DryadLinqProgram__*.xml").ToList(); if (matchingfiles.Count() != 1) - throw new CalypsoClusterException("Could not find query plan file; got " + matchingfiles.Count() + " possible matches"); + throw new ClusterException("Could not find query plan file; got " + matchingfiles.Count() + " possible matches"); IClusterResidentObject result = matchingfiles.First(); result.ShouldCacheLocally = true; // immutable return result; @@ -1306,7 +1316,7 @@ namespace Microsoft.Research.Calypso.JobObjectModel //IClusterResidentObject dir = this.ProcessWorkDirectory(new DryadProcessIdentifier("Process.000.001"), true, job.Machine, job); var matchingfiles = dir.GetFilesAndFolders("DryadLinqProgram__*.xml").ToList(); if (matchingfiles.Count() != 1) - throw new CalypsoClusterException("Could not find query plan file; got " + matchingfiles.Count() + " possible matches"); + throw new ClusterException("Could not find query plan file; got " + matchingfiles.Count() + " possible matches"); IClusterResidentObject result = matchingfiles.First(); result.ShouldCacheLocally = true; // immutable return result; @@ -1395,10 +1405,67 @@ namespace Microsoft.Research.Calypso.JobObjectModel { } + /// + /// Enumerate all the clusters this user is subscribed to. + /// + /// The list of clusters this user is subscribed to. + public static IEnumerable EnumerateAzureDfsSubscribedClusters() + { + var store = new X509Store(); + store.Open(OpenFlags.ReadOnly); + var configDir = Path.Combine( + Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), + "Windows Azure Powershell"); + var defaultFile = Path.Combine(configDir, "WindowsAzureProfile.xml"); + if (File.Exists(defaultFile)) + { + using (FileStream s = new FileStream(defaultFile, FileMode.Open, FileAccess.Read)) + { + XDocument doc = XDocument.Load(s); + XNamespace ns = doc.Root.GetDefaultNamespace(); + IEnumerable subs = doc.Descendants(ns + "AzureSubscriptionData"); + foreach (XElement sub in subs) + { + string thumbprint = sub.Descendants(ns + "ManagementCertificate").Single().Value; + string subId = sub.Descendants(ns + "SubscriptionId").Single().Value; + Guid subGuid = new Guid(subId); + + X509Certificate2 cert = store.Certificates.Cast().First(item => item.Thumbprint == thumbprint); + + HDInsightCertificateCredential sCred = new HDInsightCertificateCredential(subGuid, cert); + IHDInsightClient sClient = HDInsightClient.Connect(sCred); + var clusters = sClient.ListClusters(); + foreach (var cluster in clusters) + { + var account = cluster.DefaultStorageAccount; + var accountName = account.Name.Split('.').First(); + Console.WriteLine("Cluster " + cluster.Name + " uses account " + accountName + " with key " + account.Key); + + AzureDfsClusterConfiguration config = null; + try + { + config = new AzureDfsClusterConfiguration(); + config.AzureClient = new AzureDfsClient(accountName, account.Key, "dryad-jobs"); + config.Name = cluster.Name; + } + catch (Exception ex) + { + Console.WriteLine("Exception while reconstructing cluster " + cluster.Name + ": " + ex); + } + + if (config != null) + yield return config; + } + } + } + } + } + /// /// Azure account name. /// - public string AccountName { get; set; } + public + string AccountName { get; set; } /// /// Azure account key. /// @@ -1567,7 +1634,7 @@ namespace Microsoft.Research.Calypso.JobObjectModel IClusterResidentObject dir = this.ProcessWorkDirectory(job.ManagerProcessGuid, true, job.Machine, job); // immutable var matchingfiles = dir.GetFilesAndFolders("DryadLinqProgram__*.xml").ToList(); if (matchingfiles.Count() != 1) - throw new CalypsoClusterException("Could not find query plan file; got " + matchingfiles.Count() + " possible matches"); + throw new ClusterException("Could not find query plan file; got " + matchingfiles.Count() + " possible matches"); IClusterResidentObject result = matchingfiles.First(); (result as AzureDfsFile).IsDfsStream = true; result.ShouldCacheLocally = true; // immutable diff --git a/JobBrowser/JOM/ClusterStatus.cs b/JobBrowser/JOM/ClusterStatus.cs index 57c11a6..a7ff380 100644 --- a/JobBrowser/JOM/ClusterStatus.cs +++ b/JobBrowser/JOM/ClusterStatus.cs @@ -23,11 +23,10 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Microsoft.Research.Calypso.Tools; +using Microsoft.Research.Peloponnese.Storage; +using Microsoft.Research.Tools; -namespace Microsoft.Research.Calypso.JobObjectModel +namespace Microsoft.Research.JobObjectModel { /// /// Dynamic information of all the jobs and machines in a cluster. @@ -119,22 +118,20 @@ namespace Microsoft.Research.Calypso.JobObjectModel /// The cached of tasks on the cluster. /// /// Virtual cluster selected; defined only for Scope clusters. - /// Delegate used to report errors. - /// Used to report progress. - public IEnumerable GetClusterJobList(string virtualCluster, StatusReporter reporter, Action reportProgress) + /// Communication manager. + public IEnumerable GetClusterJobList(string virtualCluster, CommManager manager) { - this.RecomputeClusterJobList(virtualCluster, reporter, reportProgress); + this.RecomputeClusterJobList(virtualCluster, manager); return this.clusterJobs.Values.ToList(); } /// /// Force the recomputation of the cluster job list. /// - /// Delegate used to report errors. /// Virtual cluster to use (defined only for some cluster types). - /// Used to report progress. + /// Communication manager. // ReSharper disable once UnusedParameter.Global - protected abstract void RecomputeClusterJobList(string virtualCluster, StatusReporter reporter, Action reportProgress); + protected abstract void RecomputeClusterJobList(string virtualCluster, CommManager manager); /// /// Discover the (unique) dryadlinq job corresponding to a cluster job. @@ -160,12 +157,11 @@ namespace Microsoft.Research.Calypso.JobObjectModel /// /// Job to discover. /// The cluster job, or null if not found. - /// Delegate used to report errors. - /// Used to report progress. - public virtual ClusterJobInformation DiscoverClusterJob(DryadLinqJobSummary job, StatusReporter reporter, Action reportProgress) + /// Communication manager. + public virtual ClusterJobInformation DiscoverClusterJob(DryadLinqJobSummary job, CommManager manager) { if (this.clusterJobs == null) - this.RecomputeClusterJobList(job.VirtualCluster, reporter, reportProgress); + this.RecomputeClusterJobList(job.VirtualCluster, manager); return this.clusterJobs[job.ClusterJobId]; } @@ -173,13 +169,12 @@ namespace Microsoft.Research.Calypso.JobObjectModel /// Refresh the job summary status. /// /// Summary to refresh. - /// Delegate used to report errors. - /// Used to report progress. - public virtual void RefreshStatus(DryadLinqJobSummary summary, StatusReporter reporter, Action reportProgress) + /// Communication manager. + public virtual void RefreshStatus(DryadLinqJobSummary summary, CommManager manager) { // refresh the whole list - this.RecomputeClusterJobList(summary.VirtualCluster, reporter, reportProgress); - ClusterJobInformation info = this.DiscoverClusterJob(summary, reporter, reportProgress); + this.RecomputeClusterJobList(summary.VirtualCluster, manager); + ClusterJobInformation info = this.DiscoverClusterJob(summary, manager); if (info == null) { summary.Status = ClusterJobInformation.ClusterJobStatus.Unknown; @@ -226,10 +221,9 @@ namespace Microsoft.Research.Calypso.JobObjectModel /// /// Recompute the list of jobs on the cluster and add them to the clusterJobs field. /// - /// Delegate used to report errors. /// Unused. - /// Used to report progress. - protected override void RecomputeClusterJobList(string virtualCluster, StatusReporter reporter, Action reportProgress) + /// Communication manager. + protected override void RecomputeClusterJobList(string virtualCluster, CommManager manager) { this.clusterJobs = new Dictionary(); if (string.IsNullOrEmpty(CachedClusterResidentObject.CacheDirectory)) @@ -242,32 +236,32 @@ namespace Microsoft.Research.Calypso.JobObjectModel string[] files = Directory.GetFiles(joblist, "*.xml"); foreach (var file in files) { + manager.Token.ThrowIfCancellationRequested(); DryadLinqJobSummary job = Utilities.LoadXml(file); string cjid = job.Cluster + "-" + job.ClusterJobId; // there may be two jobs with same id from different clusters ClusterJobInformation ci = new ClusterJobInformation(this.Config.Name, job.Cluster, cjid, job.Name, job.User, job.Date, job.EndTime - job.Date, job.Status); ci.SetAssociatedSummary(job); if (this.clusterJobs.ContainsKey(cjid)) { - reporter("Duplicate job id, cannot insert in cache " + job.AsIdentifyingString(), StatusKind.Error); + manager.Status("Duplicate job id, cannot insert in cache " + job.AsIdentifyingString(), StatusKind.Error); continue; } this.clusterJobs.Add(cjid, ci); } - reportProgress(100); + manager.Progress(100); } /// /// Refresh the job summary status. /// /// Summary to refresh. - /// Delegate used to report errors. - /// Used to report progres. - public override void RefreshStatus(DryadLinqJobSummary job, StatusReporter reporter, Action reportProgress) + /// Communication manager. + public override void RefreshStatus(DryadLinqJobSummary job, CommManager manager) { ClusterConfiguration actual = (this.Config as CacheClusterConfiguration).ActualConfig(job); ClusterStatus actualStatus = actual.CreateClusterStatus(); - actualStatus.RefreshStatus(job, reporter, reportProgress); - ClusterJobInformation info = actualStatus.DiscoverClusterJob(job, reporter, reportProgress); + actualStatus.RefreshStatus(job, manager); + ClusterJobInformation info = actualStatus.DiscoverClusterJob(job, manager); if (info == null) { job.Status = ClusterJobInformation.ClusterJobStatus.Unknown; @@ -303,13 +297,12 @@ namespace Microsoft.Research.Calypso.JobObjectModel /// /// Cluster job. /// Throws an exception. - /// Delegate used to report errors. - /// Used to report progress. - public override ClusterJobInformation DiscoverClusterJob(DryadLinqJobSummary job, StatusReporter reporter, Action reportProgress) + /// Communication manager. + public override ClusterJobInformation DiscoverClusterJob(DryadLinqJobSummary job, CommManager manager) { ClusterConfiguration actual = (this.Config as CacheClusterConfiguration).ActualConfig(job); ClusterStatus actualStatus = actual.CreateClusterStatus(); - return actualStatus.DiscoverClusterJob(job, reporter, reportProgress); + return actualStatus.DiscoverClusterJob(job, manager); } /// @@ -358,10 +351,9 @@ namespace Microsoft.Research.Calypso.JobObjectModel /// /// Force the recomputation of the cluster job list. /// - /// Delegate used to report errors. /// Virtual cluster to use (defined only for some cluster types). - /// Used to report progress. - protected override void RecomputeClusterJobList(string virtualCluster, StatusReporter reporter, Action reportProgress) + /// Communication manager. + protected override void RecomputeClusterJobList(string virtualCluster, CommManager manager) { this.clusterJobs = new Dictionary(); if (!Directory.Exists(this.config.JobsFolder)) @@ -371,6 +363,7 @@ namespace Microsoft.Research.Calypso.JobObjectModel int done = 0; foreach (var job in subfolders) { + manager.Token.ThrowIfCancellationRequested(); string jobId = Path.GetFileName(job); ClusterJobInformation info = this.GetJobInfo(job, jobId); if (info != null) @@ -378,9 +371,9 @@ namespace Microsoft.Research.Calypso.JobObjectModel // ReSharper disable once AssignNullToNotNullAttribute this.clusterJobs.Add(jobId, info); } - reportProgress(done++ *100/subfolders.Length); + manager.Progress(done++ *100/subfolders.Length); } - reportProgress(100); + manager.Progress(100); } /// @@ -478,10 +471,9 @@ namespace Microsoft.Research.Calypso.JobObjectModel /// /// Force the recomputation of the cluster job list. /// - /// Delegate used to report errors. /// Virtual cluster to use (defined only for some cluster types). - /// Used to report progress. - protected override void RecomputeClusterJobList(string virtualCluster, StatusReporter reporter, Action reportProgress) + /// Communication manager. + protected override void RecomputeClusterJobList(string virtualCluster, CommManager manager) { this.clusterJobs = new Dictionary(); var jobs = this.config.AzureClient.EnumerateDirectory("").ToList(); @@ -489,15 +481,16 @@ namespace Microsoft.Research.Calypso.JobObjectModel int done = 0; foreach (var job in jobs) { + manager.Token.ThrowIfCancellationRequested(); ClusterJobInformation info = this.GetJobInfo(job); if (info != null) { // ReSharper disable once AssignNullToNotNullAttribute this.clusterJobs.Add(job, info); } - reportProgress(100*done++/jobs.Count); + manager.Progress(100*done++/jobs.Count); } - reportProgress(100); + manager.Progress(100); } /// @@ -547,23 +540,12 @@ namespace Microsoft.Research.Calypso.JobObjectModel DateTime lastHeartBeat = DateTime.MinValue; ClusterJobInformation.ClusterJobStatus status = ClusterJobInformation.ClusterJobStatus.Unknown; bool found = false; + string jobName = jobRootFolder; var jobsFolders = this.config.AzureClient.EnumerateDirectory(jobRootFolder).ToList(); foreach (var file in jobsFolders) { - if (file.Contains("DryadLinqProgram__")) - { - var blob = this.config.AzureClient.Container.GetBlockBlobReference(file); - blob.FetchAttributes(); - var props = blob.Properties; - if (props.LastModified.HasValue) - { - date = props.LastModified.Value.DateTime; - date = date.ToLocalTime(); - } - found = true; - } - else if (file.EndsWith("heartbeat")) + if (file.EndsWith("heartbeat")) { var blob = this.config.AzureClient.Container.GetPageBlobReference(file); blob.FetchAttributes(); @@ -582,6 +564,9 @@ namespace Microsoft.Research.Calypso.JobObjectModel case "running": status = ClusterJobInformation.ClusterJobStatus.Running; break; + case "killed": + status = ClusterJobInformation.ClusterJobStatus.Cancelled; + break; default: Console.WriteLine("Unknown status " + st); break; @@ -599,6 +584,31 @@ namespace Microsoft.Research.Calypso.JobObjectModel status = ClusterJobInformation.ClusterJobStatus.Failed; } } + if (props.ContainsKey("jobname")) + { + jobName = props["jobname"]; + } + if (props.ContainsKey("starttime")) + { + var t = props["starttime"]; + if (DateTime.TryParse(t, out date)) + date = date.ToLocalTime(); + } + + found = true; + } + else if (file.Contains("DryadLinqProgram__") && + // newer heartbeats contain the date + date != DateTime.MinValue) + { + var blob = this.config.AzureClient.Container.GetBlockBlobReference(file); + blob.FetchAttributes(); + var props = blob.Properties; + if (props.LastModified.HasValue) + { + date = props.LastModified.Value.DateTime; + date = date.ToLocalTime(); + } } } @@ -607,7 +617,7 @@ namespace Microsoft.Research.Calypso.JobObjectModel TimeSpan running = TimeSpan.Zero; if (date != DateTime.MinValue && lastHeartBeat != DateTime.MinValue) running = lastHeartBeat - date; - var info = new ClusterJobInformation(this.config.Name, "", jobRootFolder, jobRootFolder, Environment.UserName, date, running, status); + var info = new ClusterJobInformation(this.config.Name, "", jobRootFolder, jobName, Environment.UserName, date, running, status); return info; } @@ -615,9 +625,8 @@ namespace Microsoft.Research.Calypso.JobObjectModel /// Refresh the job summary status. /// /// Summary to refresh. - /// Delegate used to report errors. - /// Used to report progress. - public override void RefreshStatus(DryadLinqJobSummary summary, StatusReporter reporter, Action reportProgress) + /// Communication manager. + public override void RefreshStatus(DryadLinqJobSummary summary, CommManager manager) { // refresh the whole list ClusterJobInformation info = this.GetJobInfo(summary.JobID); @@ -636,7 +645,8 @@ namespace Microsoft.Research.Calypso.JobObjectModel /// True if the cancellation succeeded. public override bool CancelJob(DryadLinqJobSummary job) { - throw new InvalidOperationException(); + AzureUtils.KillJob(this.config.AccountName, this.config.AccountKey, this.config.Container, job.ClusterJobId); + return true; } } } diff --git a/JobBrowser/JOM/JobObjectModel.csproj b/JobBrowser/JOM/JobObjectModel.csproj index b7c7852..cabcc93 100644 --- a/JobBrowser/JOM/JobObjectModel.csproj +++ b/JobBrowser/JOM/JobObjectModel.csproj @@ -1,6 +1,6 @@ - + - + Debug AnyCPU @@ -10,7 +10,7 @@ Library Properties DistributedDataCollection - Microsoft.Research.DryadLinq.JobBrowser.DistributedDataCollection + DistributedDataCollection v4.5 512 @@ -37,28 +37,6 @@ true - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - bin\Debug\Microsoft.Research.DryadLinq.JobBrowser.DistributedDataCollection.xml - AllRules.ruleset - false - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - AllRules.ruleset - false - true bin\x64\Debug\ @@ -91,43 +69,59 @@ False ..\packages\Microsoft.Data.Services.Client.5.6.1\lib\net40\Microsoft.Data.Services.Client.dll - + + False ..\packages\Microsoft.Hadoop.Client.1.1.0.7\lib\net40\Microsoft.Hadoop.Client.dll - - False - ..\Dependencies\Microsoft.Hpc.Scheduler.dll - - - False - ..\Dependencies\Microsoft.Hpc.Scheduler.Properties.dll - False - ..\packages\Microsoft.Research.Peloponnese.0.7.1-beta\lib\net45\Microsoft.Research.Peloponnese.HadoopBridge.dll + ..\packages\Microsoft.Research.Peloponnese.0.7.2-beta\lib\net45\Microsoft.Research.Peloponnese.HadoopBridge.dll False - ..\packages\Microsoft.Research.Peloponnese.0.7.1-beta\lib\net45\Microsoft.Research.Peloponnese.Utils.dll + ..\packages\Microsoft.Research.Peloponnese.0.7.2-beta\lib\net45\Microsoft.Research.Peloponnese.Utils.dll + + + ..\packages\Microsoft.Bcl.Async.1.0.166\lib\net40\Microsoft.Threading.Tasks.dll + + + ..\packages\Microsoft.Bcl.Async.1.0.166\lib\net40\Microsoft.Threading.Tasks.Extensions.dll + + + ..\packages\Microsoft.Bcl.Async.1.0.166\lib\net40\Microsoft.Threading.Tasks.Extensions.Desktop.dll + + + ..\packages\Microsoft.WindowsAzure.Common.1.0.1\lib\net45\Microsoft.WindowsAzure.Common.dll + + + ..\packages\Microsoft.WindowsAzure.Common.1.0.1\lib\net45\Microsoft.WindowsAzure.Common.NetFramework.dll ..\packages\Microsoft.WindowsAzure.ConfigurationManager.2.0.3\lib\net40\Microsoft.WindowsAzure.Configuration.dll - + + ..\packages\Microsoft.WindowsAzure.Management.1.0.0\lib\net40\Microsoft.WindowsAzure.Management.dll + + + False ..\packages\Microsoft.WindowsAzure.Management.HDInsight.1.1.0.7\lib\net40\Microsoft.WindowsAzure.Management.HDInsight.dll - + + False ..\packages\Microsoft.Hadoop.Client.1.1.0.7\lib\net40\Microsoft.WindowsAzure.Management.HDInsight.Framework.dll - + + False ..\packages\Microsoft.Hadoop.Client.1.1.0.7\lib\net40\Microsoft.WindowsAzure.Management.HDInsight.Framework.Core.dll + + ..\packages\Microsoft.WindowsAzure.Management.Storage.1.0.0\lib\net40\Microsoft.WindowsAzure.Management.Storage.dll + False ..\packages\WindowsAzure.Storage.3.1.0.1\lib\net40\Microsoft.WindowsAzure.Storage.dll - - False + ..\packages\Newtonsoft.Json.6.0.2\lib\net45\Newtonsoft.Json.dll @@ -135,6 +129,15 @@ 3.5 + + + + ..\packages\Microsoft.Net.Http.2.2.19\lib\net45\System.Net.Http.Extensions.dll + + + ..\packages\Microsoft.Net.Http.2.2.19\lib\net45\System.Net.Http.Primitives.dll + + False ..\packages\System.Spatial.5.6.1\lib\net40\System.Spatial.dll @@ -153,7 +156,7 @@ - + @@ -186,15 +189,19 @@ - + + + + + This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - + + - + - + \ No newline at end of file diff --git a/JobBrowser/JOM/Properties/AssemblyInfo.cs b/JobBrowser/JOM/Properties/AssemblyInfo.cs index 4b3319d..f51b2bc 100644 --- a/JobBrowser/JOM/Properties/AssemblyInfo.cs +++ b/JobBrowser/JOM/Properties/AssemblyInfo.cs @@ -28,9 +28,9 @@ using System.Runtime.InteropServices; [assembly: AssemblyTitle("DistributedDataCollection")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("Microsoft Corporation")] +[assembly: AssemblyCompany("")] [assembly: AssemblyProduct("DistributedDataCollection")] -[assembly: AssemblyCopyright("Copyright © Microsoft Corporation. All rights reserved.")] +[assembly: AssemblyCopyright("Copyright © 2009")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] @@ -52,5 +52,5 @@ using System.Runtime.InteropServices; // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("0.1.0.0")] -[assembly: AssemblyFileVersion("0.1.0.0")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/JobBrowser/JOM/app.config b/JobBrowser/JOM/app.config index 932f99f..5e4a799 100644 --- a/JobBrowser/JOM/app.config +++ b/JobBrowser/JOM/app.config @@ -2,10 +2,6 @@ - - - - @@ -22,6 +18,10 @@ + + + + \ No newline at end of file diff --git a/JobBrowser/JOM/clusteraccess.cs b/JobBrowser/JOM/clusteraccess.cs index 594d56f..3cf0ec9 100644 --- a/JobBrowser/JOM/clusteraccess.cs +++ b/JobBrowser/JOM/clusteraccess.cs @@ -20,15 +20,15 @@ limitations under the License. */ using System.Text.RegularExpressions; -using Microsoft.Research.Calypso.Tools; using System; using System.Collections.Generic; using System.IO; using System.Diagnostics; using Microsoft.Research.Peloponnese.Storage; +using Microsoft.Research.Tools; using Microsoft.WindowsAzure.Storage.Blob; -namespace Microsoft.Research.Calypso.JobObjectModel +namespace Microsoft.Research.JobObjectModel { /// /// A cluster-resident object is a file or a folder. @@ -158,7 +158,7 @@ namespace Microsoft.Research.Calypso.JobObjectModel if (file.Job == null) return; if (string.IsNullOrEmpty(file.LocalCachePath)) - throw new CalypsoClusterException("Missing expected LocalCachePath"); + throw new ClusterException("Missing expected LocalCachePath"); CachedClusterResidentObject.RecordCachedFile(file.Job, file.LocalCachePath); } @@ -375,7 +375,7 @@ namespace Microsoft.Research.Calypso.JobObjectModel { // cache it if (this.RepresentsAFolder) - throw new CalypsoClusterException("Cannot cache folders"); + throw new ClusterException("Cannot cache folders"); StreamWriter writer = this.CreateTempStream(); return new FileSharedStreamReader(this.Pathname.ToString(), writer, this.OnClose); @@ -504,7 +504,7 @@ namespace Microsoft.Research.Calypso.JobObjectModel get { if (this.RepresentsAFolder) - throw new CalypsoClusterException("Cannot get size of a folder"); + throw new ClusterException("Cannot get size of a folder"); if (File.Exists(this.LocalCachePath)) { FileInfo info = new FileInfo(this.LocalCachePath); @@ -732,7 +732,7 @@ namespace Microsoft.Research.Calypso.JobObjectModel /// Throws an exception. public IEnumerable GetFilesAndFolders(string match) { - throw new CalypsoClusterException("Object is not a folder"); + throw new ClusterException("Object is not a folder"); } private long size; @@ -787,7 +787,7 @@ namespace Microsoft.Research.Calypso.JobObjectModel /// Throws an exception. public IClusterResidentObject GetFile(string filename) { - throw new CalypsoClusterException("Object is not a folder"); + throw new ClusterException("Object is not a folder"); } /// @@ -797,7 +797,7 @@ namespace Microsoft.Research.Calypso.JobObjectModel /// Throws an exception. public IClusterResidentObject GetFolder(string foldername) { - throw new CalypsoClusterException("Object is not a folder"); + throw new ClusterException("Object is not a folder"); } } @@ -1011,13 +1011,24 @@ namespace Microsoft.Research.Calypso.JobObjectModel this.client.ContainerName, this.path); } - StreamReader reader = new StreamReader(stream, System.Text.Encoding.UTF8, false, 1024 * 1024); + + long size = this.Size; + int bufferSize = 1024*1024; + if (size >= 0) + { + bufferSize = (int)(size/10); + if (bufferSize < 1024*1024) + bufferSize = 1024*1024; + if (bufferSize > 20*1024*1024) + bufferSize = 20*1024*1024; + } + StreamReader reader = new StreamReader(stream, System.Text.Encoding.UTF8, false, bufferSize); if (this.ShouldCacheLocally && this.LocalCachePath != null) { // cache it if (this.RepresentsAFolder) - throw new CalypsoClusterException("Cannot cache folders"); + throw new ClusterException("Cannot cache folders"); StreamWriter writer = this.CreateTempStream(); return new SharedStreamReader(reader, writer, this.OnClose); } diff --git a/JobBrowser/JOM/cosmos.cs b/JobBrowser/JOM/dryadlog.cs similarity index 97% rename from JobBrowser/JOM/cosmos.cs rename to JobBrowser/JOM/dryadlog.cs index 11bc3cc..2f0453f 100644 --- a/JobBrowser/JOM/cosmos.cs +++ b/JobBrowser/JOM/dryadlog.cs @@ -19,17 +19,17 @@ limitations under the License. */ -namespace Microsoft.Research.Calypso.JobObjectModel +namespace Microsoft.Research.JobObjectModel { using System.Text.RegularExpressions; using System; using System.Diagnostics; /// - /// Information about a standard Cosmos log entry. + /// Information about a standard Dryad log entry. /// [Serializable] - public class CosmosLogEntry : IParse + public class DryadLogEntry : IParse { /// /// Message severity. @@ -98,7 +98,7 @@ namespace Microsoft.Research.Calypso.JobObjectModel /// /// Allocate an empty log entry /// - public CosmosLogEntry() + public DryadLogEntry() { this.Malformed = true; } @@ -107,7 +107,7 @@ namespace Microsoft.Research.Calypso.JobObjectModel /// Allocate a log entry from a given string. /// /// String to initialize the log entry. - public CosmosLogEntry(string line) + public DryadLogEntry(string line) { this.Malformed = true; // ReSharper disable once DoNotCallOverridableMethodsInConstructor @@ -167,7 +167,7 @@ namespace Microsoft.Research.Calypso.JobObjectModel /// An extended log entry is like a cosmos log entry, but it has a prefix: GUID,Machine /// [Serializable] - public class ExtendedLogEntry : CosmosLogEntry + public class ExtendedLogEntry : DryadLogEntry { static Regex loglineregex = new Regex(@"([-0-9A-F]+), # 1 guid, inserted by reader diff --git a/JobBrowser/JOM/jobinfo.cs b/JobBrowser/JOM/jobinfo.cs index 9e47d1d..231ab2a 100644 --- a/JobBrowser/JOM/jobinfo.cs +++ b/JobBrowser/JOM/jobinfo.cs @@ -27,24 +27,23 @@ using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; -using System.Text; using System.Text.RegularExpressions; using System.Xml.Linq; -using Microsoft.Research.Calypso.Tools; using System.Diagnostics; +using Microsoft.Research.Tools; -namespace Microsoft.Research.Calypso.JobObjectModel +namespace Microsoft.Research.JobObjectModel { /// - /// Exception throw by Calypso when it cannot understand the structure of a Dryad/DryadLINQ job. + /// Exception thrown when we cannot understand the structure of a Dryad/DryadLINQ job. /// - public class CalypsoDryadException : Exception + public class DryadException : Exception { /// - /// Create a new CalypsoDryadException. + /// Create a new DryadException. /// /// Message conveyed by the exception. - public CalypsoDryadException(string message) : base(message) { } + public DryadException(string message) : base(message) { } } /// @@ -195,7 +194,7 @@ namespace Microsoft.Research.Calypso.JobObjectModel this.SuccessfulVertices++; break; default: - throw new CalypsoDryadException("Unexpected vertex state " + vertex.State); + throw new DryadException("Unexpected vertex state " + vertex.State); } } this.TotalInitiatedVertices -= this.AbandonedVertices; @@ -423,22 +422,21 @@ namespace Microsoft.Research.Calypso.JobObjectModel /// Cluster configuration. /// Summary description of the job. /// The Dryad job description, or null. - /// Delegate used to report errors. /// If true, fill all the information, otherwise the user will have to call FillInformation on the result later. - /// Delegate used to report progress. - public static DryadLinqJobInfo CreateDryadLinqJobInfo(ClusterConfiguration cf, DryadLinqJobSummary summary, bool fill, StatusReporter reporter, Action updateProgress) + /// Communication manager. + public static DryadLinqJobInfo CreateDryadLinqJobInfo(ClusterConfiguration cf, DryadLinqJobSummary summary, bool fill, CommManager manager) { try { DryadLinqJobInfo job = new DryadLinqJobInfo(cf, summary); if (fill) - job.CollectEssentialInformation(reporter, updateProgress); + job.CollectEssentialInformation(manager); return job; } catch (Exception e) { Trace.TraceInformation(e.ToString()); - reporter("Could not collect job information for " + summary.Name + ": " + e.Message, StatusKind.Error); + manager.Status("Could not collect job information for " + summary.Name + ": " + e.Message, StatusKind.Error); return null; } } @@ -506,7 +504,7 @@ namespace Microsoft.Research.Calypso.JobObjectModel if (this.stdoutpath == null) { - throw new CalypsoClusterException("Could not locate JM standard output file in folder " + jmdir); + throw new ClusterException("Could not locate JM standard output file in folder " + jmdir); } } } @@ -515,9 +513,8 @@ namespace Microsoft.Research.Calypso.JobObjectModel /// /// Refresh the job status. /// - /// Delegate used to report errors. - /// Used to report progress. - public void RefreshJobStatus(StatusReporter reporter, Action updateProgress) + /// Communication manager. + public void RefreshJobStatus(CommManager manager) { // skip if job is finished if (this.Summary.Status == ClusterJobInformation.ClusterJobStatus.Failed || @@ -526,18 +523,17 @@ namespace Microsoft.Research.Calypso.JobObjectModel return; ClusterStatus status = this.ClusterConfiguration.CreateClusterStatus(); - status.RefreshStatus(this.Summary, reporter, updateProgress); + status.RefreshStatus(this.Summary, manager); } /// /// Fill the job info by parsing the stdout.txt. - /// Delegate used to report errors. - /// True if it succeeds, false otherwise. - /// Delegate used to report progress. + /// The updated job. + /// Communication manager. /// - public bool CollectEssentialInformation(StatusReporter statusReporter, Action updateProgress) + public bool CollectEssentialInformation(CommManager manager) { - this.RefreshJobStatus(statusReporter, updateProgress); + this.RefreshJobStatus(manager); if (this.ManagerVertex == null) { this.ManagerVertex = new ExecutedVertexInstance(this, -1, 0, "JobManager", "", this.Summary.Date); @@ -562,13 +558,13 @@ namespace Microsoft.Research.Calypso.JobObjectModel if (this.stdoutpath == null) return false; - bool success = this.ParseStdout(this.stdoutpath, statusReporter, updateProgress); - updateProgress(100); + bool success = this.ParseStdout(this.stdoutpath, manager); + manager.Progress(100); if (!success) return false; this.JobInfoCannotBeCollected = false; - statusReporter("Stdout parsed", StatusKind.OK); + manager.Status("Stdout parsed", StatusKind.OK); this.LastUpdatetime = DateTime.Now; if (this.Summary.Status == ClusterJobInformation.ClusterJobStatus.Running) @@ -600,13 +596,13 @@ namespace Microsoft.Research.Calypso.JobObjectModel { Match m = numberre.Match(vertexlist); if (!m.Success) - throw new CalypsoDryadException("Could not find vertex number in " + vertexlist); + throw new DryadException("Could not find vertex number in " + vertexlist); string number = m.Groups[1].Value; // now scan a balanced number of parantheses string rest = m.Groups[2].Value; if (rest[0] != '(') - throw new CalypsoDryadException("Expecting open parens after vertex number"); + throw new DryadException("Expecting open parens after vertex number"); int opened = 0; int i; for (i = 0; i < rest.Length; i++) @@ -624,7 +620,7 @@ namespace Microsoft.Research.Calypso.JobObjectModel } } if (opened != 0 || i <= 2) - throw new CalypsoDryadException("did not find matched parantheses in vertex name in " + vertexlist + ", can't parse"); + throw new DryadException("did not find matched parantheses in vertex name in " + vertexlist + ", can't parse"); string name = rest.Substring(1, i - 2); // skip first and last paranthesis yield return new Tuple(name, int.Parse(number)); vertexlist = rest.Substring(i); @@ -761,7 +757,9 @@ namespace Microsoft.Research.Calypso.JobObjectModel case "running": { string process; - kvp.TryGetValue("id", out process); // "process" is also good + kvp.TryGetValue("id", out process); + if (process == null) + kvp.TryGetValue("process", out process); string machine = kvp["computer"]; ExecutedVertexInstance vi = this.jobVertices.FindVertex(number, version); this.jobVertices.Remap(vi, process); @@ -776,6 +774,7 @@ namespace Microsoft.Research.Calypso.JobObjectModel vi.SetState(ExecutedVertexInstance.VertexState.Successful); vi.End = timeStamp; vi.ExitCode = ""; + this.UsefulCPUTime += vi.RunningTime; break; } case "failed": @@ -784,7 +783,11 @@ namespace Microsoft.Research.Calypso.JobObjectModel if (vi.State != ExecutedVertexInstance.VertexState.Started) vi.SetState(ExecutedVertexInstance.VertexState.Cancelled); else + { vi.SetState(ExecutedVertexInstance.VertexState.Failed); + if (vi.RunningTime > TimeSpan.Zero) + this.WastedCPUTime += vi.RunningTime; + } if (kvp.ContainsKey("errorstring")) vi.AddErrorString(kvp["errorstring"]); string exitcode; @@ -797,32 +800,15 @@ namespace Microsoft.Research.Calypso.JobObjectModel } else if (kvp.ContainsKey("outputChannel")) { - string chan = kvp["outputChannel"]; - int channelNo = int.Parse(chan); ExecutedVertexInstance vi = this.jobVertices.FindVertex(number, version); - - if (!kvp.ContainsKey("errorstatus")) - { - } - else - { - if (kvp.ContainsKey("errorstring")) - vi.AddErrorString(kvp["errorstring"]); - } + if (kvp.ContainsKey("errorstring")) + vi.AddErrorString(kvp["errorstring"]); } else if (kvp.ContainsKey("inputChannel")) { - string chan = kvp["inputChannel"]; - int channelNo = int.Parse(chan); ExecutedVertexInstance vi = this.jobVertices.FindVertex(number, version); - - if (!kvp.ContainsKey("errorstatus")) - { - } - else - { + if (kvp.ContainsKey("errorstring")) vi.AddErrorString(kvp["errorstring"]); - } } else if (kvp.ContainsKey("io")) { @@ -834,6 +820,7 @@ namespace Microsoft.Research.Calypso.JobObjectModel if (vi.InputChannels == null) vi.InputChannels = new Dictionary(); + for (int i = 0; i < numberOfInputs; i++) { string uri; @@ -870,6 +857,24 @@ namespace Microsoft.Research.Calypso.JobObjectModel vi.DataRead = totalRead; vi.DataWritten = totalWritten; + if (vi.InputChannels != null) + { + foreach (int ch in vi.InputChannels.Keys) + { + long bytes = TryGetNumeric(kvp, "rb." + ch); + vi.InputChannels[ch].Size = bytes; + } + } + + if (vi.OutputChannels != null) + { + foreach (int ch in vi.OutputChannels.Keys) + { + long bytes = TryGetNumeric(kvp, "wb." + ch); + vi.OutputChannels[ch].Size = bytes; + } + } + this.TotalDataRead += totalRead; this.LocalReadData += localRead; this.CrossPodDataRead += tempReadCrossRack; @@ -878,17 +883,26 @@ namespace Microsoft.Research.Calypso.JobObjectModel else if (kvp["io"] == "running") { ExecutedVertexInstance vi = this.jobVertices.FindVertex(number, version); - - foreach (int ch in vi.InputChannels.Keys) + + if (vi.InputChannels != null) { - long bytes = TryGetNumeric(kvp, "rb." + ch); - vi.InputChannels[ch].Size = bytes; + foreach (int ch in vi.InputChannels.Keys) + { + long bytes = TryGetNumeric(kvp, "rb." + ch); + vi.InputChannels[ch].Size = bytes; + + bytes = TryGetNumeric(kvp, "tb." + ch); + vi.InputChannels[ch].TotalSize = bytes; + } } - foreach (int ch in vi.OutputChannels.Keys) + if (vi.InputChannels != null) { - long bytes = TryGetNumeric(kvp, "wb." + ch); - vi.OutputChannels[ch].Size = bytes; + foreach (int ch in vi.OutputChannels.Keys) + { + long bytes = TryGetNumeric(kvp, "wb." + ch); + vi.OutputChannels[ch].Size = bytes; + } } long totalRead = TryGetNumeric(kvp, "totalRead"); @@ -1062,7 +1076,7 @@ namespace Microsoft.Research.Calypso.JobObjectModel else if (m.Groups[1].Value == "ices") onevertex = false; else - throw new CalypsoDryadException("Can't figure out if one or many vertices"); + throw new DryadException("Can't figure out if one or many vertices"); IEnumerable> vertexList = DryadLinqJobInfo.ParseVertices(vertices); @@ -1084,7 +1098,7 @@ namespace Microsoft.Research.Calypso.JobObjectModel } if (vertexcount > 1 && onevertex) - throw new CalypsoDryadException("Expected one vertex, found " + vertexcount); + throw new DryadException("Expected one vertex, found " + vertexcount); } else { @@ -1225,7 +1239,7 @@ namespace Microsoft.Research.Calypso.JobObjectModel if (vi.State == ExecutedVertexInstance.VertexState.Started) { Console.WriteLine("Timing information while vertex is still running " + vi); - //throw new CalypsoClusterException("Timing information for vertex still running: " + vi); + //throw new ClusterException("Timing information for vertex still running: " + vi); } DateTime last = vi.SetTiming(createtime, m.Groups[5].Value, m.Groups[6].Value, m.Groups[7].Value, m.Groups[8].Value, m.Groups[9].Value); if (last > this.lastTimestampSeen) @@ -1245,7 +1259,7 @@ namespace Microsoft.Research.Calypso.JobObjectModel } } else - throw new CalypsoDryadException("Unmatched timing information line " + line); + throw new DryadException("Unmatched timing information line " + line); } else if (line.Contains("Process has failed")) { @@ -1410,7 +1424,7 @@ namespace Microsoft.Research.Calypso.JobObjectModel return retval; } - private ISharedStreamReader cachedStdoutReader = null; + private ISharedStreamReader cachedStdoutReader; /// /// Remember how many lines were parsed, and skip them on a second invocation. @@ -1420,10 +1434,9 @@ namespace Microsoft.Research.Calypso.JobObjectModel /// Parse the stdout.txt file from the job manager. /// /// File to parse. - /// Delegate used to report errors. - /// Delegate used to report progress. + /// Communication manager. /// True if the parsing succeeds. - private bool ParseStdout(IClusterResidentObject file, StatusReporter statusReporter, Action updateProgress) + private bool ParseStdout(IClusterResidentObject file, CommManager manager) { int currentLine = 0; if (this.stdoutLinesParsed == 0) @@ -1440,15 +1453,16 @@ namespace Microsoft.Research.Calypso.JobObjectModel string message = "Scanning JM stdout " + file; if (filesize >= 0) message += string.Format("({0:N0} bytes)", filesize); - statusReporter(message, StatusKind.LongOp); + manager.Status(message, StatusKind.LongOp); if (this.cachedStdoutReader == null) this.cachedStdoutReader = file.GetStream(); if (this.cachedStdoutReader.Exception != null) { - statusReporter("Exception while opening stdout " + this.cachedStdoutReader.Exception.Message, StatusKind.Error); + manager.Status("Exception while opening stdout " + this.cachedStdoutReader.Exception.Message, StatusKind.Error); return false; } + while (!this.cachedStdoutReader.EndOfStream) { string line = this.cachedStdoutReader.ReadLine(); @@ -1457,6 +1471,7 @@ namespace Microsoft.Research.Calypso.JobObjectModel { while (true) { + manager.Token.ThrowIfCancellationRequested(); int startLine = currentLine; bool completeLine = true; try @@ -1465,7 +1480,7 @@ namespace Microsoft.Research.Calypso.JobObjectModel } catch (Exception ex) { - statusReporter(string.Format("Line {0}: Exception {1}", currentLine, ex.Message), StatusKind.Error); + manager.Status(string.Format("Line {0}: Exception {1}", currentLine, ex.Message), StatusKind.Error); Console.WriteLine("Line {0}: Exception {1}", currentLine, ex); } if (!completeLine) @@ -1485,7 +1500,7 @@ namespace Microsoft.Research.Calypso.JobObjectModel currentLine++; if (currentLine % 100 == 0 && filesize > 0) { - updateProgress(Math.Min(100, (int)(100 * readbytes / filesize))); + manager.Progress(Math.Min(100, (int)(100 * readbytes / filesize))); } } @@ -1500,13 +1515,16 @@ namespace Microsoft.Research.Calypso.JobObjectModel // we are done with this stream if (this.ManagerVertex.State == ExecutedVertexInstance.VertexState.Failed || this.ManagerVertex.State == ExecutedVertexInstance.VertexState.Successful) + { this.cachedStdoutReader.Close(); + this.cachedStdoutReader = null; // will force reopening if refreshed + } } return true; } catch (Exception e) { - statusReporter("Exception while reading stdout " + e.Message, StatusKind.Error); + manager.Status("Exception while reading stdout " + e.Message, StatusKind.Error); Trace.TraceInformation(e.ToString()); return false; } @@ -1592,7 +1610,7 @@ namespace Microsoft.Research.Calypso.JobObjectModel string line = sr.ReadLine(); if (!line.Contains("DryadProfiler")) continue; - CosmosLogEntry le = new CosmosLogEntry(line); + DryadLogEntry le = new DryadLogEntry(line); if (le.Subsystem != "DryadProfiler") continue; if (!le.Message.EndsWith("channel status")) continue; @@ -1905,7 +1923,8 @@ namespace Microsoft.Research.Calypso.JobObjectModel /// /// Parse the query plan: cluster-specific. /// - protected abstract void ParseQueryPlan(); + /// Communication manager. + protected abstract void ParseQueryPlan(CommManager manager); int fictitiousStages; @@ -2009,11 +2028,11 @@ namespace Microsoft.Research.Calypso.JobObjectModel /// Factory: create the plan for a given job. /// /// Job to create plan for. - /// Delegate used to report errors. /// The plan or null. - public static DryadJobStaticPlan CreatePlan(DryadLinqJobInfo dryadLinqJobInfo, StatusReporter reporter) + /// Communication manager. + public static DryadJobStaticPlan CreatePlan(DryadLinqJobInfo dryadLinqJobInfo, CommManager manager) { - reporter("Trying to build static plan", StatusKind.LongOp); + manager.Status("Trying to build static plan", StatusKind.LongOp); ClusterConfiguration config = dryadLinqJobInfo.ClusterConfiguration; IClusterResidentObject file = config.JobQueryPlan(dryadLinqJobInfo.Summary); if (config is CacheClusterConfiguration) @@ -2025,12 +2044,12 @@ namespace Microsoft.Research.Calypso.JobObjectModel { retval = new DryadLinqJobStaticPlan(config, file.GetStream()); } - retval.ParseQueryPlan(); + retval.ParseQueryPlan(manager); return retval; } else { - reporter("Exception while looking for plan " + file.Exception.Message, StatusKind.Error); + manager.Status("Exception while looking for plan " + file.Exception.Message, StatusKind.Error); return null; } } @@ -2056,9 +2075,10 @@ namespace Microsoft.Research.Calypso.JobObjectModel /// /// Parse an XML query plan and represent that information. /// - protected override void ParseQueryPlan() + /// Communicaton manager. + protected override void ParseQueryPlan(CommManager manager) { - string planString = this.planStream.ReadToEnd(); + string planString = this.planStream.ReadToEnd(manager.Token); XDocument plan = XDocument.Parse(planString); // ReSharper disable PossibleNullReferenceException @@ -2104,7 +2124,7 @@ namespace Microsoft.Research.Calypso.JobObjectModel info.Arity = Connection.ConnectionType.AllToAll; break; default: - throw new CalypsoDryadException("Don't know about connection of type " + connection); + throw new DryadException("Don't know about connection of type " + connection); } switch (cht) { @@ -2118,7 +2138,7 @@ namespace Microsoft.Research.Calypso.JobObjectModel info.ChannelKind = Connection.ChannelType.Fifo; break; default: - throw new CalypsoDryadException("Don't know about channel of type " + cht); + throw new DryadException("Don't know about channel of type " + cht); } this.perNodeConnectionInfo.Add(stage.Id, info); } @@ -2247,14 +2267,15 @@ namespace Microsoft.Research.Calypso.JobObjectModel /// /// Parse the Algebra file. /// - private void ParseAlgebra() + /// Communication manager. + private void ParseAlgebra(CommManager manager) { // TODO: this parser is not really complete, as I don't understand the semantics of all xml elements. Dictionary outToStage = new Dictionary(); // map an output to a stage name. Assume that ios have unique names. Dictionary> inputs = new Dictionary>(); // ... - string planString = this.planStream.ReadToEnd(); + string planString = this.planStream.ReadToEnd(manager.Token); XDocument plan = XDocument.Parse(planString); // ReSharper disable PossibleNullReferenceException XElement graph = plan.Root.Element("graph"); // graph node, children are stages @@ -2416,13 +2437,13 @@ namespace Microsoft.Research.Calypso.JobObjectModel /// /// Parse the vertex definition file. /// - private void ParseVertexDef() + private void ParseVertexDef(CommManager manager) { if (this.vertexDef.Exception != null) return; // - string planString = this.vertexDef.ReadToEnd(); + string planString = this.vertexDef.ReadToEnd(manager.Token); XDocument vxDef = XDocument.Parse(planString); XElement vertices = vxDef.Root; @@ -2469,10 +2490,11 @@ namespace Microsoft.Research.Calypso.JobObjectModel /// /// Parse the query plan for a Scope job. /// - protected override void ParseQueryPlan() + /// Communication manager. + protected override void ParseQueryPlan(CommManager manager) { - this.ParseAlgebra(); - this.ParseVertexDef(); + this.ParseAlgebra(manager); + this.ParseVertexDef(manager); } } @@ -2653,9 +2675,13 @@ namespace Microsoft.Research.Calypso.JobObjectModel /// public string LocalPath { get; protected set; } /// - /// How big is the channel (0 if it cannot be determined, e.g. FIFO, -1 if the channel data cannot be retrieved). + /// The actual data read/written so far (0 if it cannot be determined, e.g. FIFO, -1 if the channel data cannot be retrieved). /// public long Size { get; set; } + /// + /// How much of the channel was + /// + public long TotalSize { get; set; } /// /// String representation of the endpoint. @@ -2664,7 +2690,10 @@ namespace Microsoft.Research.Calypso.JobObjectModel { string uritype = this.UriType; string localpath = this.LocalPath; - return string.Format("{0,4} {1,20:N0} {2}://{3}", this.Number, this.Size, uritype, localpath); + if (this.TotalSize == 0) + return string.Format("{0,4} {1,20:N0} {2}://{3}", this.Number, this.Size, uritype, localpath); + else + return string.Format("{0,4} {1,20:N0}/{2,20:N0} {3}://{4}", this.Number, this.Size, this.TotalSize, uritype, localpath); } /// @@ -2684,7 +2713,7 @@ namespace Microsoft.Research.Calypso.JobObjectModel int sepindex = uri.IndexOf("://"); if (sepindex < 0) - throw new CalypsoDryadException("Channel URI " + uri + " does not contain separator ://"); + throw new DryadException("Channel URI " + uri + " does not contain separator ://"); this.UriType = uri.Substring(0, sepindex); // some HPC URIs use the compression scheme as an "option" (not really defined for file:// uris, but...) @@ -2737,10 +2766,10 @@ namespace Microsoft.Research.Calypso.JobObjectModel this.Number = number; int sepindex = uri.IndexOf("://"); if (sepindex < 0) - throw new CalypsoClusterException("Channel URI " + uri + " does not contain separator ://"); + throw new ClusterException("Channel URI " + uri + " does not contain separator ://"); this.UriType = uri.Substring(0, sepindex); this.LocalPath = uri.Substring(sepindex + 3); - this.Size = size; + this.TotalSize = size; } } @@ -2883,7 +2912,7 @@ namespace Microsoft.Research.Calypso.JobObjectModel this.StdoutFile = job.ClusterConfiguration.ProcessStdoutFile(this.ProcessIdentifier, false, machine, job.Summary); this.SetState(VertexState.Started); if (approxStartTime == DateTime.MinValue) - throw new CalypsoDryadException("Unexpected small start time for vertex"); + throw new DryadException("Unexpected small start time for vertex"); this.LogDirectory = job.ClusterConfiguration.ProcessLogDirectory(this.ProcessIdentifier, false, machine, job.Summary); this.LogFilesPattern = job.ClusterConfiguration.VertexLogFilesPattern(false, job.Summary); this.UniqueID = uniqueId; @@ -3049,7 +3078,7 @@ namespace Microsoft.Research.Calypso.JobObjectModel { Trace.TraceInformation("Vertex {0} which is not started is still running?", this.Name); return; - //throw new CalypsoClusterException("Vertex which is not started is still running?"); + //throw new ClusterException("Vertex which is not started is still running?"); } if (this.Start > when) // This can happen if the cluster clocks are not synchronized with the local machine clocks. @@ -3088,7 +3117,7 @@ namespace Microsoft.Research.Calypso.JobObjectModel TimeSpan total = TimeSpan.FromSeconds(totSeconds); DateTime totalTime = creation + total; if (totSeconds < 0) - throw new CalypsoDryadException("Negative total time for vertex " + this.Name); + throw new DryadException("Negative total time for vertex " + this.Name); // if the vertex has no machine just ignore the times if (string.IsNullOrEmpty(this.Machine)) @@ -3209,9 +3238,8 @@ namespace Microsoft.Research.Calypso.JobObjectModel /// If the channel is an output, prefix the path with this; this is null for inputs. /// If true, do not return anything (still useful to advance the stream reader). /// If true the channel sizes are not discovered; this is much faster, since no remote machines are queried for files. - /// Delegate used to report progress. - /// Delegate used to report errors. - private Dictionary DiscoverOriginalInfoChannels(ISharedStreamReader sr, string uriprefix, bool skip, bool fast, StatusReporter reporter, Action updateProgress) + /// Communication manager. + private Dictionary DiscoverOriginalInfoChannels(ISharedStreamReader sr, string uriprefix, bool skip, bool fast, CommManager manager) { bool isInput = uriprefix == null; @@ -3231,21 +3259,18 @@ namespace Microsoft.Research.Calypso.JobObjectModel string channel = sr.ReadLine(); if (channel == null) { - if (updateProgress != null) - updateProgress(100); + manager.Progress(100); return null; } if (!skip) { - ChannelEndpointDescription desc = new ChannelEndpointDescription(isInput, i, channel, uriprefix, fast, reporter); + ChannelEndpointDescription desc = new ChannelEndpointDescription(isInput, i, channel, uriprefix, fast, manager.Status); channels.Add(i, desc); - if (updateProgress != null) - updateProgress(i * 100 / channelCount); + manager.Progress(i * 100 / channelCount); } } - if (updateProgress != null) - updateProgress(100); + manager.Progress(100); if (skip) return null; return channels; @@ -3258,9 +3283,8 @@ namespace Microsoft.Research.Calypso.JobObjectModel /// If true discover the inputs. /// If true discover the outputs. /// If true do not discover the channel sizes (much faster). - /// Delegate used to report progress. - /// Delegate used to report errors. - public bool DiscoverOriginalInfoChannels(bool inputs, bool outputs, bool fast, StatusReporter reporter, Action progress) + /// Communication manager. + public bool DiscoverOriginalInfoChannels(bool inputs, bool outputs, bool fast, CommManager manager) { string filename = string.Format("vertex-{0}-{1}-rerun-originalInfo.txt", this.Number, this.Version); bool success = true; @@ -3270,7 +3294,7 @@ namespace Microsoft.Research.Calypso.JobObjectModel // skip discovery inputs = false; ISharedStreamReader sr = this.WorkDirectory.GetFile(filename).GetStream(); - var channels = this.DiscoverOriginalInfoChannels(sr, null, !inputs, fast, reporter, progress); + var channels = this.DiscoverOriginalInfoChannels(sr, null, !inputs, fast, manager); if (channels == null) { if (inputs) @@ -3281,7 +3305,7 @@ namespace Microsoft.Research.Calypso.JobObjectModel if (this.OutputChannels != null) // skip discovery outputs = false; - channels = this.DiscoverOriginalInfoChannels(sr, this.WorkDirectory.ToString(), !outputs, fast, reporter, progress); + channels = this.DiscoverOriginalInfoChannels(sr, this.WorkDirectory.ToString(), !outputs, fast, manager); if (channels == null) { if (outputs) @@ -3300,28 +3324,27 @@ namespace Microsoft.Research.Calypso.JobObjectModel /// If true discover the inputs. /// If true discover the outputs. /// If true do not discover the channel sizes (much faster). - /// Delegate used to report progress. - /// Delegate used to report errors. + /// Communication manager. // ReSharper disable UnusedParameter.Global - public bool DiscoverScopeChannels(bool inputs, bool outputs, bool fast, StatusReporter reporter, Action progress) + public bool DiscoverScopeChannels(bool inputs, bool outputs, bool fast, CommManager manager) // ReSharper restore UnusedParameter.Global { // find the xml file var files = this.WorkDirectory.GetFilesAndFolders("vcmdStart*.xml").ToList(); if (files.Count != 1) { - reporter("Cannot locate vcmdStart*.xml file", StatusKind.Error); + manager.Status("Cannot locate vcmdStart*.xml file", StatusKind.Error); return false; } ISharedStreamReader sr = files.First().GetStream(); if (sr.Exception != null) { - reporter("Error reading vcmdStart*.xml file" + sr.Exception.Message, StatusKind.Error); + manager.Status("Error reading vcmdStart*.xml file" + sr.Exception.Message, StatusKind.Error); return false; } // ReSharper disable PossibleNullReferenceException - XDocument plan = XDocument.Parse(sr.ReadToEnd()); + XDocument plan = XDocument.Parse(sr.ReadToEnd(manager.Token)); if (inputs && this.InputChannels == null) { var channels = new Dictionary(); @@ -3365,9 +3388,8 @@ namespace Microsoft.Research.Calypso.JobObjectModel /// If true discover the inputs. /// If true discover the outputs. /// If true do not discover the channel sizes (much faster). - /// Delegate used to report progress. - /// Delegate used to report errors. - public bool DiscoverChannels(bool inputs, bool outputs, bool fast, StatusReporter reporter, Action progress) + /// Communication manager. + public bool DiscoverChannels(bool inputs, bool outputs, bool fast, CommManager manager) { // check if the result is already cached if ((this.InputChannels != null || !inputs) && @@ -3395,7 +3417,7 @@ namespace Microsoft.Research.Calypso.JobObjectModel if (wd is UNCFile) { - result = this.DiscoverOriginalInfoChannels(inputs, outputs, fast, reporter, progress); + result = this.DiscoverOriginalInfoChannels(inputs, outputs, fast, manager); } else { @@ -3448,9 +3470,9 @@ namespace Microsoft.Research.Calypso.JobObjectModel internal void Update(string name, string guid) { if (this.State != VertexState.Cancelled && this.State != VertexState.Abandoned) - throw new CalypsoDryadException("Updating a non-cancelled/abandoned vertex"); + throw new DryadException("Updating a non-cancelled/abandoned vertex"); if (this.Name != name) - throw new CalypsoDryadException("Vertex changed name"); + throw new DryadException("Vertex changed name"); this.UniqueID = guid; this.SetState(VertexState.Created); // the stdoutfile is expected to change, so I don't invalidate the cache diff --git a/JobBrowser/JOM/packages.config b/JobBrowser/JOM/packages.config index 6cc3081..bd29d67 100644 --- a/JobBrowser/JOM/packages.config +++ b/JobBrowser/JOM/packages.config @@ -1,13 +1,21 @@ - + + + + - + + + + + + - + \ No newline at end of file diff --git a/JobBrowser/JOM/storage.cs b/JobBrowser/JOM/storage.cs index 8463362..c3ca06c 100644 --- a/JobBrowser/JOM/storage.cs +++ b/JobBrowser/JOM/storage.cs @@ -22,13 +22,13 @@ limitations under the License. #undef USE_DSC #undef USE_TIDYFS -using Microsoft.Research.Calypso.Tools; using System; using System.Collections.Generic; using System.IO; using System.Linq; +using Microsoft.Research.Tools; -namespace Microsoft.Research.Calypso.JobObjectModel +namespace Microsoft.Research.JobObjectModel { /// @@ -197,7 +197,7 @@ namespace Microsoft.Research.Calypso.JobObjectModel { var sourceInputs = plan.GetStageConnections(source, true).ToList(); if (sourceInputs.Count() != 1) - throw new CalypsoDryadException("Unexpected number of inputs for stage " + source.Name); + throw new DryadException("Unexpected number of inputs for stage " + source.Name); source = sourceInputs.First().From; } diff --git a/JobBrowser/JobBrowser.sln b/JobBrowser/JobBrowser.sln index 6134b2b..4f11530 100644 --- a/JobBrowser/JobBrowser.sln +++ b/JobBrowser/JobBrowser.sln @@ -1,8 +1,6 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2013 -VisualStudioVersion = 12.0.30110.0 -MinimumVisualStudioVersion = 10.0.40219.1 +# Visual Studio 2012 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JobBrowser", "JobBrowser\JobBrowser.csproj", "{EDDD2E0B-A52B-4E25-9436-B874017673FF}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tools", "Tools\Tools.csproj", "{20B91AAF-AAD4-47DF-9F1D-494DE6E066F9}" @@ -13,72 +11,26 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UsefulForms", "UsefulForms\ EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Debug|Mixed Platforms = Debug|Mixed Platforms Debug|x64 = Debug|x64 - Debug|x86 = Debug|x86 - Release|Any CPU = Release|Any CPU - Release|Mixed Platforms = Release|Mixed Platforms Release|x64 = Release|x64 - Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {EDDD2E0B-A52B-4E25-9436-B874017673FF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {EDDD2E0B-A52B-4E25-9436-B874017673FF}.Debug|Any CPU.Build.0 = Debug|Any CPU - {EDDD2E0B-A52B-4E25-9436-B874017673FF}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU - {EDDD2E0B-A52B-4E25-9436-B874017673FF}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU {EDDD2E0B-A52B-4E25-9436-B874017673FF}.Debug|x64.ActiveCfg = Debug|x64 {EDDD2E0B-A52B-4E25-9436-B874017673FF}.Debug|x64.Build.0 = Debug|x64 - {EDDD2E0B-A52B-4E25-9436-B874017673FF}.Debug|x86.ActiveCfg = Debug|Any CPU - {EDDD2E0B-A52B-4E25-9436-B874017673FF}.Release|Any CPU.ActiveCfg = Release|Any CPU - {EDDD2E0B-A52B-4E25-9436-B874017673FF}.Release|Any CPU.Build.0 = Release|Any CPU - {EDDD2E0B-A52B-4E25-9436-B874017673FF}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU - {EDDD2E0B-A52B-4E25-9436-B874017673FF}.Release|Mixed Platforms.Build.0 = Release|Any CPU {EDDD2E0B-A52B-4E25-9436-B874017673FF}.Release|x64.ActiveCfg = Release|x64 {EDDD2E0B-A52B-4E25-9436-B874017673FF}.Release|x64.Build.0 = Release|x64 - {EDDD2E0B-A52B-4E25-9436-B874017673FF}.Release|x86.ActiveCfg = Release|Any CPU - {20B91AAF-AAD4-47DF-9F1D-494DE6E066F9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {20B91AAF-AAD4-47DF-9F1D-494DE6E066F9}.Debug|Any CPU.Build.0 = Debug|Any CPU - {20B91AAF-AAD4-47DF-9F1D-494DE6E066F9}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU - {20B91AAF-AAD4-47DF-9F1D-494DE6E066F9}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU {20B91AAF-AAD4-47DF-9F1D-494DE6E066F9}.Debug|x64.ActiveCfg = Debug|x64 {20B91AAF-AAD4-47DF-9F1D-494DE6E066F9}.Debug|x64.Build.0 = Debug|x64 - {20B91AAF-AAD4-47DF-9F1D-494DE6E066F9}.Debug|x86.ActiveCfg = Debug|Any CPU - {20B91AAF-AAD4-47DF-9F1D-494DE6E066F9}.Release|Any CPU.ActiveCfg = Release|Any CPU - {20B91AAF-AAD4-47DF-9F1D-494DE6E066F9}.Release|Any CPU.Build.0 = Release|Any CPU - {20B91AAF-AAD4-47DF-9F1D-494DE6E066F9}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU - {20B91AAF-AAD4-47DF-9F1D-494DE6E066F9}.Release|Mixed Platforms.Build.0 = Release|Any CPU {20B91AAF-AAD4-47DF-9F1D-494DE6E066F9}.Release|x64.ActiveCfg = Release|x64 {20B91AAF-AAD4-47DF-9F1D-494DE6E066F9}.Release|x64.Build.0 = Release|x64 - {20B91AAF-AAD4-47DF-9F1D-494DE6E066F9}.Release|x86.ActiveCfg = Release|Any CPU - {77739535-7FAC-4487-887F-FEBA197E7572}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {77739535-7FAC-4487-887F-FEBA197E7572}.Debug|Any CPU.Build.0 = Debug|Any CPU - {77739535-7FAC-4487-887F-FEBA197E7572}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU - {77739535-7FAC-4487-887F-FEBA197E7572}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU {77739535-7FAC-4487-887F-FEBA197E7572}.Debug|x64.ActiveCfg = Debug|x64 {77739535-7FAC-4487-887F-FEBA197E7572}.Debug|x64.Build.0 = Debug|x64 - {77739535-7FAC-4487-887F-FEBA197E7572}.Debug|x86.ActiveCfg = Debug|Any CPU - {77739535-7FAC-4487-887F-FEBA197E7572}.Release|Any CPU.ActiveCfg = Release|Any CPU - {77739535-7FAC-4487-887F-FEBA197E7572}.Release|Any CPU.Build.0 = Release|Any CPU - {77739535-7FAC-4487-887F-FEBA197E7572}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU - {77739535-7FAC-4487-887F-FEBA197E7572}.Release|Mixed Platforms.Build.0 = Release|Any CPU {77739535-7FAC-4487-887F-FEBA197E7572}.Release|x64.ActiveCfg = Release|x64 {77739535-7FAC-4487-887F-FEBA197E7572}.Release|x64.Build.0 = Release|x64 - {77739535-7FAC-4487-887F-FEBA197E7572}.Release|x86.ActiveCfg = Release|Any CPU - {27635A68-ADFC-4192-9262-B4E6ECDDCE09}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {27635A68-ADFC-4192-9262-B4E6ECDDCE09}.Debug|Any CPU.Build.0 = Debug|Any CPU - {27635A68-ADFC-4192-9262-B4E6ECDDCE09}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU - {27635A68-ADFC-4192-9262-B4E6ECDDCE09}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU {27635A68-ADFC-4192-9262-B4E6ECDDCE09}.Debug|x64.ActiveCfg = Debug|x64 {27635A68-ADFC-4192-9262-B4E6ECDDCE09}.Debug|x64.Build.0 = Debug|x64 - {27635A68-ADFC-4192-9262-B4E6ECDDCE09}.Debug|x86.ActiveCfg = Debug|Any CPU - {27635A68-ADFC-4192-9262-B4E6ECDDCE09}.Release|Any CPU.ActiveCfg = Release|Any CPU - {27635A68-ADFC-4192-9262-B4E6ECDDCE09}.Release|Any CPU.Build.0 = Release|Any CPU - {27635A68-ADFC-4192-9262-B4E6ECDDCE09}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU - {27635A68-ADFC-4192-9262-B4E6ECDDCE09}.Release|Mixed Platforms.Build.0 = Release|Any CPU {27635A68-ADFC-4192-9262-B4E6ECDDCE09}.Release|x64.ActiveCfg = Release|x64 {27635A68-ADFC-4192-9262-B4E6ECDDCE09}.Release|x64.Build.0 = Release|x64 - {27635A68-ADFC-4192-9262-B4E6ECDDCE09}.Release|x86.ActiveCfg = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/JobBrowser/JobBrowser/ClusterBrowser.cs b/JobBrowser/JobBrowser/ClusterBrowser.cs index b21c432..4e95ea6 100644 --- a/JobBrowser/JobBrowser/ClusterBrowser.cs +++ b/JobBrowser/JobBrowser/ClusterBrowser.cs @@ -28,11 +28,11 @@ using System.Linq; using System.Net; using System.Windows.Forms; using System.Diagnostics; -using Microsoft.Research.Calypso.JobObjectModel; -using Microsoft.Research.Calypso.Tools; -using Microsoft.Research.Calypso.UsefulForms; +using Microsoft.Research.JobObjectModel; +using Microsoft.Research.Tools; +using Microsoft.Research.UsefulForms; -namespace Microsoft.Research.Calypso.DryadAnalysis +namespace Microsoft.Research.DryadAnalysis { /// /// Class to browse jobs on cluster, copy, summarize and start visualization. @@ -73,7 +73,6 @@ namespace Microsoft.Research.Calypso.DryadAnalysis private TextWriterTraceListener LogFile; private BackgroundWorkQueue queue; - private BackgroundWorker queueWorker; /// /// Jobs from the cluster. @@ -92,8 +91,8 @@ namespace Microsoft.Research.Calypso.DryadAnalysis this.InitializeComponent(); this.status = new StatusWriter(this.statuslabel, this.statusStrip, this.Status); - this.queueWorker = new BackgroundWorker(); - this.queue = new BackgroundWorkQueue(this.queueWorker); + BackgroundWorker queueWorker = new BackgroundWorker(); + this.queue = new BackgroundWorkQueue(queueWorker, null, null); this.completeJobsList = new List(); this.refreshTimer = new Timer(); @@ -235,7 +234,7 @@ namespace Microsoft.Research.Calypso.DryadAnalysis this.toolStripMenuItem_job.Enabled = true; var item = new BackgroundWorkItem>( - (s, p) => BuildClusterJobList(s, p, this.clusterStatus, this.SelectedVirtualCluster), + m => BuildClusterJobList(m, this.clusterStatus, this.SelectedVirtualCluster), this.JobListRetrieved, "getJobs"); this.Queue(item); @@ -250,6 +249,8 @@ namespace Microsoft.Research.Calypso.DryadAnalysis private void JobListRetrieved(bool cancelled, List jobs) { + if (cancelled) return; + this.filteredDataGridView.DataGridView.ClearSelection(); this.completeJobsList = jobs; this.clusterJobs.SetItems(this.completeJobsList); @@ -274,12 +275,11 @@ namespace Microsoft.Research.Calypso.DryadAnalysis /// Talk to the web server and build the list of clustr jobs; used it to populate the upper panel. /// /// Virtual cluster selected; defined only for Scope clusters. - /// Reports progress. - /// Reports status. + /// Communication manager. /// Cluster to scan. - private static List BuildClusterJobList(StatusReporter reporter, Action progress, ClusterStatus status, string virtualCluster) + private static List BuildClusterJobList(CommManager manager, ClusterStatus status, string virtualCluster) { - return status.GetClusterJobList(virtualCluster, reporter, progress).ToList(); + return status.GetClusterJobList(virtualCluster, manager).ToList(); } /// @@ -311,7 +311,9 @@ namespace Microsoft.Research.Calypso.DryadAnalysis if (js == null) return; - DryadLinqJobInfo job = DryadLinqJobInfo.CreateDryadLinqJobInfo(this.clusterStatus.Config, js, false, this.Status, delegate {}); + // TODO: this should run in the background + CommManager manager = new CommManager(this.Status, delegate { }, new System.Threading.CancellationTokenSource().Token); + DryadLinqJobInfo job = DryadLinqJobInfo.CreateDryadLinqJobInfo(this.clusterStatus.Config, js, false, manager); if (job != null) { JobBrowser browser = new JobBrowser(job); @@ -366,7 +368,9 @@ namespace Microsoft.Research.Calypso.DryadAnalysis IEnumerable ti = this.SelectedJobs(); this.Status("Starting job browser...", StatusKind.LongOp); IEnumerable jobs = ti.Select(t => t.DiscoverDryadLinqJob(this.clusterStatus, this.Status)).ToList(); - IEnumerable detailed = jobs.Select(j => DryadLinqJobInfo.CreateDryadLinqJobInfo(this.clusterStatus.Config, j, false, this.Status, delegate { })); + + CommManager manager = new CommManager(this.Status, delegate { }, new System.Threading.CancellationTokenSource().Token); + IEnumerable detailed = jobs.Select(j => DryadLinqJobInfo.CreateDryadLinqJobInfo(this.clusterStatus.Config, j, false, manager)); foreach (DryadLinqJobInfo j in detailed) { if (j == null) continue; @@ -442,7 +446,7 @@ namespace Microsoft.Research.Calypso.DryadAnalysis IEnumerable jobs = todo.Select(j => j.DiscoverDryadLinqJob(this.clusterStatus, this.Status)).Where(j => j != null); var item = new BackgroundWorkItem( - (s, p) => ClusterWork.CancelJobs(jobs, this.clusterStatus, s, p), + m => ClusterWork.CancelJobs(jobs, this.clusterStatus, m), (c, b) => { }, "cancel"); this.Queue(item); @@ -504,8 +508,11 @@ namespace Microsoft.Research.Calypso.DryadAnalysis this.autoRefreshToolStripMenuItem.Checked = this.formSettings.AutoRefresh; this.AddClusterNameToMenu(""); + this.AddClusterNameToMenu(""); ClusterConfiguration.ReconstructKnownCluster(this.formSettings.KnownClusters); + + int found = 0; IEnumerable clusters = ClusterConfiguration.GetKnownClusterNames(); foreach (string c in clusters) { @@ -515,7 +522,12 @@ namespace Microsoft.Research.Calypso.DryadAnalysis { (config as CacheClusterConfiguration).StartCaching(); } + found++; } + + if (found == 0) + // try to find them by scanning + this.ScanClusters(); } /// @@ -539,6 +551,11 @@ namespace Microsoft.Research.Calypso.DryadAnalysis newItem.Click += this.AddNewCluster; return; } + if (clusterName == "") + { + newItem.Click += this.ScanClusters; + return; + } var selItem = newItem.DropDownItems.Add("Select"); var delItem = newItem.DropDownItems.Add("Delete"); @@ -548,6 +565,31 @@ namespace Microsoft.Research.Calypso.DryadAnalysis editItem.Click += editItem_Click; } + /// + /// Scan the clusters we are subscribed to and add them to the list of known clusters. + /// + /// Unused. + /// Unused. + private void ScanClusters(object sender, EventArgs e) + { + this.ScanClusters(); + } + + /// + /// Scan the clusters we are subscribed to and add them to the list of known clusters. + /// + private void ScanClusters() + { + this.Status("Scanning for known clusters", StatusKind.LongOp); + foreach (var conf in ClusterConfiguration.EnumerateSubscribedClusters()) + { + ClusterConfiguration.AddKnownCluster(conf); + this.AddClusterNameToMenu(conf.Name); + this.Status("Adding cluster " + conf.Name, StatusKind.OK); + } + this.Status("Scan completed", StatusKind.OK); + } + /// /// Edit a cluster. /// @@ -684,7 +726,7 @@ namespace Microsoft.Research.Calypso.DryadAnalysis IEnumerable jobs = todo.Select(j => j.DiscoverDryadLinqJob(this.clusterStatus, this.Status)).Where(j => j != null); var item = new BackgroundWorkItem>( - (s, p) => ClusterWork.DiagnoseJobs(jobs, this.clusterStatus.Config, s, p), + m => ClusterWork.DiagnoseJobs(jobs, this.clusterStatus.Config, m), DiagnosisResult.ShowDiagnosisResult, "cancel"); this.Queue(item); @@ -895,18 +937,18 @@ namespace Microsoft.Research.Calypso.DryadAnalysis /// /// Jobs to cancel. /// Cluster where the jobs are running. - /// Delegate used to report errors. /// True if all cancellations succeed. - /// Delegate used to report progress. + /// Communicatoni manager. // ReSharper disable once UnusedParameter.Global - public static bool CancelJobs(IEnumerable jobs, ClusterStatus cluster, StatusReporter statusReporter, Action updateProgress) + public static bool CancelJobs(IEnumerable jobs, ClusterStatus cluster, CommManager manager) { bool done = true; foreach (DryadLinqJobSummary job in jobs) { + manager.Token.ThrowIfCancellationRequested(); if (job.Status != ClusterJobInformation.ClusterJobStatus.Running) { - statusReporter("Job " + job.Name + " does not appear to be running; will still try to cancel", StatusKind.Error); + manager.Status("Job " + job.Name + " does not appear to be running; will still try to cancel", StatusKind.Error); } bool success; @@ -923,9 +965,9 @@ namespace Microsoft.Research.Calypso.DryadAnalysis } if (success) - statusReporter("Job " + job.Name + " cancelled", StatusKind.OK); + manager.Status("Job " + job.Name + " cancelled", StatusKind.OK); else - statusReporter("Cancellation of " + job.Name + " failed " + reason, StatusKind.Error); + manager.Status("Cancellation of " + job.Name + " failed " + reason, StatusKind.Error); done &= success; } return done; @@ -936,9 +978,8 @@ namespace Microsoft.Research.Calypso.DryadAnalysis /// /// Jobs to diagnose. /// Cluster configuration. - /// Delegate used to report errors. - /// Delegate used to report progress. - public static List DiagnoseJobs(IEnumerable jobs, ClusterConfiguration config, StatusReporter reporter, Action updateProgress) + /// Communicatino manager. + public static List DiagnoseJobs(IEnumerable jobs, ClusterConfiguration config, CommManager manager) { var dryadLinqJobSummaries = jobs as DryadLinqJobSummary[] ?? jobs.ToArray(); int jobCount = dryadLinqJobSummaries.Count(); @@ -949,15 +990,16 @@ namespace Microsoft.Research.Calypso.DryadAnalysis { if (summary == null) continue; - JobFailureDiagnosis diagnosis = JobFailureDiagnosis.CreateJobFailureDiagnosis(config, summary, reporter, updateProgress); - reporter("Diagnosing " + summary.ShortName(), StatusKind.LongOp); + manager.Token.ThrowIfCancellationRequested(); + JobFailureDiagnosis diagnosis = JobFailureDiagnosis.CreateJobFailureDiagnosis(config, summary, manager); + manager.Status("Diagnosing " + summary.ShortName(), StatusKind.LongOp); DiagnosisLog log = diagnosis.Diagnose(); result.Add(log); done++; - updateProgress(done * 100 / jobCount); + manager.Progress(done * 100 / jobCount); } - reporter("Diagnosis complete", StatusKind.OK); + manager.Status("Diagnosis complete", StatusKind.OK); return result; } } diff --git a/JobBrowser/JobBrowser/ClusterBrowser.designer.cs b/JobBrowser/JobBrowser/ClusterBrowser.designer.cs index e2b4681..7347464 100644 --- a/JobBrowser/JobBrowser/ClusterBrowser.designer.cs +++ b/JobBrowser/JobBrowser/ClusterBrowser.designer.cs @@ -18,9 +18,9 @@ See the Apache Version 2.0 License for specific language governing permissions a limitations under the License. */ -using Microsoft.Research.Calypso.Tools; +using Microsoft.Research.Tools; -namespace Microsoft.Research.Calypso.DryadAnalysis +namespace Microsoft.Research.DryadAnalysis { partial class ClusterBrowser { @@ -56,10 +56,9 @@ namespace Microsoft.Research.Calypso.DryadAnalysis this.openInJobBrowserToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.diagnoseToolStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem(); this.terminateToolStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem(); + this.cancelToolStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem(); this.statusStrip = new System.Windows.Forms.StatusStrip(); this.statuslabel = new System.Windows.Forms.ToolStripStatusLabel(); - this.toolStripStatusLabel_backgroundWork = new System.Windows.Forms.ToolStripStatusLabel(); - this.toolStripStatusLabel_currentWork = new System.Windows.Forms.ToolStripStatusLabel(); this.toolStripProgressBar = new System.Windows.Forms.ToolStripProgressBar(); this.flowLayoutPanel_header = new System.Windows.Forms.FlowLayoutPanel(); this.label_vc = new System.Windows.Forms.Label(); @@ -78,7 +77,7 @@ namespace Microsoft.Research.Calypso.DryadAnalysis this.logFileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.autoRefreshToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.clusterToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.filteredDataGridView = new Microsoft.Research.Calypso.Tools.FilteredDataGridView(); + this.filteredDataGridView = new FilteredDataGridView(); this.contextMenuStrip_job.SuspendLayout(); this.statusStrip.SuspendLayout(); this.flowLayoutPanel_header.SuspendLayout(); @@ -92,7 +91,7 @@ namespace Microsoft.Research.Calypso.DryadAnalysis this.diagnoseToolStripMenuItem1, this.terminateToolStripMenuItem1}); this.contextMenuStrip_job.Name = "contextMenuStrip_job"; - this.contextMenuStrip_job.Size = new System.Drawing.Size(182, 92); + this.contextMenuStrip_job.Size = new System.Drawing.Size(182, 70); // // openInJobBrowserToolStripMenuItem // @@ -114,15 +113,19 @@ namespace Microsoft.Research.Calypso.DryadAnalysis this.terminateToolStripMenuItem1.Name = "terminateToolStripMenuItem1"; this.terminateToolStripMenuItem1.Size = new System.Drawing.Size(181, 22); this.terminateToolStripMenuItem1.Text = "Terminate"; - this.terminateToolStripMenuItem1.Visible = false; this.terminateToolStripMenuItem1.Click += new System.EventHandler(this.terminateToolStripMenuItem_Click); // + // cancelToolStripMenuItem1 + // + this.cancelToolStripMenuItem1.Name = "cancelToolStripMenuItem1"; + this.cancelToolStripMenuItem1.Size = new System.Drawing.Size(186, 22); + this.cancelToolStripMenuItem1.Text = "Cancel current work"; + this.cancelToolStripMenuItem1.Click += new System.EventHandler(this.cancelToolStripMenuItem_Click); + // // statusStrip // this.statusStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { this.statuslabel, - this.toolStripStatusLabel_backgroundWork, - this.toolStripStatusLabel_currentWork, this.toolStripProgressBar}); this.statusStrip.Location = new System.Drawing.Point(0, 431); this.statusStrip.Name = "statusStrip"; @@ -134,32 +137,16 @@ namespace Microsoft.Research.Calypso.DryadAnalysis // this.statuslabel.Name = "statuslabel"; this.statuslabel.Overflow = System.Windows.Forms.ToolStripItemOverflow.Never; - this.statuslabel.Size = new System.Drawing.Size(833, 17); + this.statuslabel.Size = new System.Drawing.Size(1026, 17); this.statuslabel.Spring = true; this.statuslabel.Text = "Status displayed here"; this.statuslabel.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; // - // toolStripStatusLabel_backgroundWork - // - this.toolStripStatusLabel_backgroundWork.BorderStyle = System.Windows.Forms.Border3DStyle.SunkenOuter; - this.toolStripStatusLabel_backgroundWork.Name = "toolStripStatusLabel_backgroundWork"; - this.toolStripStatusLabel_backgroundWork.Overflow = System.Windows.Forms.ToolStripItemOverflow.Never; - this.toolStripStatusLabel_backgroundWork.Size = new System.Drawing.Size(109, 17); - this.toolStripStatusLabel_backgroundWork.Text = "0 pending activities"; - // - // toolStripStatusLabel_currentWork - // - this.toolStripStatusLabel_currentWork.BorderStyle = System.Windows.Forms.Border3DStyle.SunkenOuter; - this.toolStripStatusLabel_currentWork.Name = "toolStripStatusLabel_currentWork"; - this.toolStripStatusLabel_currentWork.Overflow = System.Windows.Forms.ToolStripItemOverflow.Never; - this.toolStripStatusLabel_currentWork.Size = new System.Drawing.Size(84, 17); - this.toolStripStatusLabel_currentWork.Text = "Doing nothing"; - // // toolStripProgressBar // this.toolStripProgressBar.Name = "toolStripProgressBar"; this.toolStripProgressBar.Size = new System.Drawing.Size(100, 16); - // + // // flowLayoutPanel_header // this.flowLayoutPanel_header.AutoSize = true; @@ -219,6 +206,7 @@ namespace Microsoft.Research.Calypso.DryadAnalysis // jobToolStripMenuItem_file // this.jobToolStripMenuItem_file.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.cancelToolStripMenuItem1, this.newWindowToolStripMenuItem, this.refreshToolStripMenuItem1, this.exitToolStripMenuItem1}); @@ -264,7 +252,6 @@ namespace Microsoft.Research.Calypso.DryadAnalysis this.toolStripMenuItem_job.Size = new System.Drawing.Size(37, 20); this.toolStripMenuItem_job.Text = "&Job"; this.toolStripMenuItem_job.ToolTipText = "View job informaotion in detail."; - this.toolStripMenuItem_job.Visible = false; // // jobBrowserToolStripMenuItem // @@ -279,6 +266,7 @@ namespace Microsoft.Research.Calypso.DryadAnalysis this.diagnoseToolStripMenuItem.Size = new System.Drawing.Size(163, 22); this.diagnoseToolStripMenuItem.Text = "Diagnose"; this.diagnoseToolStripMenuItem.ToolTipText = "Attempt to diagnose job failures."; + this.diagnoseToolStripMenuItem.Visible = false; this.diagnoseToolStripMenuItem.Click += new System.EventHandler(this.diagnoseToolStripMenuItem_Click); // // terminateToolStripMenuItem @@ -295,6 +283,7 @@ namespace Microsoft.Research.Calypso.DryadAnalysis this.openFromURLToolStripMenuItem.Size = new System.Drawing.Size(163, 22); this.openFromURLToolStripMenuItem.Text = "Open job URL..."; this.openFromURLToolStripMenuItem.ToolTipText = "Open the job given a URL."; + this.openFromURLToolStripMenuItem.Visible = false; this.openFromURLToolStripMenuItem.Click += new System.EventHandler(this.openFromURLToolStripMenuItem_Click); // // settingsToolStripMenuItem @@ -309,7 +298,7 @@ namespace Microsoft.Research.Calypso.DryadAnalysis // logFileToolStripMenuItem // this.logFileToolStripMenuItem.Name = "logFileToolStripMenuItem"; - this.logFileToolStripMenuItem.Size = new System.Drawing.Size(139, 22); + this.logFileToolStripMenuItem.Size = new System.Drawing.Size(152, 22); this.logFileToolStripMenuItem.Text = "Log file"; this.logFileToolStripMenuItem.ToolTipText = "When enabled logs errors in the selected file."; this.logFileToolStripMenuItem.Click += new System.EventHandler(this.logFileToolStripMenuItem_Click); @@ -317,7 +306,7 @@ namespace Microsoft.Research.Calypso.DryadAnalysis // autoRefreshToolStripMenuItem // this.autoRefreshToolStripMenuItem.Name = "autoRefreshToolStripMenuItem"; - this.autoRefreshToolStripMenuItem.Size = new System.Drawing.Size(139, 22); + this.autoRefreshToolStripMenuItem.Size = new System.Drawing.Size(152, 22); this.autoRefreshToolStripMenuItem.Text = "Auto refresh"; this.autoRefreshToolStripMenuItem.Click += new System.EventHandler(this.autoRefreshToolStripMenuItem_Click); // @@ -326,15 +315,15 @@ namespace Microsoft.Research.Calypso.DryadAnalysis this.clusterToolStripMenuItem.Name = "clusterToolStripMenuItem"; this.clusterToolStripMenuItem.Size = new System.Drawing.Size(56, 20); this.clusterToolStripMenuItem.Text = "Cluster"; - + // // filteredDataGridView // this.filteredDataGridView.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; this.filteredDataGridView.ContextMenuStrip = this.contextMenuStrip_job; this.filteredDataGridView.Dock = System.Windows.Forms.DockStyle.Fill; - this.filteredDataGridView.Location = new System.Drawing.Point(0, 56); + this.filteredDataGridView.Location = new System.Drawing.Point(0, 24); this.filteredDataGridView.Name = "filteredDataGridView"; - this.filteredDataGridView.Size = new System.Drawing.Size(1143, 375); + this.filteredDataGridView.Size = new System.Drawing.Size(1143, 429); this.filteredDataGridView.TabIndex = 15; this.filteredDataGridView.CellFormatting += new System.Windows.Forms.DataGridViewCellFormattingEventHandler(this.filteredDataGridView_CellFormatting); this.filteredDataGridView.CellMouseDoubleClick += new System.Windows.Forms.DataGridViewCellMouseEventHandler(this.filteredDataGridView_CellMouseDoubleClick); @@ -365,6 +354,11 @@ namespace Microsoft.Research.Calypso.DryadAnalysis } + private void cancelToolStripMenuItem_Click(object sender, System.EventArgs e) + { + this.queue.CancelCurrentWork(); + } + #endregion private System.Windows.Forms.StatusStrip statusStrip; @@ -378,13 +372,12 @@ namespace Microsoft.Research.Calypso.DryadAnalysis private System.Windows.Forms.ToolStripMenuItem refreshToolStripMenuItem1; private System.Windows.Forms.ToolStripMenuItem openFromURLToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem terminateToolStripMenuItem; - private System.Windows.Forms.ToolStripStatusLabel toolStripStatusLabel_backgroundWork; - private System.Windows.Forms.ToolStripStatusLabel toolStripStatusLabel_currentWork; private System.Windows.Forms.ToolStripMenuItem diagnoseToolStripMenuItem; private System.Windows.Forms.ContextMenuStrip contextMenuStrip_job; private System.Windows.Forms.ToolStripMenuItem openInJobBrowserToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem diagnoseToolStripMenuItem1; private System.Windows.Forms.ToolStripMenuItem terminateToolStripMenuItem1; + private System.Windows.Forms.ToolStripMenuItem cancelToolStripMenuItem1; private System.Windows.Forms.ToolStripProgressBar toolStripProgressBar; private FilteredDataGridView filteredDataGridView; private System.Windows.Forms.ToolStripMenuItem newWindowToolStripMenuItem; diff --git a/JobBrowser/JobBrowser/ClusterBrowser.resx b/JobBrowser/JobBrowser/ClusterBrowser.resx index a3018f8..9ee9554 100644 --- a/JobBrowser/JobBrowser/ClusterBrowser.resx +++ b/JobBrowser/JobBrowser/ClusterBrowser.resx @@ -123,9 +123,6 @@ 17, 17 - - 253, 17 - 419, 16 diff --git a/JobBrowser/JobBrowser/ClusterConfigEditor.Designer.cs b/JobBrowser/JobBrowser/ClusterConfigEditor.Designer.cs index ac163ec..c05883e 100644 --- a/JobBrowser/JobBrowser/ClusterConfigEditor.Designer.cs +++ b/JobBrowser/JobBrowser/ClusterConfigEditor.Designer.cs @@ -18,7 +18,7 @@ See the Apache Version 2.0 License for specific language governing permissions a limitations under the License. */ -namespace Microsoft.Research.Calypso.JobObjectModel +namespace Microsoft.Research.JobObjectModel { partial class ClusterConfigEditor { diff --git a/JobBrowser/JobBrowser/ClusterConfigEditor.cs b/JobBrowser/JobBrowser/ClusterConfigEditor.cs index 7b50b7b..321daac 100644 --- a/JobBrowser/JobBrowser/ClusterConfigEditor.cs +++ b/JobBrowser/JobBrowser/ClusterConfigEditor.cs @@ -23,9 +23,9 @@ using System; using System.Collections.Generic; using System.Drawing; using System.Windows.Forms; -using Microsoft.Research.Calypso.Tools; +using Microsoft.Research.Tools; -namespace Microsoft.Research.Calypso.JobObjectModel +namespace Microsoft.Research.JobObjectModel { /// /// Editor for cluster configuration. diff --git a/JobBrowser/JobBrowser/Diagnosis.cs b/JobBrowser/JobBrowser/Diagnosis.cs index 4a47117..23d7b86 100644 --- a/JobBrowser/JobBrowser/Diagnosis.cs +++ b/JobBrowser/JobBrowser/Diagnosis.cs @@ -19,15 +19,15 @@ limitations under the License. */ -using Microsoft.Research.Calypso.JobObjectModel; -using Microsoft.Research.Calypso.Tools; using System; using System.IO; using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; +using Microsoft.Research.JobObjectModel; +using Microsoft.Research.Tools; -namespace Microsoft.Research.Calypso.DryadAnalysis +namespace Microsoft.Research.DryadAnalysis { /// /// The result of a decision (ternary booleans?) @@ -236,30 +236,24 @@ namespace Microsoft.Research.Calypso.DryadAnalysis /// public DryadLinqJobInfo Job { get; protected set; } /// - /// Delegate used to report erorrs. + /// Communication manager. /// - public StatusReporter Reporter { get; protected set; } + public CommManager Manager { get; protected set; } /// /// Plan of the job. /// public DryadJobStaticPlan StaticPlan { get; protected set; } /// - /// Delegate used to report progress. - /// - public Action ProgressReporter { get; protected set; } - /// /// Create a FailureDiagnosis object. /// /// Job being diagnosed. /// Static plan of the job. - /// Delegate used to report errors. - /// Delegate used to report progress. - protected FailureDiagnosis(DryadLinqJobInfo job, DryadJobStaticPlan plan, StatusReporter reporter, Action progressReporter) + /// Communication manager. + protected FailureDiagnosis(DryadLinqJobInfo job, DryadJobStaticPlan plan, CommManager manager) { this.Job = job; this.StaticPlan = plan; - this.Reporter = reporter; - this.ProgressReporter = progressReporter; + this.Manager = manager; this.Summary = job.Summary; this.cluster = job.ClusterConfiguration; } @@ -267,19 +261,18 @@ namespace Microsoft.Research.Calypso.DryadAnalysis /// /// Try to find the job information from cluster and summary. /// - /// Delegate used to report status. - /// Delegate used to report progress. - protected void FindJobInfo(StatusReporter status, Action progress) + /// Communication manager. + protected void FindJobInfo(CommManager manager) { - DryadLinqJobInfo jobinfo = DryadLinqJobInfo.CreateDryadLinqJobInfo(this.cluster, this.Summary, true, status, progress); + DryadLinqJobInfo jobinfo = DryadLinqJobInfo.CreateDryadLinqJobInfo(this.cluster, this.Summary, true, manager); if (jobinfo == null) { - status("Cannot collect information for " + Summary.ShortName() + " to diagnose", StatusKind.Error); + manager.Status("Cannot collect information for " + Summary.ShortName() + " to diagnose", StatusKind.Error); return; } this.Job = jobinfo; - this.StaticPlan = JobObjectModel.DryadJobStaticPlan.CreatePlan(jobinfo, status); + this.StaticPlan = JobObjectModel.DryadJobStaticPlan.CreatePlan(jobinfo, manager); } /// @@ -287,15 +280,13 @@ namespace Microsoft.Research.Calypso.DryadAnalysis /// /// Cluster where job resides. /// Job summary. - /// Delegate used to report errors. - /// Delegate used to report progress. - protected FailureDiagnosis(ClusterConfiguration config, DryadLinqJobSummary summary, StatusReporter reporter, Action progressReporter) + /// Communication manager. + protected FailureDiagnosis(ClusterConfiguration config, DryadLinqJobSummary summary, CommManager manager) { this.cluster = config; this.Summary = summary; - this.Reporter = reporter; - this.ProgressReporter = progressReporter; - this.FindJobInfo(reporter, progressReporter); + this.Manager = manager; + this.FindJobInfo(manager); } /// @@ -327,11 +318,10 @@ namespace Microsoft.Research.Calypso.DryadAnalysis /// /// Vertex to diagnose. /// Job containing the vertex. - /// Delegate used to report progress. - /// Delegate used to report status. /// Plan of the executed job. - protected VertexFailureDiagnosis(DryadLinqJobInfo job, DryadJobStaticPlan plan, ExecutedVertexInstance vertex, StatusReporter reporter, Action progressReporter) - : base(job, plan, reporter, progressReporter) + /// Communication manager. + protected VertexFailureDiagnosis(DryadLinqJobInfo job, DryadJobStaticPlan plan, ExecutedVertexInstance vertex, CommManager manager) + : base(job, plan, manager) { this.Job = job; this.Vertex = vertex; @@ -344,15 +334,13 @@ namespace Microsoft.Research.Calypso.DryadAnalysis /// /// Vertex to diagnose. /// Job containing the vertex. - /// Delegate used to report progress. - /// Delegate used to report status. + /// Communication manager. /// A subclass of VertexFailureDiagnosis. /// Plan of the executed job. public static VertexFailureDiagnosis CreateVertexFailureDiagnosis(DryadLinqJobInfo job, DryadJobStaticPlan plan, - ExecutedVertexInstance vertex, - StatusReporter reporter, - Action progressReporter) + ExecutedVertexInstance vertex, + CommManager manager) { ClusterConfiguration config = job.ClusterConfiguration; if (config is CacheClusterConfiguration) @@ -381,7 +369,7 @@ namespace Microsoft.Research.Calypso.DryadAnalysis DiagnosisLog log = new DiagnosisLog(this.Job, this.Summary); log.AddMessage(new DiagnosisMessage(DiagnosisMessage.Importance.Final, "Diagnostic for " + this.VertexName, "Vertex state is " + this.Vertex.State)); this.Diagnose(log); - this.Reporter("Vertex diagnosis complete", StatusKind.OK); + this.Manager.Status("Vertex diagnosis complete", StatusKind.OK); return log; } @@ -409,7 +397,8 @@ namespace Microsoft.Research.Calypso.DryadAnalysis /// Detect whether the vertex had problems reading a particular channel. /// /// The channel that cannot be read, or null if that's not the problem. - public virtual ChannelEndpointDescription ChannelReadFailure() + /// Communication manager. + public virtual ChannelEndpointDescription ChannelReadFailure(CommManager manager) { List stack = this.StackTrace().ToList(); if (stack.Count == 0) @@ -424,7 +413,7 @@ namespace Microsoft.Research.Calypso.DryadAnalysis bool success = int.TryParse(m.Groups[3].Value, out channelNo); if (!success) return null; - this.Vertex.DiscoverChannels(true, false, true, this.Reporter, null); + this.Vertex.DiscoverChannels(true, false, true, manager); var channels = this.Vertex.InputChannels; if (channels == null) return null; @@ -604,11 +593,10 @@ namespace Microsoft.Research.Calypso.DryadAnalysis /// Create a class to diagnose the problems of a job. /// /// Job to diagnose. - /// Delegate used to report progress. - /// Delegate used to report status. /// Plan of the diagnosed job. - protected JobFailureDiagnosis(DryadLinqJobInfo job, DryadJobStaticPlan plan, StatusReporter status, Action progress) - : base(job, plan, status, progress) + /// Communication manager. + protected JobFailureDiagnosis(DryadLinqJobInfo job, DryadJobStaticPlan plan, CommManager manager) + : base(job, plan, manager) { this.diagnosisLog = new DiagnosisLog(job, job.Summary); this.jobManager = this.Job.ManagerVertex; @@ -617,12 +605,11 @@ namespace Microsoft.Research.Calypso.DryadAnalysis /// /// Create a class to diagnose the problems of a job. /// - /// Delegate used to report progress. - /// Delegate used to report status. /// Cluster where job resides. + /// Communication manager. /// Job summary. - protected JobFailureDiagnosis(ClusterConfiguration config, DryadLinqJobSummary summary, StatusReporter status, Action progress) - : base(config, summary, status, progress) + protected JobFailureDiagnosis(ClusterConfiguration config, DryadLinqJobSummary summary, CommManager manager) + : base(config, summary, manager) { this.diagnosisLog = new DiagnosisLog(this.Job, summary); if (this.Job != null) @@ -825,11 +812,10 @@ namespace Microsoft.Research.Calypso.DryadAnalysis /// Create a suitable Job Failure diagnosis object for the job being analyzed. /// /// Job to diagnose. - /// Delegate used to report errors. - /// Delegate used to report progress. + /// Communication manager. /// A subclass of JobFailureDiagnosis with the type appropriate for the job. /// Plan of the job being diagnosed. - public static JobFailureDiagnosis CreateJobFailureDiagnosis(DryadLinqJobInfo job, DryadJobStaticPlan plan, StatusReporter status, Action progress) + public static JobFailureDiagnosis CreateJobFailureDiagnosis(DryadLinqJobInfo job, DryadJobStaticPlan plan, CommManager manager) { ClusterConfiguration config = job.ClusterConfiguration; if (config is CacheClusterConfiguration) @@ -843,11 +829,10 @@ namespace Microsoft.Research.Calypso.DryadAnalysis /// Create a suitable Job Failure diagnosis object for the job being analyzed. /// /// Job to diagnose. - /// Delegate used to report errors. - /// Delegate used to report progress. /// Cluster where job resides. + /// Communication manager. /// A subclass of JobFailureDiagnosis with the type appropriate for the job. - public static JobFailureDiagnosis CreateJobFailureDiagnosis(ClusterConfiguration config, DryadLinqJobSummary summary, StatusReporter status, Action progress) + public static JobFailureDiagnosis CreateJobFailureDiagnosis(ClusterConfiguration config, DryadLinqJobSummary summary, CommManager manager) { if (config is CacheClusterConfiguration) config = (config as CacheClusterConfiguration).ActualConfig(summary); @@ -860,7 +845,8 @@ namespace Microsoft.Research.Calypso.DryadAnalysis /// This is incomplete: e.g., it does not work for tidyfs streams. /// /// Yes if there were correlated failures. - protected Decision LookForCorrelatedReadFailures() + /// Communication manager. + protected Decision LookForCorrelatedReadFailures(CommManager manager) { // if we have more than this many failures we start to worry const int maxFailures = 5; @@ -876,13 +862,13 @@ namespace Microsoft.Research.Calypso.DryadAnalysis int verticesDone = 0; foreach (ExecutedVertexInstance v in failures) { - var crf = VertexFailureDiagnosis.CreateVertexFailureDiagnosis(this.Job, this.StaticPlan, v, null, null).ChannelReadFailure(); + var crf = VertexFailureDiagnosis.CreateVertexFailureDiagnosis(this.Job, this.StaticPlan, v, manager).ChannelReadFailure(manager); if (crf != null) { channelsFailed.Add(crf); } verticesDone++; - this.ProgressReporter(verticesDone * 100 / totalFailures); + manager.Progress(verticesDone * 100 / totalFailures); } if (channelsFailed.Count() < maxFailures) return Decision.No; diff --git a/JobBrowser/JobBrowser/DiagnosisResult.Designer.cs b/JobBrowser/JobBrowser/DiagnosisResult.Designer.cs index 09ce3b2..c7de24a 100644 --- a/JobBrowser/JobBrowser/DiagnosisResult.Designer.cs +++ b/JobBrowser/JobBrowser/DiagnosisResult.Designer.cs @@ -18,7 +18,7 @@ See the Apache Version 2.0 License for specific language governing permissions a limitations under the License. */ -namespace Microsoft.Research.Calypso.DryadAnalysis +namespace Microsoft.Research.DryadAnalysis { using System; diff --git a/JobBrowser/JobBrowser/DiagnosisResult.cs b/JobBrowser/JobBrowser/DiagnosisResult.cs index 5f813eb..6f0650f 100644 --- a/JobBrowser/JobBrowser/DiagnosisResult.cs +++ b/JobBrowser/JobBrowser/DiagnosisResult.cs @@ -20,11 +20,11 @@ limitations under the License. */ using System.Collections.Generic; -using Microsoft.Research.Calypso.JobObjectModel; using System; using System.Windows.Forms; +using Microsoft.Research.JobObjectModel; -namespace Microsoft.Research.Calypso.DryadAnalysis +namespace Microsoft.Research.DryadAnalysis { /// /// Display the diagnosis results. diff --git a/JobBrowser/JobBrowser/DryadJobMain.cs b/JobBrowser/JobBrowser/DryadJobMain.cs index dfd16e2..9b307ce 100644 --- a/JobBrowser/JobBrowser/DryadJobMain.cs +++ b/JobBrowser/JobBrowser/DryadJobMain.cs @@ -23,7 +23,7 @@ using System; using System.Diagnostics; using System.Windows.Forms; -namespace Microsoft.Research.Calypso.DryadAnalysis +namespace Microsoft.Research.DryadAnalysis { static class Program { diff --git a/JobBrowser/JobBrowser/JobBrowser.Designer.cs b/JobBrowser/JobBrowser/JobBrowser.Designer.cs index be4a8f4..bc179ba 100644 --- a/JobBrowser/JobBrowser/JobBrowser.Designer.cs +++ b/JobBrowser/JobBrowser/JobBrowser.Designer.cs @@ -18,7 +18,7 @@ See the Apache Version 2.0 License for specific language governing permissions a limitations under the License. */ -namespace Microsoft.Research.Calypso.DryadAnalysis +namespace Microsoft.Research.DryadAnalysis { partial class JobBrowser { @@ -65,13 +65,13 @@ namespace Microsoft.Research.Calypso.DryadAnalysis this.menu = new System.Windows.Forms.MenuStrip(); this.jobToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.refreshToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.collectDataToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.hideCancelledVerticesToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.exportToCSVToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.toolStripMenuItem_terminate = new System.Windows.Forms.ToolStripMenuItem(); this.packageCachedFilesToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.diagnoseToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.closeToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.cancelCurrentWorkToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.stageToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.cacheLogsForAllVerticesToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.vertexToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); @@ -80,7 +80,6 @@ namespace Microsoft.Research.Calypso.DryadAnalysis this.profileLocallyCPUSamplingToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.profileLocallyMemorySamplingToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.jMStdoutMentionsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.jMLogsMentionsToolStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem(); this.diagnoseToolStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem(); this.viewToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.loadFileInEditorToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); @@ -124,7 +123,6 @@ namespace Microsoft.Research.Calypso.DryadAnalysis this.menuItem_stageVertexProfileLocallyCPUSampling = new System.Windows.Forms.ToolStripMenuItem(); this.menuItem_stageVertexProfileLocallyMemorySampling = new System.Windows.Forms.ToolStripMenuItem(); this.jMStdoutLinesToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.jMLogsMentionsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.diagnoseToolStripMenuItem2 = new System.Windows.Forms.ToolStripMenuItem(); this.splitContainer_vertexData = new System.Windows.Forms.SplitContainer(); this.dataGridView_vertexHeader = new System.Windows.Forms.DataGridView(); @@ -147,7 +145,6 @@ namespace Microsoft.Research.Calypso.DryadAnalysis this.toolStripStatusLabel_currentWork = new System.Windows.Forms.ToolStripStatusLabel(); this.toolStripStatusLabel_backgroundWork = new System.Windows.Forms.ToolStripStatusLabel(); this.toolStripProgressBar = new System.Windows.Forms.ToolStripProgressBar(); - this.backgroundWorker = new System.ComponentModel.BackgroundWorker(); this.menu.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.splitContainer_jobAndRest)).BeginInit(); this.splitContainer_jobAndRest.Panel1.SuspendLayout(); @@ -204,13 +201,13 @@ namespace Microsoft.Research.Calypso.DryadAnalysis // this.jobToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.refreshToolStripMenuItem, - this.collectDataToolStripMenuItem, this.hideCancelledVerticesToolStripMenuItem, this.exportToCSVToolStripMenuItem, this.toolStripMenuItem_terminate, this.packageCachedFilesToolStripMenuItem, this.diagnoseToolStripMenuItem, - this.closeToolStripMenuItem}); + this.closeToolStripMenuItem, + this.cancelCurrentWorkToolStripMenuItem}); this.jobToolStripMenuItem.Name = "jobToolStripMenuItem"; this.jobToolStripMenuItem.Size = new System.Drawing.Size(37, 20); this.jobToolStripMenuItem.Text = "Job"; @@ -248,7 +245,6 @@ namespace Microsoft.Research.Calypso.DryadAnalysis this.toolStripMenuItem_terminate.Size = new System.Drawing.Size(195, 22); this.toolStripMenuItem_terminate.Text = "Terminate job"; this.toolStripMenuItem_terminate.ToolTipText = "Requests the cluster to terminate the job execution."; - this.toolStripMenuItem_terminate.Visible = false; this.toolStripMenuItem_terminate.Click += new System.EventHandler(this.toolStripMenuItem_terminate_Click); // // packageCachedFilesToolStripMenuItem @@ -279,6 +275,13 @@ namespace Microsoft.Research.Calypso.DryadAnalysis this.closeToolStripMenuItem.ToolTipText = "Save the settings and close the window."; this.closeToolStripMenuItem.Click += new System.EventHandler(this.closeToolStripMenuItem_Click); // + // cancelCurrentWorkToolStripMenuItem + // + this.cancelCurrentWorkToolStripMenuItem.Name = "cancelCurrentWorkToolStripMenuItem"; + this.cancelCurrentWorkToolStripMenuItem.Size = new System.Drawing.Size(195, 22); + this.cancelCurrentWorkToolStripMenuItem.Text = "Cancel current work"; + this.cancelCurrentWorkToolStripMenuItem.Click += new System.EventHandler(this.cancelCurrentWorkToolStripMenuItem_Click); + // // stageToolStripMenuItem // this.stageToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { @@ -304,7 +307,6 @@ namespace Microsoft.Research.Calypso.DryadAnalysis this.profileLocallyCPUSamplingToolStripMenuItem, this.profileLocallyMemorySamplingToolStripMenuItem, this.jMStdoutMentionsToolStripMenuItem, - this.jMLogsMentionsToolStripMenuItem1, this.diagnoseToolStripMenuItem1}); this.vertexToolStripMenuItem.Enabled = false; this.vertexToolStripMenuItem.Name = "vertexToolStripMenuItem"; @@ -908,10 +910,9 @@ namespace Microsoft.Research.Calypso.DryadAnalysis this.menuItem_stageVertexProfileLocallyCPUSampling, this.menuItem_stageVertexProfileLocallyMemorySampling, this.jMStdoutLinesToolStripMenuItem, - this.jMLogsMentionsToolStripMenuItem, this.diagnoseToolStripMenuItem2}); this.contextMenu_stageVertex.Name = "vertexContextMenuStrip"; - this.contextMenu_stageVertex.Size = new System.Drawing.Size(256, 158); + this.contextMenu_stageVertex.Size = new System.Drawing.Size(256, 136); // // menuItem_stageVertexLocalDebugManaged // @@ -1252,11 +1253,6 @@ namespace Microsoft.Research.Calypso.DryadAnalysis this.toolStripProgressBar.Name = "toolStripProgressBar"; this.toolStripProgressBar.Size = new System.Drawing.Size(100, 16); // - // backgroundWorker - // - this.backgroundWorker.DoWork += new System.ComponentModel.DoWorkEventHandler(this.backgroundWorker_DoWork); - this.backgroundWorker.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.backgroundWorker_RunWorkerCompleted); - // // JobBrowser // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); @@ -1352,7 +1348,6 @@ namespace Microsoft.Research.Calypso.DryadAnalysis private System.Windows.Forms.TextBox textBox_stageCode; private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel3; private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel4; - private System.Windows.Forms.ToolStripMenuItem collectDataToolStripMenuItem; private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel5; private System.Windows.Forms.Label label1; private System.Windows.Forms.TextBox textBox_find; @@ -1362,7 +1357,6 @@ namespace Microsoft.Research.Calypso.DryadAnalysis private System.Windows.Forms.Label label_matches; private System.Windows.Forms.CheckBox checkBox_refresh; private System.Windows.Forms.Button button_filter; - private System.ComponentModel.BackgroundWorker backgroundWorker; private System.Windows.Forms.ComboBox comboBox_vertexInformation; private System.Windows.Forms.Label label_comboVertex; private System.Windows.Forms.ToolStripMenuItem viewToolStripMenuItem; @@ -1380,9 +1374,7 @@ namespace Microsoft.Research.Calypso.DryadAnalysis private System.Windows.Forms.ToolStripMenuItem menuItem_stageVertexLocalDebugUnmanaged; private System.Windows.Forms.ToolStripMenuItem debugLocallyUnmanagedToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem jMStdoutMentionsToolStripMenuItem; - private System.Windows.Forms.ToolStripMenuItem jMLogsMentionsToolStripMenuItem1; private System.Windows.Forms.ToolStripMenuItem jMStdoutLinesToolStripMenuItem; - private System.Windows.Forms.ToolStripMenuItem jMLogsMentionsToolStripMenuItem; private System.Windows.Forms.TextBox textBox_stageFilter; private System.Windows.Forms.Button button_stageFilter; private System.Windows.Forms.Button button_clearStageFilter; @@ -1412,5 +1404,6 @@ namespace Microsoft.Research.Calypso.DryadAnalysis private System.Windows.Forms.ToolStripMenuItem cacheAllLogsToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem exportToCSVToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem cacheLogsForAllVerticesToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem cancelCurrentWorkToolStripMenuItem; } } diff --git a/JobBrowser/JobBrowser/JobBrowser.cs b/JobBrowser/JobBrowser/JobBrowser.cs index 4767cd4..5014ab7 100644 --- a/JobBrowser/JobBrowser/JobBrowser.cs +++ b/JobBrowser/JobBrowser/JobBrowser.cs @@ -34,10 +34,10 @@ using System.Threading; using System.Windows.Forms; using Microsoft.Msagl.GraphViewerGdi; using Microsoft.Msagl.Splines; -using Microsoft.Research.Calypso.JobObjectModel; -using Microsoft.Research.Calypso.Tools; +using Microsoft.Research.JobObjectModel; +using Microsoft.Research.Tools; -namespace Microsoft.Research.Calypso.DryadAnalysis +namespace Microsoft.Research.DryadAnalysis { /// /// A form to display information about a DryadLinq job. @@ -66,40 +66,7 @@ namespace Microsoft.Research.Calypso.DryadAnalysis /// private BackgroundWorkQueue queue; - /// - /// Activity to perform by the backgroundWorker. - /// - class BackgroundWorkInfo - { - public enum WorkKind - { - LoadJobInfo, - }; - - public WorkKind Work; // kind of operation to perform in the background - public bool Success; // if true the work succeeded - public DateTime workStartTime; // when work is started - - public override string ToString() - { - return this.Work.ToString(); - } - - /// - /// Add yourself to the list of pending work. - /// - /// List of pending work. - internal void AddTo(List list) - { - list.Add(this); - } - } - - /// - /// List of work activities to perform. - /// - readonly List pendingWork; - + // window regions starting from left-top in order going down #region JOB_HEADER /// @@ -352,10 +319,9 @@ namespace Microsoft.Research.Calypso.DryadAnalysis this.InitializeComponent(); this.queueWorker = new BackgroundWorker(); - this.queue = new BackgroundWorkQueue(this.queueWorker); + this.queue = new BackgroundWorkQueue(this.queueWorker, this.toolStripStatusLabel_currentWork, this.toolStripStatusLabel_backgroundWork); this.WarnedAboutDebugging = false; - this.pendingWork = new List(); this.status = new StatusWriter(this.toolStripStatusLabel, this.statusStrip, this.Status); this.refreshTimer = new System.Windows.Forms.Timer(); @@ -558,16 +524,6 @@ namespace Microsoft.Research.Calypso.DryadAnalysis this.RefreshDisplay(); } - /// - /// Load information about the current job. - /// - private void LoadJobDetails() - { - BackgroundWorkInfo work = new BackgroundWorkInfo(); - work.Work = BackgroundWorkInfo.WorkKind.LoadJobInfo; - this.StartBackgroundWork(work); - } - /// /// Loading the job information has completed. /// Time it took to load the job information. @@ -857,7 +813,7 @@ namespace Microsoft.Research.Calypso.DryadAnalysis case ExecutedVertexInstance.VertexState.Failed: return Color.Tomato; default: - throw new CalypsoDryadException("Unexpected vertex state " + state); + throw new DryadException("Unexpected vertex state " + state); } } @@ -986,22 +942,6 @@ namespace Microsoft.Research.Calypso.DryadAnalysis yield return new Tuple(unknown, VertexStateColor(ExecutedVertexInstance.VertexState.Unknown)); } - private static DryadJobStaticPlan CreatePlan(DryadLinqJobInfo job, StatusReporter status) - { - status("Constructing static plan", StatusKind.LongOp); - try - { - var result = JobObjectModel.DryadJobStaticPlan.CreatePlan(job, status); - return result; - } - catch (Exception ex) - { - status("Exception during building of static plan: " + ex.Message, StatusKind.Error); - Trace.TraceInformation(ex.ToString()); - return null; - } - } - /// /// Refresh and redisplay the query plan. /// @@ -1010,7 +950,7 @@ namespace Microsoft.Research.Calypso.DryadAnalysis this.richTextBox_file.Text = ""; var item = new BackgroundWorkItem( - (s, p) => JobObjectModel.DryadJobStaticPlan.CreatePlan(this.Job, this.Status), + m => JobObjectModel.DryadJobStaticPlan.CreatePlan(this.Job, m), this.PlanComputed, "refresh plan"); this.Queue(item); @@ -1455,7 +1395,7 @@ namespace Microsoft.Research.Calypso.DryadAnalysis this.EnableStageFiltering(true); this.stageHeaderData.RaiseListChangedEvents = false; this.currentStage = stage; - this.currentTable = null; + this.currentTable = null; // stageData is populated by the selectionChanged event handler for the stageHeader if (this.ShowingStageOrTable != KindOfStageShown.Stage) @@ -1677,10 +1617,9 @@ namespace Microsoft.Research.Calypso.DryadAnalysis /// /// Cluster object whose contents is read. /// Pattern to filter contents, for folders. - /// Used to report status. /// The file contents. - /// Progress reporter. - private static FileContents GetContents(StatusReporter status, Action progress, IClusterResidentObject path, string pattern) + /// Communication manager. + private static FileContents GetContents(CommManager manager, IClusterResidentObject path, string pattern) { if (path == null) { @@ -1705,6 +1644,7 @@ namespace Microsoft.Research.Calypso.DryadAnalysis int displayed = 0; foreach (IClusterResidentObject d in dirs) { + manager.Token.ThrowIfCancellationRequested(); if (d.Exception != null) { error += " [Error " + d.Exception.Message + "]"; @@ -1730,7 +1670,7 @@ namespace Microsoft.Research.Calypso.DryadAnalysis } else { - status("Extracting contents of " + path, StatusKind.LongOp); + manager.Status("Extracting contents of " + path, StatusKind.LongOp); ISharedStreamReader sr = path.GetStream(); if (sr.Exception != null) { @@ -1741,7 +1681,7 @@ namespace Microsoft.Research.Calypso.DryadAnalysis { if (path.Size == 0) error += "[empty]"; - var contents = sr.ReadToEnd(); + var contents = sr.ReadToEnd(manager.Token); return new FileContents(contents, error, linkCache); } } @@ -1756,7 +1696,7 @@ namespace Microsoft.Research.Calypso.DryadAnalysis private void DisplayContents1(IClusterResidentObject path, string pattern) { var item = new BackgroundWorkItem( - (s, p) => GetContents(s, p, path, pattern), + m => GetContents(m, path, pattern), this.ShowContents, "Read file"); this.Queue(item); @@ -1892,7 +1832,9 @@ namespace Microsoft.Research.Calypso.DryadAnalysis { this.label_title.Text = "Inputs"; this.Status("Discovering vertex channel information", StatusKind.LongOp); - bool found = this.currentVertex.DiscoverChannels(true, false, false, this.Status, this.UpdateProgress); + // TODO: this should run in the background + CommManager manager = new CommManager(this.Status, this.UpdateProgress, new CancellationTokenSource().Token); + bool found = this.currentVertex.DiscoverChannels(true, false, false, manager); if (found) { this.richTextBox_file.SuspendLayout(); @@ -1922,7 +1864,9 @@ namespace Microsoft.Research.Calypso.DryadAnalysis { this.label_title.Text = "Outputs"; this.Status("Discovering vertex channel information", StatusKind.LongOp); - bool found = this.currentVertex.DiscoverChannels(false, true, false, this.Status, this.UpdateProgress); + // TODO: this should run in the background + CommManager manager = new CommManager(this.Status, this.UpdateProgress, new CancellationTokenSource().Token); + bool found = this.currentVertex.DiscoverChannels(false, true, false, manager); if (found) { this.richTextBox_file.SuspendLayout(); @@ -2319,7 +2263,7 @@ namespace Microsoft.Research.Calypso.DryadAnalysis this.Status("Refreshing...", StatusKind.LongOp); this.Job.InvalidateCaches(); this.stageColorMap = null; // force recomputation - this.LoadJobDetails(); + this.RefreshJob(); } /// @@ -2340,8 +2284,7 @@ namespace Microsoft.Research.Calypso.DryadAnalysis private void JobBrowser_FormClosing(object sender, FormClosingEventArgs e) { this.refreshTimer.Stop(); - this.pendingWork.Clear(); - + this.queue.Stop(); this.formSettings.WarnedAboutDebugging = this.WarnedAboutDebugging; this.formSettings.WarnedAboutProfiling = this.WarnedAboutProfiling; this.formSettings.Location = this.Location; @@ -2496,107 +2439,37 @@ namespace Microsoft.Research.Calypso.DryadAnalysis /// Unused. private void JobBrowser_Shown(object sender, EventArgs e) { - this.LoadJobDetails(); - } - - #region BACKGROUND_WORK - /// - /// Start performing a piece of background work. - /// - /// Work to perform. - private void StartBackgroundWork(BackgroundWorkInfo work) - { - if (this.backgroundWorker.IsBusy) - { - work.AddTo(this.pendingWork); - this.toolStripStatusLabel_backgroundWork.Text = this.pendingWork.Count() + " tasks pending."; - this.Status("Queued task for execution", StatusKind.OK); - return; - } - - backgroundWorker.RunWorkerAsync(work); + this.RefreshJob(); } /// - /// Show the work currently being done. + /// Refresh the job details. /// - /// Message to display (work description). - private void ShowCurrentWork(string msg) + private void RefreshJob() { - if (this.InvokeRequired) - this.Invoke(new Action(this.ShowCurrentWork), msg); - else - this.toolStripStatusLabel_currentWork.Text = msg; - } - - /// - /// Perform some background work. - /// - /// Unused. - /// Event describing the work to perform. - private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e) - { - BackgroundWorkInfo work = (BackgroundWorkInfo)e.Argument; - work.workStartTime = DateTime.Now; - this.ShowCurrentWork("Doing " + work + "."); - - switch (work.Work) - { - case BackgroundWorkInfo.WorkKind.LoadJobInfo: - { - work.Success = this.Job.CollectEssentialInformation(this.Status, this.UpdateProgress); - break; - } - } - e.Result = work; - } - - /// - /// Background work has terminated. - /// - /// Unused. - /// Event describing the result. - private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) - { - this.ShowCurrentWork("Doing nothing."); - if (e.Cancelled) - { - this.Status("Background work was cancelled", StatusKind.OK); - goto end; - } - else if (e.Error != null) - { - this.Status("Exception during background work: " + e.Error.Message, StatusKind.Error); - Trace.TraceInformation(e.ToString()); - goto end; - } - - if (e.Result == null) - { - Trace.TraceInformation("Null result from background work!"); - // I don't know why this happens - goto end; - } - BackgroundWorkInfo result = (BackgroundWorkInfo)e.Result; - if (result.Success) - { - switch (result.Work) + DryadLinqJobInfo job = this.Job; + DateTime start = DateTime.Now; + var item = new BackgroundWorkItem( + m => { - case BackgroundWorkInfo.WorkKind.LoadJobInfo: - this.LoadJobCompleted(DateTime.Now - result.workStartTime); - break; - } - this.Status("Completed " + result, StatusKind.OK); - } - else - { - // failed in background work - goto end; - } + job.CollectEssentialInformation(m); + return DateTime.Now - start; + }, + this.JobInfoLoaded, + "refreshJob"); + this.Queue(item); + } - // do not overwrite the error message if the job did not succeed + /// + /// Called after a job has been loaded. + /// + /// If true the loading has been cancelled. + /// Time to load job. + private void JobInfoLoaded(bool cancelled, TimeSpan loadTime) + { + if (cancelled) return; - // refresh the stage view too + this.LoadJobCompleted(loadTime); string s = this.currentStage != null ? this.currentStage.Name : null; if (this.doingStartup && string.IsNullOrEmpty(s)) { @@ -2613,17 +2486,7 @@ namespace Microsoft.Research.Calypso.DryadAnalysis { this.SetTable(this.currentTable.Refresh(this.Job, this.Status, !this.hideCancelledVerticesToolStripMenuItem.Checked)); } - - end: - if (this.pendingWork.Any()) - { - BackgroundWorkInfo work = this.pendingWork[0]; - this.pendingWork.RemoveAt(0); - this.toolStripStatusLabel_backgroundWork.Text = this.pendingWork.Count() + " tasks pending."; - this.StartBackgroundWork(work); - } } - #endregion #region MOUSE_DYNAMIC_VIEWS /// @@ -2879,7 +2742,7 @@ namespace Microsoft.Research.Calypso.DryadAnalysis lv.Show(); var item = new BackgroundWorkItem( - (s, p) => ScanJMStdout(this.currentVertex, this.Job.ManagerVertex.StdoutFile, lv), + m => ScanJMStdout(this.currentVertex, this.Job.ManagerVertex.StdoutFile, lv), (c, b) => { }, "findStdout"); this.Queue(item); @@ -2936,7 +2799,8 @@ namespace Microsoft.Research.Calypso.DryadAnalysis /// Unused. private void diagnoseToolStripMenuItem_Click(object sender, EventArgs e) { - JobFailureDiagnosis diagnosis = JobFailureDiagnosis.CreateJobFailureDiagnosis(this.Job, this.staticPlan, this.Status, this.UpdateProgress); + CommManager manager = new CommManager(this.Status, this.UpdateProgress, new CancellationToken()); + JobFailureDiagnosis diagnosis = JobFailureDiagnosis.CreateJobFailureDiagnosis(this.Job, this.staticPlan, manager); DiagnosisLog log = diagnosis.Diagnose(); this.DisplayDiagnosis(log); } @@ -3328,7 +3192,7 @@ namespace Microsoft.Research.Calypso.DryadAnalysis ClusterStatus clusterStatus = this.Job.ClusterConfiguration.CreateClusterStatus(); var item = new BackgroundWorkItem( - (s, p) => ClusterWork.CancelJobs(job, clusterStatus, s, p), + m => ClusterWork.CancelJobs(job, clusterStatus, m), (c, b) => { }, "cancel"); this.Queue(item); @@ -3344,7 +3208,9 @@ namespace Microsoft.Research.Calypso.DryadAnalysis if (this.currentVertex == null) return; - VertexFailureDiagnosis vfd = VertexFailureDiagnosis.CreateVertexFailureDiagnosis(this.Job, this.staticPlan, this.currentVertex, this.Status, this.UpdateProgress); + // TODO: this should run in the background + CommManager manager = new CommManager(this.Status, this.UpdateProgress, new CancellationToken()); + VertexFailureDiagnosis vfd = VertexFailureDiagnosis.CreateVertexFailureDiagnosis(this.Job, this.staticPlan, this.currentVertex, manager); DiagnosisLog log = vfd.Diagnose(); this.DisplayDiagnosis(log); } @@ -3387,16 +3253,6 @@ namespace Microsoft.Research.Calypso.DryadAnalysis /// Unused. private void dataGridView_Scroll(object sender, ScrollEventArgs e) { - if (e.Type == ScrollEventType.EndScroll) - { - VScrollBar scrollbar = sender as VScrollBar; - if (scrollbar == null) - return; - DataGridView view = scrollbar.Parent as DataGridView; - if (view == null) - return; - view.AutoResizeColumns(DataGridViewAutoSizeColumnsMode.DisplayedCells); - } } /// @@ -3427,9 +3283,7 @@ namespace Microsoft.Research.Calypso.DryadAnalysis /// Unused. private void cacheAllLogsToolStripMenuItem_Click(object sender, EventArgs e) { - BackgroundWorkInfo work = new BackgroundWorkInfo(); - work.Work = BackgroundWorkInfo.WorkKind.LoadJobInfo; - this.StartBackgroundWork(work); + this.RefreshJob(); IClusterResidentObject folder = this.richtextBoxShownFile; if (folder == null || folder.Exception != null || !folder.RepresentsAFolder) @@ -3518,7 +3372,7 @@ namespace Microsoft.Research.Calypso.DryadAnalysis List vertices = this.stageData.ToList(); var item = new BackgroundWorkItem( - (s, p) => CacheAllVertices(this.Job.ClusterConfiguration, this.Job.Summary, vertices, s, p), + m => CacheAllVertices(this.Job.ClusterConfiguration, this.Job.Summary, vertices, m), (c, b) => { }, "cacheAll"); this.Queue(item); @@ -3528,23 +3382,27 @@ namespace Microsoft.Research.Calypso.DryadAnalysis /// Cache the vertices in the list; executed on the background thread. /// /// True: success. + /// Communication manager. + /// Cluster configuration. + /// Job to cache. + /// Vertices to cache. private static bool CacheAllVertices( ClusterConfiguration config, DryadLinqJobSummary summary, List vertices, - StatusReporter status, Action progress) + CommManager manager) { int done = 0; int todo = vertices.Count; int files = 0; - status("Caching data for " + todo + " vertices", StatusKind.LongOp); + manager.Status("Caching data for " + todo + " vertices", StatusKind.LongOp); foreach (ExecutedVertexInstance v in vertices) { files += CacheVertexInfo(config, summary, v); done++; - progress(done / todo); + manager.Progress(done / todo); } - progress(100); - status("Cached " + files + " files", StatusKind.OK); + manager.Progress(100); + manager.Status("Cached " + files + " files", StatusKind.OK); return true; } @@ -3576,6 +3434,7 @@ namespace Microsoft.Research.Calypso.DryadAnalysis } ISharedStreamReader reader = file.GetStream(); + // ReSharper disable once UnusedVariable foreach (string line in reader.ReadAllLines()) { // discard; causes caching @@ -3584,6 +3443,11 @@ namespace Microsoft.Research.Calypso.DryadAnalysis } return cached; } + + private void cancelCurrentWorkToolStripMenuItem_Click(object sender, EventArgs e) + { + this.queue.CancelCurrentWork(); + } } /// diff --git a/JobBrowser/JobBrowser/JobBrowser.csproj b/JobBrowser/JobBrowser/JobBrowser.csproj index 32ec137..fe6aed5 100644 --- a/JobBrowser/JobBrowser/JobBrowser.csproj +++ b/JobBrowser/JobBrowser/JobBrowser.csproj @@ -1,5 +1,6 @@ - + + Debug AnyCPU @@ -8,8 +9,8 @@ {EDDD2E0B-A52B-4E25-9436-B874017673FF} Exe Properties - DryadAnalysis - JobBrowser + JobBrowser + DryadLinqBrowser v4.5 512 @@ -22,6 +23,7 @@ 3.5 + publish\ true Disk @@ -36,29 +38,6 @@ 1.0.0.%2a false true - - - - true - full - false - bin\Debug\ - TRACE;DEBUG - prompt - 4 - bin\Debug\JobBrowser.xml - AllRules.ruleset - false - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - AllRules.ruleset - false true @@ -79,7 +58,48 @@ prompt AllRules.ruleset + + OnBuildSuccess + + + False + ..\..\..\Program Files (x86)\Microsoft Visual Studio 9.0\Common7\IDE\PublicAssemblies\EnvDTE.dll + + + False + ..\..\..\Program Files (x86)\Microsoft Visual Studio 9.0\Common7\IDE\PublicAssemblies\EnvDTE80.dll + + + False + ..\..\..\Program Files (x86)\Microsoft Visual Studio 9.0\Common7\IDE\PublicAssemblies\EnvDTE90.dll + + + False + ..\packages\Microsoft.Data.Edm.5.6.1\lib\net40\Microsoft.Data.Edm.dll + + + False + ..\packages\Microsoft.Data.OData.5.6.1\lib\net40\Microsoft.Data.OData.dll + + + False + ..\packages\Microsoft.Data.Services.Client.5.6.1\lib\net40\Microsoft.Data.Services.Client.dll + + + False + ..\packages\Microsoft.Hadoop.Client.1.1.0.7\lib\net40\Microsoft.Hadoop.Client.dll + + + False + ..\..\Daphne\Dependences\Microsoft.Hpc.Scheduler.dll + True + + + False + ..\..\Daphne\Dependences\Microsoft.Hpc.Scheduler.Properties.dll + True + False ..\packages\Microsoft.Research.DryadLINQ.MSAGL.3.0.0.1\lib\Microsoft.Msagl.dll @@ -92,13 +112,71 @@ False ..\packages\Microsoft.Research.DryadLINQ.MSAGL.3.0.0.1\lib\Microsoft.Msagl.GraphViewerGdi.dll + + False + ..\packages\Microsoft.Research.Peloponnese.0.7.2-beta\lib\net45\Microsoft.Research.Peloponnese.HadoopBridge.dll + + + False + ..\packages\Microsoft.Research.Peloponnese.0.7.2-beta\lib\net45\Microsoft.Research.Peloponnese.Utils.dll + + + ..\packages\Microsoft.Bcl.Async.1.0.166\lib\net40\Microsoft.Threading.Tasks.dll + + + ..\packages\Microsoft.Bcl.Async.1.0.166\lib\net40\Microsoft.Threading.Tasks.Extensions.dll + + + ..\packages\Microsoft.Bcl.Async.1.0.166\lib\net40\Microsoft.Threading.Tasks.Extensions.Desktop.dll + + + ..\packages\Microsoft.WindowsAzure.Common.1.0.1\lib\net45\Microsoft.WindowsAzure.Common.dll + + + ..\packages\Microsoft.WindowsAzure.Common.1.0.1\lib\net45\Microsoft.WindowsAzure.Common.NetFramework.dll + + + ..\packages\Microsoft.WindowsAzure.ConfigurationManager.2.0.3\lib\net40\Microsoft.WindowsAzure.Configuration.dll + + + ..\packages\Microsoft.WindowsAzure.Management.1.0.0\lib\net40\Microsoft.WindowsAzure.Management.dll + + + False + ..\packages\Microsoft.WindowsAzure.Management.HDInsight.1.1.0.7\lib\net40\Microsoft.WindowsAzure.Management.HDInsight.dll + + + False + ..\packages\Microsoft.Hadoop.Client.1.1.0.7\lib\net40\Microsoft.WindowsAzure.Management.HDInsight.Framework.dll + + + False + ..\packages\Microsoft.Hadoop.Client.1.1.0.7\lib\net40\Microsoft.WindowsAzure.Management.HDInsight.Framework.Core.dll + + + ..\packages\Microsoft.WindowsAzure.Management.Storage.1.0.0\lib\net40\Microsoft.WindowsAzure.Management.Storage.dll + + + False + ..\packages\WindowsAzure.Storage.3.1.0.1\lib\net40\Microsoft.WindowsAzure.Storage.dll + 3.5 + + + + ..\packages\Microsoft.Net.Http.2.2.19\lib\net45\System.Net.Http.Extensions.dll + + + ..\packages\Microsoft.Net.Http.2.2.19\lib\net45\System.Net.Http.Primitives.dll + + + 3.5 @@ -186,11 +264,11 @@ JobObjectModel - {20B91AAF-AAD4-47DF-9F1D-494DE6E066F9} + {20b91aaf-aad4-47df-9f1d-494de6e066f9} Tools - {27635A68-ADFC-4192-9262-B4E6ECDDCE09} + {27635a68-adfc-4192-9262-b4e6ecddce09} UsefulForms @@ -234,6 +312,19 @@ + + + + + + + + This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + - + \ No newline at end of file diff --git a/JobBrowser/JobBrowser/JobBrowser.resx b/JobBrowser/JobBrowser/JobBrowser.resx index 951e670..0e46710 100644 --- a/JobBrowser/JobBrowser/JobBrowser.resx +++ b/JobBrowser/JobBrowser/JobBrowser.resx @@ -126,9 +126,6 @@ 17, 17 - - 209, 17 - 42 diff --git a/JobBrowser/JobBrowser/LocalDebuggingAndProfiling.cs b/JobBrowser/JobBrowser/LocalDebuggingAndProfiling.cs index 2920dda..f8201ff 100644 --- a/JobBrowser/JobBrowser/LocalDebuggingAndProfiling.cs +++ b/JobBrowser/JobBrowser/LocalDebuggingAndProfiling.cs @@ -24,13 +24,13 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Text.RegularExpressions; -using Microsoft.Research.Calypso.JobObjectModel; -using Microsoft.Research.Calypso.Tools; -using Microsoft.Research.Calypso.UsefulForms; +using Microsoft.Research.JobObjectModel; +using Microsoft.Research.Tools; +using Microsoft.Research.UsefulForms; using Microsoft.Win32; using System.Diagnostics; -namespace Microsoft.Research.Calypso.DryadAnalysis +namespace Microsoft.Research.DryadAnalysis { /// /// Class that encapsulates the steps required to debug a vertex locally. diff --git a/JobBrowser/JobBrowser/LogViewer.Designer.cs b/JobBrowser/JobBrowser/LogViewer.Designer.cs index 360b6c0..693c491 100644 --- a/JobBrowser/JobBrowser/LogViewer.Designer.cs +++ b/JobBrowser/JobBrowser/LogViewer.Designer.cs @@ -18,9 +18,9 @@ See the Apache Version 2.0 License for specific language governing permissions a limitations under the License. */ -using Microsoft.Research.Calypso.Tools; +using Microsoft.Research.Tools; -namespace Microsoft.Research.Calypso.DryadAnalysis +namespace Microsoft.Research.DryadAnalysis { partial class LogViewer { diff --git a/JobBrowser/JobBrowser/LogViewer.cs b/JobBrowser/JobBrowser/LogViewer.cs index c1fd03e..9c00e1f 100644 --- a/JobBrowser/JobBrowser/LogViewer.cs +++ b/JobBrowser/JobBrowser/LogViewer.cs @@ -19,15 +19,15 @@ limitations under the License. */ -using Microsoft.Research.Calypso.JobObjectModel; using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Windows.Forms; -using Microsoft.Research.Calypso.Tools; +using Microsoft.Research.JobObjectModel; +using Microsoft.Research.Tools; -namespace Microsoft.Research.Calypso.DryadAnalysis +namespace Microsoft.Research.DryadAnalysis { /// /// A log viewer displays fragments of logs or other text files. @@ -37,7 +37,7 @@ namespace Microsoft.Research.Calypso.DryadAnalysis bool canceled; DGVData shownText; - DGVData shownLogLines; + DGVData shownLogLines; StatusWriter status; /// @@ -77,7 +77,7 @@ namespace Microsoft.Research.Calypso.DryadAnalysis } else { - this.shownLogLines = new DGVData(); + this.shownLogLines = new DGVData(); this.filteredDataGridView.SetDataSource(this.shownLogLines); foreach (string s in new string[] { "Malformed", "IsError", "OriginalLogLine", "File", "LineNo" }) { @@ -132,7 +132,7 @@ namespace Microsoft.Research.Calypso.DryadAnalysis long bytes = 0; List toAddText = new List(); - List toAddLog = new List(); + List toAddLog = new List(); while (!sr.EndOfStream) { string line = sr.ReadLine(); @@ -141,7 +141,7 @@ namespace Microsoft.Research.Calypso.DryadAnalysis toAddText.Add(new TextFileLine(lineno, line)); else { - PositionedCosmosLogEntry cle = new PositionedCosmosLogEntry(filename, lineno, line); + PositionedDryadLogEntry cle = new PositionedDryadLogEntry(filename, lineno, line); if (cle.Malformed) { Trace.TraceInformation("Malformed log entry: " + cle.OriginalLogLine); @@ -204,7 +204,7 @@ namespace Microsoft.Research.Calypso.DryadAnalysis } else { - PositionedCosmosLogEntry cle = new PositionedCosmosLogEntry(file, lineno, text); + PositionedDryadLogEntry cle = new PositionedDryadLogEntry(file, lineno, text); if (cle.Malformed) return; this.shownLogLines.AddItem(cle); @@ -274,7 +274,7 @@ namespace Microsoft.Research.Calypso.DryadAnalysis var rows = this.filteredDataGridView.DataGridView.SelectedRows; for (int i = 0; i < rows.Count; i++) { - PositionedCosmosLogEntry entry = ((PositionedCosmosLogEntry)rows[i].DataBoundItem); + PositionedDryadLogEntry entry = ((PositionedDryadLogEntry)rows[i].DataBoundItem); position += entry.File + ":" + entry.LineNo + Environment.NewLine; } MessageBox.Show(position, "File containing log entries"); @@ -319,7 +319,7 @@ namespace Microsoft.Research.Calypso.DryadAnalysis /// /// Cosmos log entry with position information. /// - public class PositionedCosmosLogEntry : CosmosLogEntry + public class PositionedDryadLogEntry : DryadLogEntry { /// /// File containing the log entry. @@ -336,7 +336,7 @@ namespace Microsoft.Research.Calypso.DryadAnalysis /// File containing the log entry. /// Line number. /// Line contents. - public PositionedCosmosLogEntry(string file, long lineno, string line) + public PositionedDryadLogEntry(string file, long lineno, string line) : base(line) { this.File = file; diff --git a/JobBrowser/JobBrowser/Properties/AssemblyInfo.cs b/JobBrowser/JobBrowser/Properties/AssemblyInfo.cs index 628ac4c..74ab41b 100644 --- a/JobBrowser/JobBrowser/Properties/AssemblyInfo.cs +++ b/JobBrowser/JobBrowser/Properties/AssemblyInfo.cs @@ -25,12 +25,12 @@ using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. -[assembly: AssemblyTitle("DryadAnalysis")] +[assembly: AssemblyTitle("DryadLinqBrowser")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("Microsoft Corporation")] -[assembly: AssemblyProduct("DryadAnalysis")] -[assembly: AssemblyCopyright("Copyright © Microsoft Corporation. All rights reserved.")] +[assembly: AssemblyCompany("MSIT")] +[assembly: AssemblyProduct("DryadLinqBrowser")] +[assembly: AssemblyCopyright("Copyright © Microsoft")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] @@ -52,5 +52,5 @@ using System.Runtime.InteropServices; // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("0.1.0.0")] -[assembly: AssemblyFileVersion("0.1.0.0")] +[assembly: AssemblyVersion("1.0.1.0")] +[assembly: AssemblyFileVersion("1.0.1.0")] diff --git a/JobBrowser/JobBrowser/jobschedule.cs b/JobBrowser/JobBrowser/jobschedule.cs index a9dd6e9..69df083 100644 --- a/JobBrowser/JobBrowser/jobschedule.cs +++ b/JobBrowser/JobBrowser/jobschedule.cs @@ -19,13 +19,13 @@ limitations under the License. */ -using Microsoft.Research.Calypso.Tools; using System; using System.Collections.Generic; using System.Drawing; using System.Linq; +using Microsoft.Research.Tools; -namespace Microsoft.Research.Calypso.JobObjectModel +namespace Microsoft.Research.JobObjectModel { /// /// Information about the dynamic execution schedule of a job. diff --git a/JobBrowser/JobBrowser/packages.config b/JobBrowser/JobBrowser/packages.config index 6d273c8..420f9ff 100644 --- a/JobBrowser/JobBrowser/packages.config +++ b/JobBrowser/JobBrowser/packages.config @@ -1,4 +1,22 @@ - + + + + + + + + + - + + + + + + + + + + + \ No newline at end of file diff --git a/JobBrowser/Microsoft.Research.JobBrowser.nuspec b/JobBrowser/Microsoft.Research.JobBrowser.nuspec deleted file mode 100644 index 67ad440..0000000 --- a/JobBrowser/Microsoft.Research.JobBrowser.nuspec +++ /dev/null @@ -1,48 +0,0 @@ - - - - Microsoft.Research.Calypso - 0.7.0-beta024 - Calypso Job Browser for DryadLINQ - msrsvc - msrsvc,Microsoft - http://www.apache.org/licenses/LICENSE-2.0 - http://research.microsoft.com/en-us/um/siliconvalley/projects/BigDataDev/ - true - The DryadLINQ job browser is a graphical user interface which provides a unified view of a large part of the distributed state of a DryadLINQ job. In this document we provide a brief overview of the main capabilities of the DryadLINQ job browser. - © Microsoft Corporation. All rights reserved. - en-US - DryadLINQ Dryad Azure - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/JobBrowser/Tools/Graphlayout.cs b/JobBrowser/Tools/Graphlayout.cs index c37821f..ba548d6 100644 --- a/JobBrowser/Tools/Graphlayout.cs +++ b/JobBrowser/Tools/Graphlayout.cs @@ -24,7 +24,7 @@ using System.Collections.Generic; using System.Drawing; using System.Drawing.Drawing2D; -namespace Microsoft.Research.Calypso.Tools +namespace Microsoft.Research.Tools { /// /// Deals with the plane representation of a graph. diff --git a/JobBrowser/Tools/Properties/AssemblyInfo.cs b/JobBrowser/Tools/Properties/AssemblyInfo.cs index ca06971..2bcfed4 100644 --- a/JobBrowser/Tools/Properties/AssemblyInfo.cs +++ b/JobBrowser/Tools/Properties/AssemblyInfo.cs @@ -28,9 +28,9 @@ using System.Runtime.InteropServices; [assembly: AssemblyTitle("Tools")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("Microsoft Corporation")] +[assembly: AssemblyCompany("MSIT")] [assembly: AssemblyProduct("Tools")] -[assembly: AssemblyCopyright("Copyright © Microsoft Corporation. All rights reserved.")] +[assembly: AssemblyCopyright("Copyright © MSIT 2008")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] @@ -52,5 +52,5 @@ using System.Runtime.InteropServices; // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("0.1.0.0")] -[assembly: AssemblyFileVersion("0.1.0.0")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/JobBrowser/Tools/Tools.csproj b/JobBrowser/Tools/Tools.csproj index 9711bdd..48d27a0 100644 --- a/JobBrowser/Tools/Tools.csproj +++ b/JobBrowser/Tools/Tools.csproj @@ -1,4 +1,4 @@ - + Debug @@ -9,7 +9,7 @@ Library Properties Microsoft.Research.Artemis.Tools - Microsoft.Research.DryadLinq.JobBrowser.Tools + Microsoft.Research.Calypso.Tools v4.5 512 @@ -33,28 +33,6 @@ true - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - bin\Debug\Microsoft.Research.DryadLinq.JobBrowser.Tools.xml - AllRules.ruleset - false - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - AllRules.ruleset - false - true bin\x64\Debug\ @@ -84,6 +62,7 @@ + @@ -117,4 +96,4 @@ --> - + \ No newline at end of file diff --git a/JobBrowser/Tools/drawingSurface.cs b/JobBrowser/Tools/drawingSurface.cs index c6d43c7..e5dfc88 100644 --- a/JobBrowser/Tools/drawingSurface.cs +++ b/JobBrowser/Tools/drawingSurface.cs @@ -19,7 +19,7 @@ limitations under the License. */ -namespace Microsoft.Research.Calypso.Tools +namespace Microsoft.Research.Tools { using System; using System.Drawing; diff --git a/JobBrowser/Tools/partitionedTables.cs b/JobBrowser/Tools/partitionedTables.cs index 858b04c..943b38c 100644 --- a/JobBrowser/Tools/partitionedTables.cs +++ b/JobBrowser/Tools/partitionedTables.cs @@ -19,7 +19,7 @@ limitations under the License. */ -namespace Microsoft.Research.Calypso.Tools +namespace Microsoft.Research.Tools { using System.Collections.Generic; using System.IO; diff --git a/JobBrowser/Tools/tools.cs b/JobBrowser/Tools/tools.cs index dc47e3f..3132511 100644 --- a/JobBrowser/Tools/tools.cs +++ b/JobBrowser/Tools/tools.cs @@ -31,6 +31,7 @@ using System.Net; using System.Reflection; using System.Runtime.InteropServices; using System.Text; +using System.Windows.Forms; using System.Xml; using System.Xml.Serialization; using System.Text.RegularExpressions; @@ -38,7 +39,7 @@ using System.Threading; using System.Security.Cryptography; // Implement here generally-useful tools. -namespace Microsoft.Research.Calypso.Tools +namespace Microsoft.Research.Tools { /// /// An error handling function. @@ -66,6 +67,38 @@ namespace Microsoft.Research.Calypso.Tools LongOp, }; + /// + /// Communication management with background activities. + /// + public struct CommManager + { + /// + /// Used to report status. + /// + public StatusReporter Status; + /// + /// Used to report progress. + /// + public Action Progress; + /// + /// Used to cancel activities. + /// + public CancellationToken Token; + + /// + /// Create a communication manager. + /// + /// Status to report errors. + /// Action to report progress. + /// Token to cancel computations. + public CommManager(StatusReporter status, Action progress, CancellationToken token) + { + this.Status = status; + this.Progress = progress; + this.Token = token; + } + } + /// /// Untyped version of work item. /// @@ -100,6 +133,10 @@ namespace Microsoft.Research.Calypso.Tools /// Exception that occurred during background work (or null). /// void RunContinuation(Exception ex); + /// + /// Can be used to cancel this work item. + /// + CancellationTokenSource TokenSource { get; } } /// @@ -111,7 +148,7 @@ namespace Microsoft.Research.Calypso.Tools /// /// Computation to invoke. If the computation is not cancelled the result is passed as the second argument to the continuation. /// - public Func, T> Computation { get; protected set; } + public Func Computation { get; protected set; } /// /// Function to call when the work is completed. The first argument is 'true' if the computation was not cancelled. The second argument is the result of the computation. @@ -141,6 +178,10 @@ namespace Microsoft.Research.Calypso.Tools /// Queue containing item. /// private BackgroundWorkQueue queue; + /// + /// Source for cancellation token. + /// + public CancellationTokenSource TokenSource { get; protected set; } // ReSharper disable ConvertToConstant.Local bool TraceAsync = @@ -159,7 +200,7 @@ namespace Microsoft.Research.Calypso.Tools /// Computation to perform on a background thread. Ideally this should always be a static method. /// Continuation to invoke on the foreground thread when work is done. /// Description of the background work. - public BackgroundWorkItem(Func, T> computation, Action continuation, string description) + public BackgroundWorkItem(Func computation, Action continuation, string description) { this.Description = description; this.Computation = computation; @@ -167,23 +208,22 @@ namespace Microsoft.Research.Calypso.Tools this.reporter = null; this.queue = null; this.Id = crtid++; + this.TokenSource = new CancellationTokenSource(); } /// /// Perform the background work. /// - /// Worker which does the work. - /// Delegate used to report errors. + /// Worker which does the work. + /// Delegate used to report errors. /// Delegate used to report progress. /// If true for an item, cancel it. - // ReSharper disable ParameterHidesMember - public void Queue(BackgroundWorkQueue queue, StatusReporter reporter, Action progressReporter, Func cancel) - // ReSharper restore ParameterHidesMember + public void Queue(BackgroundWorkQueue q, StatusReporter rep, Action progressReporter, Func cancel) { if (TraceAsync) Console.WriteLine("{0} Queueing {1}", Utilities.PreciseTime, this.Description); - this.queue = queue; - this.reporter = reporter; + this.queue = q; + this.reporter = rep; this.progress = progressReporter; this.queue.CancelMatching(cancel); this.queue.Enqueue(this); @@ -200,7 +240,8 @@ namespace Microsoft.Research.Calypso.Tools Console.WriteLine("{0} Running function {1}", Utilities.PreciseTime, this.Description); try { - this.Result = this.Computation(this.reporter, this.progress); + CommManager manager = new CommManager(this.reporter, this.progress, this.TokenSource.Token); + this.Result = this.Computation(manager); } catch (Exception ex) { @@ -235,6 +276,7 @@ namespace Microsoft.Research.Calypso.Tools if (TraceAsync) Console.WriteLine("{1}/{0}: Cancelling", this.Description, this.Id); this.Cancelled = true; + this.TokenSource.Cancel(); this.queue.CancelMe(this); } @@ -266,12 +308,18 @@ namespace Microsoft.Research.Calypso.Tools /// IBackgroundWorkItem current; + private ToolStripStatusLabel currentItemLabel, queueSizeLabel; + /// /// Create a background work queue servicing a specified worker. /// /// Worker to use. - public BackgroundWorkQueue(BackgroundWorker worker) + /// Label where the current work is displayed. + /// Label where the queue size is displayed. + public BackgroundWorkQueue(BackgroundWorker worker, ToolStripStatusLabel current, ToolStripStatusLabel queue) { + this.currentItemLabel = current; + this.queueSizeLabel = queue; if (worker == null) throw new ArgumentNullException("worker"); this.BackgroundWorker = worker; @@ -280,6 +328,7 @@ namespace Microsoft.Research.Calypso.Tools this.BackgroundWorker.DoWork += this.worker_DoWork; this.queue = new List(); this.current = null; + this.stopped = false; } /// @@ -289,7 +338,7 @@ namespace Microsoft.Research.Calypso.Tools /// Unused. void worker_DoWork(object sender, DoWorkEventArgs e) { - if (this.current == null) + if (this.stopped || this.current == null) return; #if DEBUG_WORKQUEUE #endif @@ -306,6 +355,8 @@ namespace Microsoft.Research.Calypso.Tools e.Cancel = true; } + private bool stopped; + /// /// Called when the worker is completed. /// @@ -319,6 +370,8 @@ namespace Microsoft.Research.Calypso.Tools #endif IBackgroundWorkItem crt = this.current; this.current = null; + if (this.currentItemLabel != null) + this.currentItemLabel.Text = ""; crt.RunContinuation(e.Error); } this.Kick(); @@ -348,7 +401,11 @@ namespace Microsoft.Research.Calypso.Tools if (this.current != null) throw new Exception("current is not null"); this.current = this.queue[0]; + if (this.currentItemLabel != null) + this.currentItemLabel.Text = "Doing " + this.current.Description; this.queue.RemoveAt(0); + if (this.queueSizeLabel != null) + this.queueSizeLabel.Text = "Pending " + this.queue.Count + " items"; this.Start(); } @@ -411,9 +468,22 @@ namespace Microsoft.Research.Calypso.Tools } } + /// + /// Stop the queue. + /// public void Stop() { - // TODO + this.stopped = true; + this.CancelCurrentWork(); + } + + /// + /// Cancel the currently running work. + /// + public void CancelCurrentWork() + { + if (this.current == null) return; + this.current.Cancel(); } } @@ -3409,7 +3479,8 @@ namespace Microsoft.Research.Calypso.Tools /// Read the stream to the end from the current position. /// /// The contents of the stream. - string ReadToEnd(); + /// Can be used to cancel the reading. + string ReadToEnd(CancellationToken token); /// /// Read all the lines remaining in the stream. @@ -3465,11 +3536,15 @@ namespace Microsoft.Research.Calypso.Tools /// Read the whole stream to the end. /// /// A string containing the whole contents of the stream. - public virtual string ReadToEnd() + /// Can be used to cancel the reading. + public virtual string ReadToEnd(CancellationToken token) { StringBuilder result = new StringBuilder(); foreach (string s in this.ReadAllLines()) + { + token.ThrowIfCancellationRequested(); result.AppendLine(s); + } return result.ToString(); } @@ -3612,11 +3687,13 @@ namespace Microsoft.Research.Calypso.Tools /// Read the stream to the end from the current position. /// /// The contents of the stream. - public override string ReadToEnd() + /// Can be used to cancel the reading. + public override string ReadToEnd(CancellationToken token) { string result = this.actualReader.ReadToEnd(); if (this.cacheWriter != null) { + token.ThrowIfCancellationRequested(); this.cacheWriter.Write(result); this.cacheWriter.Close(); if (this.onClose != null) diff --git a/JobBrowser/UsefulForms/FilteredDataGridView.Designer.cs b/JobBrowser/UsefulForms/FilteredDataGridView.Designer.cs index 8a716ac..f28be25 100644 --- a/JobBrowser/UsefulForms/FilteredDataGridView.Designer.cs +++ b/JobBrowser/UsefulForms/FilteredDataGridView.Designer.cs @@ -18,7 +18,7 @@ See the Apache Version 2.0 License for specific language governing permissions a limitations under the License. */ -namespace Microsoft.Research.Calypso.Tools +namespace Microsoft.Research.Tools { partial class FilteredDataGridView { @@ -33,7 +33,7 @@ namespace Microsoft.Research.Calypso.Tools /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { - if (disposing && (components != null)) + if (disposing && (components != null)) { components.Dispose(); } diff --git a/JobBrowser/UsefulForms/FilteredDataGridView.cs b/JobBrowser/UsefulForms/FilteredDataGridView.cs index 7ad7d1c..37a60bf 100644 --- a/JobBrowser/UsefulForms/FilteredDataGridView.cs +++ b/JobBrowser/UsefulForms/FilteredDataGridView.cs @@ -23,7 +23,7 @@ using System; using System.Collections.Generic; using System.Windows.Forms; -namespace Microsoft.Research.Calypso.Tools +namespace Microsoft.Research.Tools { /// /// A data grid that filters the contents. diff --git a/JobBrowser/UsefulForms/PasswordDialog.Designer.cs b/JobBrowser/UsefulForms/PasswordDialog.Designer.cs index 0c3b543..1e67ce9 100644 --- a/JobBrowser/UsefulForms/PasswordDialog.Designer.cs +++ b/JobBrowser/UsefulForms/PasswordDialog.Designer.cs @@ -18,7 +18,7 @@ See the Apache Version 2.0 License for specific language governing permissions a limitations under the License. */ -namespace Microsoft.Research.Calypso.UsefulForms +namespace Microsoft.Research.UsefulForms { partial class PasswordDialog { diff --git a/JobBrowser/UsefulForms/PasswordDialog.cs b/JobBrowser/UsefulForms/PasswordDialog.cs index 694724b..a8d6e86 100644 --- a/JobBrowser/UsefulForms/PasswordDialog.cs +++ b/JobBrowser/UsefulForms/PasswordDialog.cs @@ -21,7 +21,7 @@ limitations under the License. using System.Windows.Forms; -namespace Microsoft.Research.Calypso.UsefulForms +namespace Microsoft.Research.UsefulForms { /// /// Prompt user for password. diff --git a/JobBrowser/UsefulForms/Properties/AssemblyInfo.cs b/JobBrowser/UsefulForms/Properties/AssemblyInfo.cs index 74404d8..334a73e 100644 --- a/JobBrowser/UsefulForms/Properties/AssemblyInfo.cs +++ b/JobBrowser/UsefulForms/Properties/AssemblyInfo.cs @@ -28,9 +28,9 @@ using System.Runtime.InteropServices; [assembly: AssemblyTitle("UsefulForms")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("Microsoft Corporation")] +[assembly: AssemblyCompany("Microsoft")] [assembly: AssemblyProduct("UsefulForms")] -[assembly: AssemblyCopyright("Copyright © Microsoft Corporation. All rights reserved.")] +[assembly: AssemblyCopyright("Copyright © Microsoft 2010")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] @@ -52,5 +52,5 @@ using System.Runtime.InteropServices; // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("0.1.0.0")] -[assembly: AssemblyFileVersion("0.1.0.0")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/JobBrowser/UsefulForms/Status.cs b/JobBrowser/UsefulForms/Status.cs index aefdb21..2684eef 100644 --- a/JobBrowser/UsefulForms/Status.cs +++ b/JobBrowser/UsefulForms/Status.cs @@ -23,7 +23,7 @@ using System.Drawing; using System; using System.Windows.Forms; -namespace Microsoft.Research.Calypso.Tools +namespace Microsoft.Research.Tools { /// /// Delegate used for invoking status messages from accross threads. diff --git a/JobBrowser/UsefulForms/UrlDialog.Designer.cs b/JobBrowser/UsefulForms/UrlDialog.Designer.cs index 18b5ddd..ff53ec3 100644 --- a/JobBrowser/UsefulForms/UrlDialog.Designer.cs +++ b/JobBrowser/UsefulForms/UrlDialog.Designer.cs @@ -18,7 +18,7 @@ See the Apache Version 2.0 License for specific language governing permissions a limitations under the License. */ -namespace Microsoft.Research.Calypso.UsefulForms +namespace Microsoft.Research.UsefulForms { partial class CustomDialog { diff --git a/JobBrowser/UsefulForms/UrlDialog.cs b/JobBrowser/UsefulForms/UrlDialog.cs index 7bf385f..39866e2 100644 --- a/JobBrowser/UsefulForms/UrlDialog.cs +++ b/JobBrowser/UsefulForms/UrlDialog.cs @@ -19,7 +19,7 @@ limitations under the License. */ -namespace Microsoft.Research.Calypso.UsefulForms +namespace Microsoft.Research.UsefulForms { using System.Windows.Forms; diff --git a/JobBrowser/UsefulForms/UsefulForms.csproj b/JobBrowser/UsefulForms/UsefulForms.csproj index d7cea04..4b7384e 100644 --- a/JobBrowser/UsefulForms/UsefulForms.csproj +++ b/JobBrowser/UsefulForms/UsefulForms.csproj @@ -1,4 +1,4 @@ - + Debug @@ -9,7 +9,7 @@ Library Properties UsefulForms - Microsoft.Research.DryadLinq.JobBrowser.UsefulForms + Microsoft.Research.Calypso.UsefulForms v4.5 512 @@ -33,27 +33,6 @@ true - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - AllRules.ruleset - false - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - AllRules.ruleset - false - true bin\x64\Debug\ @@ -72,6 +51,9 @@ prompt AllRules.ruleset + + OnBuildSuccess + @@ -152,4 +134,4 @@ --> - + \ No newline at end of file diff --git a/JobBrowser/UsefulForms/WarningBox.Designer.cs b/JobBrowser/UsefulForms/WarningBox.Designer.cs index 23f9ad7..35a0a78 100644 --- a/JobBrowser/UsefulForms/WarningBox.Designer.cs +++ b/JobBrowser/UsefulForms/WarningBox.Designer.cs @@ -18,7 +18,7 @@ See the Apache Version 2.0 License for specific language governing permissions a limitations under the License. */ -namespace Microsoft.Research.Calypso.UsefulForms +namespace Microsoft.Research.UsefulForms { partial class WarningBox { diff --git a/JobBrowser/UsefulForms/WarningBox.cs b/JobBrowser/UsefulForms/WarningBox.cs index e686741..0c01b52 100644 --- a/JobBrowser/UsefulForms/WarningBox.cs +++ b/JobBrowser/UsefulForms/WarningBox.cs @@ -22,7 +22,7 @@ limitations under the License. using System; using System.Windows.Forms; -namespace Microsoft.Research.Calypso.UsefulForms +namespace Microsoft.Research.UsefulForms { /// /// A box displaying a warning, which can be turned off. diff --git a/JobBrowser/doc/clusterBrowser.png b/JobBrowser/doc/clusterBrowser.png new file mode 100644 index 0000000000000000000000000000000000000000..c113ff43ea068e86c306b6cefe4d9231c8824f93 GIT binary patch literal 25940 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 C@<#0d literal 0 HcmV?d00001 diff --git a/JobBrowser/doc/clusterManipulation.png b/JobBrowser/doc/clusterManipulation.png new file mode 100644 index 0000000000000000000000000000000000000000..19a454533d712031ad0c49b053a5bf1d26370fa4 GIT binary patch literal 4225 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 LgMe#vT%!IDzE&i$ literal 0 HcmV?d00001 diff --git a/JobBrowser/doc/clusterMenu.png b/JobBrowser/doc/clusterMenu.png new file mode 100644 index 0000000000000000000000000000000000000000..e99b1c61c6b431e3496cd200aaff81c67c25254a GIT binary patch literal 2267 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 literal 0 HcmV?d00001 diff --git a/JobBrowser/doc/dynamicPlan.png b/JobBrowser/doc/dynamicPlan.png new file mode 100644 index 0000000000000000000000000000000000000000..b855ddd405eb2ffa2f3b2bb1c91c8f274f97a68c GIT binary patch literal 4586 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 EUkCcIJOBUy literal 0 HcmV?d00001 diff --git a/JobBrowser/doc/filteringVertex.png b/JobBrowser/doc/filteringVertex.png new file mode 100644 index 0000000000000000000000000000000000000000..7b21cd64fca1a3a57116a75850c7987e34de21b0 GIT binary patch literal 1982 zcmV;v2SNCWP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D2UAHzK~#8N?VZ1F zBR3Gn{aSgIyhR|mNPkFiFj5$Pg_J2>ro9va?ur0Tae!T(B!2i|F&qxb5jCRzS$8+Px82#nNq?V7m})yzp{Imo(l<9Z!KxCbdj9&1N%Sfn z!Ft3NB!^Kr>Bq0zS-@1EzkIe7dP+FInjT~v?$=rL-}g|Ydr=eG{Im&HFPlk*kTFUM z*Ra@L@C2($yWOF7yD~LRDPLr)9zilu5C)MG3|v%>$ME{^tF2Kqv5VHTx!u?%u$g3t z+z92<%Qo!TI)r?X6%3msk?$37_mbz-40f3+XMfXz&?Q$f-b-Cpco=`%D#*tg=tYcp ztlt{4%VLXPB(^wZ2Rss~1|&9JiHJ)Hw`@)765H*b{&`B#b-(2Tfb23k3E6?U$nA1t z7S5@VDZLaWXKsGB^0i8+*!^CBVGjR*pip~8Fct8$*hpB zj`OcOqJ;aZSEfd_>zeCW-qlpd)^TtZ2wNSjUbc>d%Y^y(sG@a@u@E^&p&=DB5M0NF z3xxZs7sBK!5~oO--hc5rY!}|_ygXa56{8+OU#FuGsY{@N()6<(j&qZYhIxZ*L6ad6 zNuCsllwca-ofI(2`1t6I;lApH7`cjJZM}+dJvwaH@LM{76bXQAQw)0*gX}y-L7s45HKldfuHEkG@25JnbWn)YH3?hC z)K!iqvqCECxKYlBUp}CW>cCi?XLHNFrQG&KJYOaNT2}SB?_9~OkeWJv z{PG%ZTvI~7{T05f!qc(tTLn5CbEuQc9goV^arhRY zO16%JYr1S52UnHOzK-cLJgH+iVhkoJO9 z9ZRIzVnEiu5)JCO|69@h_H> z46)Vl&QT(9GT2T*(~Z`#@47bGI@a%Xu`OihJ>XC}?)&7?v5J0wVUrPbh@Z_fwT}H& zWCIB6j0U7g&Xt)lTF2Ccyyto}sN?>7U2F*1))Qq=$GtN(HZ|Lzj)z@3WHb|SWF1>S zITi1WhIx{6=~bk?Ab%c1VvF-Sm8xTr*w(4G7|LiJcY{q+ur?^EV{MYhlCNWcv1HR# zjHpMkPT!lNG|nP*+?0{6WBri{?iIBQv2{#c<=8r=u1Sp6@jKG$_#_(CvHOFYat3wm zdJ7rUvFk18((3s6b#=cgTN~IS*{jWM_fZ%9G_8>Cqpto>eDc3_%cH-9n=6?WQd7r` zN#O3sNmj!N{!VR{WL8M4Ks1>~2@4*_?X$Y3qop5bDtI}z{D~uAU94_B zEg8Wl(!`{yzM&)sEv7D^I3TLO71QKg)0uzXX(!54OiKB7<9y*Sr(q1t{QNMLs zTdz$QspxUMGe7rI_H_~YnU?jdVVmTPdIYTNPrzt00F&rE-k|1aq-Sa}?hWM&Vet8;|nnWmnbxggs30oA}u2-*37pdfN z?0$ZQGKe;awWc2Be&}={f#5w*HLMdYUdwla6D^Hpw8HnqpG`QFj7bo96fa zRHUaaner7={wO|~JeV`pq%QJAZ5rbJ(E#hrM_PmIBbjZYQS5PiQek(3)pT)69mn*G zZh$f%&+Md5Knh!il_%3Ep&iFf=cINVr&xJ1jS?0;jz7Ir{!9K%O%UoQejLBm|I0MT zpsOn_89`TXOk%m?xY0NTcOUQWe!Y8rj^Y% QBLDyZ07*qoM6N<$f_1ye+yDRo literal 0 HcmV?d00001 diff --git a/JobBrowser/doc/jobBrowser.png b/JobBrowser/doc/jobBrowser.png new file mode 100644 index 0000000000000000000000000000000000000000..bc099450bdf62ae038025061acd97f7b6dd20d71 GIT binary patch literal 507358 zcmZtNWl&qq8z^v`QUbKl0)b-1U4nZH6n7|EoZ#+mEfgp&!QI{6wSnO7ZpGaKT;BPA zxc9@&OeS+?GdbCF&h9?X?r%et6eO|U61_!0K){lg5?4V$Kp{jxK)Qc}hJb)*!&12U zT7c-JBKZ}eV)WC&>j8?{7x^y;2-Q)Ta6{DBV+?yKEhhv7g7^Qu5r=3lMGz2P@}?ZB@`nnxoS@{9#MQMM2 zY&}_kfZR?#z}EdQQ?)Wz9*?|67p*E+pgDod6ueR4ZoCV$ylI2|m}yLSpGLQgaA`ugPO+ckh6G?hP9XR0 zYVGBDPNyT1L|}-_51xHa>T{(G7Ez^Xa5z7XJ=odcBU%|)F{);cE!}ELFn6a zLl_o>)6`u5?`+inT_%CFBjbp9ZnBQoO0(M6$l3EoJwA5buT+04kW2ogv%D_If)NTF zZ4BeGr3mf0tfT5f(VLcwx~@RjvDvAn@ua)-EOSFQ_}QA@)sTjVb$z9Y$2>{QU`-d1 z9qUeo;!Z(=#TAKu@vD>9A^%tKk!0&q(vh-QXfuoe(5)NRy0e2c^bkDgso{UIigl_n zO!soz^#Y{@3U@I6zIeD)EOTy*=Xo+apaLfPIyKNL<9Z|+-o-v~7(o$%9nP7Ht@66r zG@Oq2Uy`eC;uj@RKe@uOrJ)S+a^A-Ju1X^tvNzD*`7}6q&*B$*Z6`7jtupt)Br|>k zkxE?WAszJaQfI_&f7m=CI6UH9|Msj95y(L9iOUUt45CYo(Ul!g2YVPlBIAqB2zk4q|;`pN*3{c7K|*=;;b~ zDyxB$a9)PVJg{^e)`OW=oU}MhhKhHy=&ei_R!cctc9f;H$IL!Soa~FxHU>(zsD2;f&%4MzsyblNt#X2`0aq6S{Gb}9+Fho z#G2CfN8oCc3j?d80xb2H>$=mRCx}HaQE3Yss~!2niBgi7t(TmwpM}p#5~KpSE5ZtW z_Ic?v8O#pH^<@`C`ti1zN<*wt=AtkzT$ca?2Lnp)90ns^ZG{NfW=2J=_kVC`aZBiW;CUY^m=5J~9_#o!SG}xMci_XOyP<{l zs~Gnmf`F$7E-@C7r|N+=hn1%LYGD-0*B)T7e>P&JCBQ|e>DZ6bJ=t(Kmbr&Bg4L9h zY^d@6t`q|*inq&pRFR7@03NL+ z54Kl>I1I2}DyLr@`tqk3WODU;f+#X+t-&HynOuz1d$%sR#&JQ$j?{nc&9d(~#)Y8Y zO&f<;Bj!rp@I8x)hoz}STXyw_Q+3@G8ygyRGxeFOfZa+xQK4ea;@GR^Wl7JF+wXow zUP^92gXagmV@9DsFY{*fRFNL*h3qV+FEzf`o!U zklQss{o{@#Q%_~+2@i~|m!r2xIoA3}Q<~fAdyW~}(J+K*j*P>!=Yx-QZwO%SEITNq zSce(cZJjupOoE)LItIzPQoX`}bMJUTew1sf@@0aB_+jbhhMXy+9#`jy1DpmTVY* zUyumQ=@lFXfFRafGU>|O@OP}j?C(WXyy$#;aEY&vXEYe%OAS_YB`qTDE^g&`ekHS< z0V&yy#4a0nVM=w@+gvW8SihTO3o=DqCe;RMPqK8pXkVLc*iW>O5{@(h4ckE;htF(m zO4P#*Qw65}z%clC*&Y&UAgQky5)u-X-*aFrvyOsR9i8Z^&#lijWn^lBLb~zhcJ$L@ zlyoR={^wtVwA}zkr0;Z~Ww!PRMPX^yse+N1jo!h(sdgRtrZ|8$Cez{X@#>}AcOzS{ zWRPR;6@HfY{adMlKl*nOZmlRF0GaUeFyQX#&X`>~{Q=8I|8UkOAy+aA3QNAMUyc&G zN#Z$Hzg%L&BzRV)QAlGsnDoHMTROX=!N<7Nb4zz&N5=eWEdTg)N6jXm$}bOA$Sj6c zy{uDIzMO9jQuc*Mx}Gd<&53kifRos5g3|=N6q36iPS-j`z6$#Fxkre+{LCqiEBxoR z;<9QOOoj!ba@B2hm2a?Jt$>|xQTL4v&S>aDwURXpxbGfdQ=Njx@t`2@0BF;p=$TMj-4&n87|E9E%74H@H*k6 z<@|io`Qm$jP?)*B*&nWwB^vOCSfs`6lqEYmJC<4&-{Wp0gjy^Z@J~PcD=ODk81c7; zD4@{dFu(%vWH}v@+x@w9ESa>QB}Rc!V)hfB^vVyDW$c(Wvrxba(}jab3{yc6BB*Uq zV!KH?t|s*b2S|nj`n2rFi39?C;#FE3EYAUtq(n0NR+}8)YBd|>TMHQ{1vms7?cjDI zg_`g>o;7jww(kB~d+WjuTOSkYIA}Q-&_?B`mPIQStpFwLR~Vr!$t^S3(%R^DB@O%- z0kpcECa$j=G&}xECd6^v8Ol$URkB3GA~LDlm{$1xccIqIWmx-svwvr~*-54Jd-)rq z@i4BG5WknlHA{3d-@9z%aN_w*d}r>zk6#XeN~^4$aFvlZkINFji5!WDhK|uIh9Q~R zGOd}h%}S%-`%9trV4hWXG5I0AA|;A?zsKW7%ZI?IKL7Wf5i|oGUNmnJPd}vzdZ?su zSrwcw)>%+8YE;Bmc0Tj>JkMH86+rhg{VGl~+`fLoOmQ7@(~u6nBQ!Ihx=`pE0j%v_ zzEtB8e`X8X;Y5J+p0mp}}1;KnbVCq_`K}l?DN`_V!Qr@kTp+u7h;8BBS$Z zRYksrrfvu@7>l@IcRX9{dg19InI#BOo>;RM9FbxR|2Tr;gy;I)rJ|{ngaf8uiNOA_~QTtq}%Svr5u+Eg#Q{0E|5#xe0|QL5MYDtVhNeq%gK}Fss%Vn z(4hvF%qKbQ`t)%923BR+&j92Br@>a8H)ue0mNP@p&o_6MVniz1}{f(xuBm;r}q_79n#WQ9jf&iqvcdQUVb~Y(^zRA}Z zg?=53Ci|YMH<4MVjmRU4RA|~{9J)ED@8@Do>izO`yZWU-wNODiUnaq1;16)JSe4ck zfpFuvac%Z28Dv)CA#hd*@;>h;P^r-6tM|K%D8$t0^!4F-VsA`fz`#n}{uT18lq5aO zjK(#r3n1WjBsaYc4nEd#Tl-jNoN6W{_S|;w(hh!XIL>$48*O1x1yaMqrYp2`@=-S^ zgCnC|%a^3XG*tg4tD2b{?N|R3Zo17%<8%1-Z?>ZJ|8O$z+81xABZ zY4y2H(uEG}4ylTk@@ePd4pbMc`yT0UTv0j!;%C0B#n{D^~H`ey6 zSZ)Hz$WO{5UIp)j0K1$_acp#7b%eZbtff=A%O9>!*YxF63WpLHy;i)PyRwF9Grez+6c7qJnpg71{ayw{<|9LK4qDPwwt$=Y z8U;|qx2+96In3eSWuR4U{p*u}r`+>x=Zi(#n(vc5f88PZT!miT?JC!r@ih_2{^3G< zVcX@6K#lPuHp%oBkSy|!2jdnXAWd9YNn$D>mYRE2;P`32#`E+L8q1acDP0^ z5U9_}&dq7G{br%V=QW&*u8~4Z;yRqmpm0obnR<|Ku|t8fRjr90KTkZ>7pdnadTXo; z{FrKTo=~xvDEpP?I|5>AHyb4NIR8a(?V;xH6nszk;QMqvm#+h{<*>UEoFNviXPBAx zzD;+_H);->Y&El6fuq3x0TO|e`LcyD1c#&LiCoE#ThV>%>LJ20|6Jk~kQ){;r5@I$~Y0cj0CdJN>tf;fz{efHc-G&)*J6OsB? z(n2J*o^j4{E?Vy$*@r(su5Xu-SlxgS9FlM<;IbTsce2`XLHXcl$9p$I{($QsdL=@e zBx>R{o0iKnN#Eq|OmhXfZAA_$s);lMZPXck!l9B*2o~sQzItpJFnw}kvnU&U1>7#J z?3iSE%z6$oxxHB_5hD>^H~gD0tYJAN85cZV`XH&kP~#B3=w`cA{L$caw0$z)n1Ijq zE=lnA$myYNwp_b(x>&h9c;Jgg0i~#CsjRq!Z*sQ7)_^v7xO^&iF>vOfNO`1z`h$1! zMqHH0#Ev#VSgXp;zse7ONp|6~TS;v?6tB{0>jTYX?F?SZKz$3`Wmom8FLtmr_MUhh zUI1c%IUiy+G)nbeQ+!*hwsLZNxR#CqxlvwFR(Lpwd(4zPUCQ# zqF`~@#Wm`rnXYOz3>F%I1DB+t86!oBzp`R z9mt8nm(C%rE8Au(Ek9R(YE-JM92P!Tz`ZYj_W?u_Dw*k3^VE#GdTnl9bi8y|=6zOX zNr~MOVOsI?BoZ43md)L2?EJmAYd);>DtY4-I_u4%>KRAN2nW7Q@Z9NQb;;wn0Yw$R zuE??@t=hv7L~a%uah@u(#*B;YI7QE=oX@Y#>L%GtJpkUkJ(j|gY8tJ{iPInUQB}ge zFZz8AgF(XR$N3dKGM~GccqTm+^g_a*EDJu*v!atfYxOn|xXM2QpVdG<%xUYng7miQ zSsf1FBX=LgZIIOi(>-U98P$Eq$NdnRsG|G;bH)A8U2>zYM|OfLb3z4j$NxlK2SiPky=somld?WG0fGry!2}%{3MEm zqLriu=Fg2q=wDsGAcGdqY-uEA@Y^lT@bsj;#_Eto-fC$dibqo_O+2Pxf3{0(=lShB zmI#~DfKnx@83?|&_7amB_LPS#&PMAA@ZhKmcMN&jo``e2xzl0=xwj^w{Sa_buT>3E z7)nFGAyI;g@WfVX6*tD8m5rwoIR!(DAoR#*MES3}s#TIWnK4SeM3)YUi zoTH_B#gD1&0yjeqBTH9<3w5@EKn3FS%qIH3*`lUsLSg>x`|7&&q z_SPoV*WeVfzW2?~B{%;S8N$hV!NoeuT-$i{W$T_9UIpsq8pCjF?M7&?eeeQQKnA)l zn+UW;=dQeq2RH;?2A)#gDV8!*If4UpM|a$QPi4@5s6(%iX9Nu}~k{(?)%60T@3$0Qjp1(1KVNmTeG zl)Zi9r0TrU7>6H54>)k?ZQH*3MsJk*30W~5g7)U)2pMYf$%+aj%yD=J1Ntuv21H^z4-y|g9da+*>h!1(Vb}YYwxkUj)FjvTE;w{ae|4IH z=6iC&AFOm?w~5M5$XEE?{X5v+7#aJ~SZ37l0k=1TFga_g11d~Ff;pw(?C@0f_3|Bd zi2eXod4=rloGw<&uH1+j4B`lo{XK4Q@L6e$C9r_!yR-(4p+2tkfA}~<+B8||r@Md6 zYda|5a`oF4aX}ds)b@DN%3t%>oP2#pqgYp>!ES+tVANe0&5C3ol8Eu?@n){!6O_&R zjE!XJV7em?yJ(@#Id!7>+4{b4RCyhJ=bk2tTWn0^rL7T@6i>n^I2~PxHmymy(pKno zAS`7CnT@>WcpA`}{$Z2-=xhexeY}w1kbm)vn(dzTDOqA6*c`6QnuDgCdkX0$uqO5z z`|htkU+8|z;xkViEX~?z9})?5IUV8h>wgmd2dpu*_9w{jX_87V{v3|Ix-8(qHeS2J zvPKsZDj?*`*1LN78FldY2U6Ba7E!8*P!Zn{=u{uA!bme|?GDG~Mr-(obaT&-LgJ*B zHj55%_~3cJFI3I+iyP!I@daY`i)}S^J`&oj1QtzFd6P^rY6E_~(o>x2t>m@fvDo}@ zh$nRrwI{i4qezW|zn!L<$+bXZX5|tj+Tit0JO=!g-Ad)QX2|q8PyTRM;cr`G;L6Nx z5Chxj4aqltjErSaXDD8)>QG6ntd;6MeLDn4y>yW#w9%6@;KP69m5d@`UVA!AZzz_; zA(_EJ*P?MB_U8TjP>VCQ`0^G?4xwa-c6Ej{q zXgK$DL&Ic%=EfR=Q|imI4kq%)>AwpVz_OjJYNEYsyLbJ@V^|S*j=ni%MJYVIvOLa) z{*!FpiJSvuEns4@#!Suv>nfpb2qa-YE^fy<`EHP$z4Kzk2L*crv zEqO=2xV5YTz~Oh-4N%N?m+7#(Y^U;LZYC|5xs}nkXzQrXgGH-sc}Kq>bMc84dl|SL zc$d^(HBU{v(rSR+X|6#W({Q1L3wj{<*%DTo?cs2TZw5*r{-JP8Kbb!s^*#Xj z2ihDbg@o2tG_wDJc-A^v>=EZ+&_I6cernI?q1z$k>QEg0k;MbHB{7s>XI856higA*99OgF0z-ifq--f_ zoaW7)`OcW!BAivJYWkW#O5`hP{9un_GB~Wv%x-n@4TFm^$uXvKazda{3QDX|k6F<` zyh6JlL{0BWK;#rf3zlwjNnf5w4gxxtNbugXa>Ld~rrId=!Rw}Dna0l4#;Yt7f@j#% z#~{CIpUay!C-X{F;Ps~Ur07<2+v3`m$g9=~MbF@yc-t?#)V$NLt49Y;`B5n7v~dFw zWbub-CzkIVDZ~k!G&kKhmVTKArKV@*^aUyS9q++CyS_$94jfr0F z-7+9XE8!ALa%?;k**@q{xH|X=tIZofgN+9<8sO!6I?)_@#8*?AnVw5-TY1v%yg=-P z=dUc8(QR4rC)0E~-#!f)<9F0m*5F{7>CI6F+~9K$VfMC15E@`|Ptqv(M~Gav9pc0R zz&c#<4V-#JeEygVukA>OLLl5yB#&CZk=jpoJWHKrX|V`5*b@?H>4yJ!Pq!@u+b-fp<1b4f2ydv7{u#kXDfuV<>n|bN^e(^; zR@VqFEKQEC*WtBjIDy6YDfhEJ)L+vqpNniH%uJZgw$VI<{w{jx(|}DX#$+_Q6|?;k z*M~fH8W2FD$t`8sJr4bVulrY1Lvq&`Hz+T>51@l1v64!slK0zpemb*o_A*lWgV)a+ zB{Xxdx^l??0Q=?~Tk!ji*baBk2%Pro8y%HWFtd83?MJVhqlx10tv8dUprn+tg7D>$ zpXECa7Bo!7olr7R2{^E=Oj>Y&CC8FR&wT?GnkrCfc9Pv`b`lWQb!6P(eSal}xT%oD z_T_jO;yK#CD^_o@unQh>v)^8Se}9)7<-)0wAGHxQDFR7cthYQwqyT|O$0c}=;wf!;YSq3`(}We`6RD04$!JkqBqrrb1vFHB8qlC1Xzl39WbQ&E#P&I!ooz7)~w zd4HY4YGM~f&(3h)!JwaP{;ig#sr)Ue6C&&`ZB!$TJ7dwsY4X!GnrMb<1GOEm=GBu6~gZZiLoe9*j=7GV{gE9N$QC zkA~$l3i^(&$Icyp)(XI8)og?ulP4_aM)0OvBcWR#}ZRP z(lW%u+@UPL#E`mAn6$#0LSQK;>r-(Z^FVwkd$N`z9J<0GcD~wQpL}P!&-y+kl=FU4 zs{B_yl~2soYZDhdn@66scM&^qYtOv-+*BEG<5PA|sc_G}Q*X^@wHv=`*lyZ(e`!NQ z>aqJ?{_$)Ir&PG-v>RTwZ8qfJsd)cmAgS|p_LD*d#O__RNBZX!)3?&L;Zo5^csY1CqpLeM|HnaJNu#1aqO~0^F|HsUiyFvw}AAk-z2|p$Vl10~X`LBIj zpMH-CKgnHlrQ~mle7xQ5et09d>TLW{0!=iL{^Rxy>NI6u$UEYf7o|q#GiOj zklq_j*C}il$T;4=pAYy((P|)mf=cO#EEJ}*SDT*yRD7cX)&*isxp%KP|EyKgP(1A> zU=tgo6f%}a-ow5+BqR9eP!pp6zsJU~Z(0CPCQb5-jj+#VogRLV0$mcKNW)~c!AG`< zJ{4Z!50@fLKE3jSzrg|h?CX6oc>mDj*bBPaP-PdTYCWF7BSEzcY+4y1YU|>_n*wp8 zV4d_HVZTwQ#IM%f*$Iu9j6fv>_LcZbW#MAs#ujJ63FZ9Y0Z=T3-xmDh9EVKy(q=?G z9)y1`ovkPsJ0x8y6jS6cTgmD-@8Em5cM4Y9illwqSX0C_Eoq=k5Y}sP+8_^!=N#)uLpe(U>q%#$wKj8_Hgk}MXkZ1FxnBzV2g z-L$0fW3qlMSl@Zhd`*%NUrx2XX}Jidr@ITw5^6046r=okpDJ%P1muzB2HMcclJo{& zNS`t}dU@(QV}{}wWp6AO@)wURW3QviO8nTO`>%f{51GX3);mHWrXACUdiqcUjQ;WC zoBX66=IY3a<8SCyuHQl3N+6Q4#9dosjh;yBidbNe$IW>Ou3P z2C>-&%onEqMK>{tR#&`F-G$Jkru^k`={)BUyNHMG^xx`T!Q|^0=%A|9w{qW;cfSC; zz10Yj-EP-UCQl3m?Cg3J4Ck~|Bs**#%GFYJXc zn*q8nd}`}QuLnghMsU6~42Osap=}nKyt%ns;N&;8IqK`b+TFNY5VRLNI;we|P&NVo z2ESb*7*zZOv79MA=uwEg>xSpUSUJ2#t!cmYAb|-vhrJ6lC&#PBo*o86ckj-_gI_02 zVe_R5&SR3xKv0Uz=YW&)3zuQUr?(uW~3pxF}rA+)zzR|8T>!+RT{O6$ue$F2@ z-?@rw%OVw;t+Xjko!>ra`z5@Q`UulUhcn>N`e{=43M{O}1k9liEorSKBF?2-IN-f( z6q3&7U;J9cpWN$6yBC0|_qFk;+-#e{9a&NX5T^2;+kKOy7D9(--dCx z!ZedXUXi#V+VHel@p2*IJ7t>?8MnpV9m?vqrE&0Xq!0!XY0R8yquP&%G>4arQ zHfa2aZVHSb!p)2WUyVl#h-2ElSV&jYDh5n6?d$Pd>A^S>m&LS#9Y}Yl`*3XjS9~8d zfE%Tt5$GI_IVO;QoA!G+B*zNc6|+JdU=$k5YuQIvKC^5iKZiSB575NTa^0-SO(jOu z>g?K}y6NTES8G-=jc^<07n76S$?eh42VJeM2+aPRzu9{oYwJ59|Bbbc$?IEUa*Ru+ z=Ce38WN58eAw|&Wh#(6qo~5%DTerKSjoi9}YLE3XWq+7zmf1YK>NQ164>{>rMHxYn zq2yYgwT&0@e0xPCcgS3R+jzr`$0TP651_CT7d%lY6!4ge#qpI2_Kvu;0L4^F3ws>> z8dT#DeD2w5asMU3!qh7ncJ`-GRhXIjG4X?|yF`6ce_-x2&7btFa7~~uc%a56wc()~ z0n;p%^Qw%?e_ncozyCnB@X-GwZEaOlEE`Qm^ry5_|5U;G`fuJ>n30?&PMqN8wV~ql z06H{Go4Hu6k=Oogg|}C0kImlCAiFO;*c45ebZP(qA72%l*;R{66wtA>3*0f2qv{3# za1%(?v?U3XrM|Y+&LP*pMB%~&k90v;&Fta?Bbyp7uIi>}DX2+*I5|=G86sx6Vp;ZD z%(Y5JOa6HbOx(nPnUgJd_dlNuuKQzKQjLga2zodpM5XY%-RPR)7gSsDBjo=1Lz#7_ zb+%z{6!w872Vgn~)Likl(_Ty^zFarFrO!fzS#IC3mdmQtYEw*N#rU2^o|Na=d*H5 zXFs#Ws$wqLN|?D-qwVj-L)X?Vt=3A5W820hR+>N{A^SB)Z^KiJ7oJax3&bP@$Ad6e z&0?X^C7ibFOTA@AZ}Gyt7p);R@=%r?DB$li`5L;4pSsV-4FL86*r@2flvqha_~C>S z%h)p|O4-lM>n%apCEO7bW~y#El#!beqIY%SpW5t1CWXE3tD6?Ho40yxn|c zwFGs)1rGy`iiRMxneekGoCo5&H|h);F+Q!g2kUspUbieOF5()7ljmQ6Cu5l+et8wt zn5LSQ>r3@{k3&5=OZ9cb|MsHGo{T*n~gTH?Ud&haa^1Zui5*>1{a;5i_y`Xqf!HL zvAk)l4Q@d$zDe|75QKk!|d0?%z*$Ag0OC)8W@7&!dhBy1{9xN0n z=%sQGMBu}8KEJNf-w@-IL=bX+m9UiSE4H)+iz59*4Yo+;v00=!oU^@<6>vL=DaNCH zz^bkDoU!a2Yx`(4I=~7lTXdA0MJ?x|jCYqx+%rsMNGckFx>6n?(j((~L5r9A-%J&XU`8%Ug& z8T(}$n%)Mh+B0c_-wd-(7Tf4x@aV>gpUY|l1{oiBny$1+`Oa17hOXmB9(KQ6m^Kga zOAq=vk5eTB+mv2s>LAIGT#6ns{GmTx&5lCTajRZlBrrD9WnSpb_1x;8IJ)QkqiW;H zut(0yA8`-$R>MwXf|UK?_%m)9jFQ;brxW?%13Scj^JO`VFH8>RY6~six)tFz+3v_- z;edoXh7uT{9mg5{RRjuY<6ix59>~xS!jJt}J>c#oN+|~Q|^oNH5 zjBcp^kRVhKkCV$I1p6-3aV9TtGhFHz4l%0NSQ^=$6hdxK8uQAl-InZYWM8FmxKNt& zfBG5&Y481ipN&|wt^be0{{MYFX1${grqAh$sADfBE_Wu#Y`8A?`4x7bG;Ej@o;j@I z<_w9E;S=y4d`1Kt=%XwRtW1=>=7!K%YCICq-FcXQ5;lXWMto-~Tu`yqhj+olVZ95{ zF4-d!e>Tb<^G2cR%ctM(b@T~en!2S45M7-B-L^Hpe(>{*8aXtpYrvf}l)@Ep>y zq|^PQAJ7;rzTvS*B8dy4B7aNVk~-w2!5+kXw4$^>5RC0!<|g5R2Vb#e6W?u1TFRFT zUZ%79tKZgyyCZA-W*LhNi|Q^UoK~D}&5s0B-OF*jg0y=0qrd4wIymZ-NQS|#Cz?a7 zlW&PSG`8pzRvv5ps;!aa`E~S0NaHkKEj9Y|swvzN*bI-Z`@|-{GQN%~khitpZ|Qfk z6HhcZ5K=`@uB36Ci-bK)eCop#$CCkx%*B^!KFYj%l1aI;H`YrnW5qh72HQO=Mr`|;tDYvOi3z&)HdvYA-YfpDh z_iS5p97B0_0@f99-SXnL%oEv>pu5lncS+2Kw6n=kJc!BjF5YCe7!IHB^46_Qikzd7 zD366kbxq2S$QT19!Ev0))JQmSyG3J}uIrDg!WWJZIS#VClT07eD|mX%$xo?*BUF=P z_LzQwUaA!WJ}UhUeUpQX;R~w-ni><0CTCK9NnZvX+2dS-qxi&?!69TE;WCerHQc;X z^S4j%|46mcZW2Mnim*w|N zFG|EznibHKO?>-Wkh?9pk2!UHD_8V-J7iO}W?%HCl`zCaq_ujtonWmp+%xR^!gURbaBsS@WF6V}OHj~%u;18M3efG> zw6HsDMWwpi^0-Dlf*0R-{7PzLNofm|*Bj^~ai7TJI$3`g2XP2Ns|5`yy^`xzgPvDU z?=JsU3@@n%@JpbeesH_tCTW@bm>P$f$2$vVFgNPN^3Fg=$W2@ygxX_*JQ#?INyWq| z*YmaK%Gy`~4RJoZG+Sc^M<7*n#TF)YQdR0W@*yl;#FD5n<0try!1K|kNqbdueIChO zM1zZeUQY^e8u2o;znqCoK<8;p8GEJFIFBaFz6pbg1C^7;KNx*VZsm}hg8Ob zr>vh}{&55R)7$KgEwx`ShX4+*$~*@e8F5bq*>cOPT&AbgS*_G{P3lg1*x;p0z_C5j zm9b4mY~@KKIUi6UB8FgKloB zii^MtpY=`eC+iY7md?E2G{_U(_|My`2&hZLAf#)}R0ef;h);&MP#{lcu zA*8YI#U%GFN^~*gc-QUUDp>6ik+YoCXdsB8-+r35-4%I0$Eno!4gwK|06yiws<_-s zPq6*AN_nn+XI~f(AJRLdF?RF4kN?GpT>>l15onRcVPU>$WB7e0y&F|qBEoc*ouNHg z?3~Wx9H#VKS?gVXlfLBYIR-}HbKD-EvKWjYCOFvk<4GhImcoC9VrFA9HSET?LZGvn z_XkOAeb8K5ED zIkpLk**k;b%P(d^S3$M8yi@?1?kFcFz75jA0GS)cV)TG60NWo{5CF(2j)OZm8iTrT z)X~XUfag8O2s&!cT6{6R{Ol77Nlp6g=*SY&S=IMVpy5~v++~~a?~Z28>|rw5=}UFN zwHFqsOypgo03||*#ENtm${uT)hV9GoTx#Po)%U8MA6C2RKJpG|=|9)I3{2BV(8pwJ zjwO2{{(gX_aJ81{wM(}+AJD&gp4q=4IGKrPSSdq83B+OJx;&x?#@;7 z9JY4f_iUw(X7Dkpm#8Zj_yf<73l-51xS#Agpo|_D+mQZnWGdqU*6A@G99aqqLn3Sr zGw$QEOx{K@-CG~_{TwE(BfeuU*xg8St<>=K4kV+sO5}+%w^-?Ox_}#mK;N7G>x1mK zV|ty2Kqmc;Z@%zq&AQGV*HD^#$uZI)0D5FFF_`x~3pzH*Z2sK4!YEBIwYuA`FP+aA z5|6iJvBjzd|9<^o3+m(z4Zb0Zer3=@umey6(0-}v>G4+b&m)0qlOd8q>{7c^<0oE= zB*RX_;HJP7Pg-`Te{vXtWOT#GvkaiN39;=UVN%aV$e?%Yo_y!6*IHdGeR(z~4J=)l zf)4gOBwgT!<^jBUbw1@dFrGyrwnGK08s+0jdCcWKPdf}de)gx76qI~)_~lQ`Cf4cu z^a+AKRp3!^_aE)OE;AAJUDg~nG?0uUohgml_}hd8HlMms)WWa&mP~QT7NgPf`)ZAR z(lYt4aPAFkfwpERUaDi_b5#9%KH%pIYjm8rEA2zWK%B?0BQ6Ir+v69?0fHYb?oiI= z0(%xPol=&6N7Q_lcqnd-?~^CJURx6n)*N00A^X?kb5u`*VF>e_Q&A%cwkult=9|bj zZE^lUrN4;+u(Jw}jnL#1d{kU;)!Xfn_kVdeD{X0T1x_$tmq{1?0Jl_a_sNa@;U9szlLRcQ|H)sEb67-X9@;#%$3Km5Kf4L z41(=0e4V^v17vwlsSJYi9D?sc((}>YCvD)8=pvtdG2!LnCkHlFD;(2w)Fa!P)*IL$ zk=tGL`7k1Xf8|(;X3gFUPJ}#-M^7`aFgl`y>se)!g-O3enYqS~3Tl9E{Eqc_v0k-R zWPTMz2qzo^kETc()NSMXJNNaE91PN{7Qk$H@SdrVt>~At1}|3J)($?3x@kjQdPe|B z9nOVWW1x@YkX@E z4O!PY2U7xwHz*G}zgR)@dVnCE={~JmFm!(+_xe78K|R)}Kg{AzANBFQ0h)esj3#s# zkhV%npxp>g*MF@752(uA?QwR+4OMxyrT*~WPTmxaVw^+np!AZ+l1%ID@ztbLmwJEG z1d_o1euu zS1RoVrLYFj<#<`0-(^3~*0v7=1AC%(fs#IYqWq@=GW4CQ&C718omw)=D`C=@!or_> z2)yHtAhrebx^YO6`fWP=5Fd^*qfwocATroHL2Hql*HRQs5s?3GmgQ}`URXe{5dcSZ zj>u!C6-A_pVCMPH;?C50NNQ?W9G#qG2d_DMUep`_oY~J44j&>BVE+14VDrhm(5vGm zjbmgqK=j*>40LYy$S|eS@63ZPcua+U&mLp)Hm{4l3C~ZDrRi-J-_0G%))wonu0`3F zVc`gb-NDA6FD$~mlfM}1p~&_yX>OqW?&T7tvnq>hVBJ!PO$8Rnrxesb)+&lj#Y~&U zdMW%iv>+NjA@e+=(6U}U6VZ95|BoLJl6uMYA~sGbw?f00{-I$J=9M-O&Ds^w&MKyI z$PYx3R5~0(K_VZYq~+0`>tYFa$%Sx@k4x%TIlk`ubQ1(#@xS@6HF8rb^bMZxWh!yH z7>R?WVm|-6+MgA}KH|%2EVJLi>B80|8Wd7EVaJ+uHWDn zpjFTRT2ab~)O3Mk%tsX;C^B3$htz)86aLTlmShZ8!G&m{wSD-_T&d_G$Xctmn9p%u z>YnX5#PWr+x3I=~f&=>C@?H8#+wz|Oa;~K@$P7JMTfg1XC7H`7u|k!C3@vH4@*JQ# zC1(%$o(Dz(H@TnZa9YnR73R8~t}j6)az3K2eu79q~z6B{@S7At#M^XEc+Kwcctb(bMJMK?Zi;QO`YQ z`~3qh)LVrcB$?x59&QiLPI9`y2o0?XuKxBRwW56KYwM)Gke5hMc^tu=W@h#(#4stb~z z&-8t;sL$QFG)mTMSM>)Xh;%Uny{`lI=~x`>Aj|C-av4}FO}p<8X5}?I+U~C!?-R#J z23uC1qsy{Y#p8?WyVMO zHqswn=A?BiZQ_M-Nb?gC@#Q$1Vi#+<|f2xb{dCtylG&Cf(AZJS7Q{-T|e)v|) z_96i=5o0>q^v)v3%hwvp3Q{u1blYC)H{A<5pi9nmUcpvYND8iKB>Dj{9W z-A`BBi9*bJ^R!39Ayh|3nm=!4JoZrq&-`$T9wX+2!Xsbx!tIJ%=GKM(h8w)`b+eBx zm2^-GR$c3xp1__wiCTP1^u7pY2`E6$X;M)l`wJ%?kPR2Ad@T|7LD|i^JCWokcum`G%DD_EY33ns?Pb=wE#4~RJo!INon=&1 zU%ZA@kdPq+X#tV$p&LY$?(UH8?(Xhx>F)0C?(XjH0q*gC?}z)51&d)F_RQJmx1aZU z{jM=r)9BkLx;cBT5aVeBNv-(%GV55 z0sb~E@o1qfyHCe8TV>WboeAt0RBjKYA70u6b-G-aU@Tum5k+QIiDtg7#bP`m^kE!k zDEgx)mz6lz5ToAJmpI~^JO~360@123-0f{Oz-;+HUkCgFFwnoV?~SHhzXAJ=*f^8< zVokZ4{+SB1J^qnoPF|S>=4n^CQVss8#5NwMM}Jig*N2LcWSyx1&1kx|5n?{R&Z@P* zvpt%(SSrPlew*IkpO*0CnUI-GPB8Xkar{THbF!WT{|p82Qy%;6_T{kqC^sDA?&ZT1 z1Iu##&8TfeBAQ?-Wet9}3o%n_55Q?DH$17bqnbu!D{{1es1~Xd8d6EfhEU99?3 zyDyRrp!&l=0w&j?e2u}f;|DgQb7;*|bzE7fN{eG_+r00zYN#8O zEiT<${`pX!+>SNM5B4`{qAN@44H#gukIt=Ad zd+Lm_JldK@so_VSc*^Ho-FL4LLIe-(Hr?iBD?*24B}SZDzy{Hs40GQ)EQZ; zI@nA1er*Ym^}U412^5M)8{t*xt|J^4rv>JCdaqm^lew}e3>sC#@@(N3Qg@PW)jgv2 zZPSk0o&f9S>@43%kom_c^P^2t=cb$yBCthAi+7p?>^S#xYH|y&|6;9vfG?0x;VFD2 zi*Sj5B2<38Uw?C&o}|$y5ZxnO3k>4 zg~6j2xKytmC!tlS=3y#=)+~1*&m9M7MugHx>~i*d)2GfXIdA`Ak~>9dqDh1=5r-tV z`-QvLQyIl?_hxMg03I>KxwtmjQ11E=3<7(Mw_ms1G|x%O=nGNQV$R3&Bs>XCBZ}v4 z^#n&Zo0~iB-5S)WnF9$$^X&b;*38M{tP79?hG;pHe1Z|-11YFXjH149w2?S&3lDKGCB8c`FHy6e%S{K%xf^O zrh@Qm_uC&gEE7Z7z?8u1jrL9w3CfSWU1z+_lo8qx;*5Ld@o5{gWTi2~`^Et*LY@I3 zaz$~IDaW3qJM~RAWS{qKGGa9G*VqaBEw+JV#%(y?yDl3bNjtql$MyDeC28~CxLq^? zeBI^UBn+Iz%b2UNAG%jIGcC^jqLe5lU-fPlPH%DoU|%W;qpcp@Xc7|)v$u|>+v6PL z(ju7#AqW_XEa&kVE)p?>Hk^bQZp8CY!e9`W6>QyRK8=UjoPrI`&dP?3c)E=UE|^Ue zLJ;#{jIzp9jRtl5BgK5c`y|ABJ^w6X;l>UQ$W6}_P-PSYyGSq$CeJd`At#bN9~GC! zE?9jf@We)0gc{Xc<~Pfo@=Y-ywI*H@x)w|{BOMB-JVp#SqAe~a{mvtrOFaaln9hxL zuxTs)o*wbIk}^JMd42!;kdCRWIq924a`4UX>EiYLUf#I5XH~&Pv4t{{PupEi2=WYn zZyKB9TL_whDDMy>K}l^RIog6q&Md5c&`y*XKJq0iQxL=hzun=OamHh#FG8_0@jch3 zNEd&SaIKVK`Plwag|t}<+WPy2{Aqi7P_U|JMr*6Zlr~coSGMk-Op+5|z+=5EUCme~ zQ0`^o5V6$PTJ`t|1)P69Jj0=U(we_k&pnT8bMI!4onDGU0z;7=f(vgK2yu}=esPG{ z2qcy1>Dx_WVqI7r(K1kaAMKUiWi!rAR^9>$2A!}@ppCzy2#y|gLJ&fl(`BgCo&N?z zXqCkM+*0V(JXV9jMUTo$r4W+hhQ-odpxk(*0qtMB1|E~Bw6Iv_XZ9JX( zZRT)kvVPD&O zS2Us6I*7mqr9(hY-X$Gw|T z`WE|ycyv5r()x6pBY2L_;JRRWYqC^Q2+=rnz{@v1po+u5l?*&=$@om@)HXUxxhD(2r0>NZw=@fqMQKANVoF)xKC(Q12Gio zGfdCGaqza0KjM9fbb~yIfPTeAhszWDN&Or%v#xnC_=G$K9#gt{brkv#{H%7@V8+!) zX8CMF8YnuAD$mF_AKlw+OS70A{2>W)xWiGs(8zBf$pyuU@*C&N!21^%Lo9;OD6Ql# z2zkA*H}}*+Wq=?Fgmn-!JVGlj`j2%NTq@7?m}Nuj6PIEQCK3Cw0`QT63*{3y!o96*_yyV^JhibpJ+b+%!k}a<0qfHolGS{+4)^)~tNeSHUqacm@l7rGT z0bH+B+ehd6Fe3#6rEGovf3msy{7}&ufg$QrWiS4cm@k&qSIL=|R3lQz!+-x4dz!*k z8-vl?yZ~@r&zjWbP6tsHVTvt|YW0Qvmq?GT^Dc z?s@y!Goma+4P-o5;A9~L!>NGcB}Vt&YMdJh0B#fZmXihH>ZdY?lW2{AQ7e;69 zj>RXBp^A{^i7!8j8Q3{+&(CfXEQn8|`Zo{uktS=FPiP#4|7Cm-f?cv&I)QU9VwK$>Lu@Wvtldc=&VTpRQVOR64za;p9F{WH&b()iy7NYi^6! zWR%EZ9B{UY@`>U*hh#C=RGIwDzHld~W^sCyfGA6CLz{O=QLjCEa*s~5mwHV6uaXZl;tauaKCfHR$OXVE3`t!JT6pjMunRAC%QZ~M!b#vOpx-+mRLqd` zA9%14z{{Zo;6oOKxBG=R;W^%_A);}v@7p6808B=&M-QZpTW@wW-<}fA8g;Eo`xt+W z|9MYa-CN@ORMz3ZF$3vA17HR3k9Hm(&9^i`Z3XV0W`1{2jKl0MbEd#KV>=Z&iD>K$ z+gF4@+(HJOBhX_z?5m`MZf_Dckow}BjU6x|G0m_3w32WKZ}kSh*^NRgLifOG!SunFFjYX?&|_Qx zc&*&vGl128?lNQKll2aoi})U>`_Q;Po?LdG+h(j!og^)uQr8=faW#mXA(R4TwzEyZ z6OYu-Q4sVYB&pDp6i@R2eNA-rb22;>=dLMl`0wKQ(wTJ$eiIzbmFa1%OGxkMEuEzn znoWOg>J=x$SAVCm^eed<&LPn0^6#~Lr)E#=#PBn>hWZ&*Wyve%f&CXl3%8f}F~yeG z-QzX=4?)wfhAy@B0ONa9UQ%wK3=b2k0Gk3{aoKjb{IVW_cDESM}ef3&~W^PB5i zdc4!jiU*`+@Qi;H*vf8i7jRfj%$RI7be|q7pEe(5Mh$DG`J>N+>p3BTQ z3xz!&buFF;XLKNHaO~thRwSkgvC>0V76uPkdr~?z$qRkZoXOL%2T7ucWvl;w6l*O)?< z{@#pD@G6sWqPc_Mcch-_oEh>%g$bUL0GuhCj|{-q%cf=RNRrs;SN(Ymb{;~64VX-M z-}BKy&Pxog17y@LejEoA9|?1V#Pj;9Q^=N=#r1Y}{kco^{8k9MSj<{c*FOtejv1!m;_vQ?!Jt?@G*#z`(%w>Vnd1(!UmfkLu-7BSawKI_PyvHR`yo>%=s z#35pl|E`)rvXlPjGONYBBQnp!Ux&?Og7&veck-xE&I-hco784ckMXngLqmiUAW9Hv zZ)Oob3U=`F5VMs8cO#s)ejCqs*GP9cW7-4!=?Gr^L%qG~xNS_+ zd%Ib@%p`D64MVLOI$J;TZzSQ3uzuD59>jCF$z2g}aqO^q~lgdn4f7J@PoAY@Q9sW@1^Os$$nGfz5axWZ<7|KbL0AbIHT+1Z~{k4&_VyNppx2$y_FsSYMp%i)JK0r zQ=X3sUww2^B;(=aM#0z;`sG+fY;E+itZ8)wSiQgSej9Ys0tcIYWseYhzD!5_&0Xt!0{8a#8QUtD`(;<|lbJ z@T#PQc;&VdMF!seJVl^j1nZ5q1zDuHQ#L`Lihx=%7Fz{Bi2Ypz34 zx9p#lIhp5=KEw+l%abeet(s0(PksE}_Kba(v~X&pbT5=(dowV3G|sMk56xJ^JEg(w zM*KcTeP-=d@p*Wgj;G87QyL^rCJD2G_4l%1lL?u6zB)uT-SHA0{aFac8FuF6WVX6^ zFr0Gg7*rhe+{KyROH&e|yGEq<6w5FIaZ}phPpiG5HK!lHS6}I>bZxphc|zoFPU4Pw zsQfgDB#4%N7Gt~A{r9Cmyq(eqW^T2_iji6QuIv$ap{7Cmo#&1DVoR>M$`Hs{&F0(b`wUku9xPaw==3Pj(dmxlcbub0A=+}N*kHzB4>3TfU_Cob!q-=A zmmeN|a-}D14@o3GHau~;6RvuBCRyAYO>dQ9HW_H+cF9q&8?SRw*kCcT-*-lRr+VRiXEUd30s-V2;vGT{+S2C3>UdxCgwOoy=Ae#)OG9& z0^1eZ@ee(}ONg+m6i3TOy$`X99{HOIYLpLbdb3S=-sY~G4EGAZNFIL2?85~U2)_nW zR*cwA%|w!!YcS=vc7p0mgy+rIN}SdJ-^8M7_*eFucgTB~DBI{m&bCk;hvx4n{ut1( zF9pPCM`3yDSpe3$ghSapldm^OP4N-jPl;`G5`T9e*M5Wj0W(+5$PacRFjRiyBARR> z`DV?R`$I}!Rn|Oi@sHm@Rpt&VVqGiwchZJ4P7NN zo%T&aF9Ol(B*SeJ43*GaDHRYm`Fd6RW!i$U9l?OEhk$(w;kySN<3Ya-4~lB~8k>h)!iLAPmx`0;DUSpw4Duh5t)U;RRI3Jz^E9~EfV7CD}KcH1jO z%|Cn4^S66{tZUYx!VTrwOrr>0A{FEIX7XXq@ZrdFw(3t~zh-v=tfA}FVvX-|&4Mt$ z%GEcz^%h~l2Jw(~@sJFf1Wuae!6Zj7B?8C%0UkzL+oK+eG_fXJfL7V`m+i31rv4+D z^^o1N?wY;FoSpdWKbp=V-!+yIvEe{NOziy(M>f&4GLdRDy%RmTT&B)28n9jO?Cv?z zQUSoIIGh%&y#+^It*6SsTyKT%CgKnH_q3J9*$q7I4H6Z?Kt&Y-TsXR6@*mVRT ziL7?^j~$@F*GEa=EMl2p1UZdWeoOZ`^^f@n_n_2_xPpx@r|DkT)u>2gHfPvuyvJqU zvNtX;W8)%nmcjPp9goWJu>0)wVU8n5bsLZJ4w$J~>x`D}3R&K=HUIq>8{2pgGvEf! z0n95cnOz-gD`BhAxc`{Zr&_>7hCB9Zn|HHvxq9b^fGJ?v|B-Z59X9_!+S@^o&o*#X zLd`&D)stv<#eCx!2GDFNwxd%iICj76?$ z+s~WXt+swa*Owb@;`YzC0hVI*urs>Q!Mp;kE;NiWn?HzDSf-iU0vlt&KBOxfNJh}` zp6|WF#zSD?ph8s1<1rz5&4?0{I=*2;&(L#-(;rq)sypWJ&>h$HhU4X3vc;m}U zowh31nf;|7Z@aHIa9z<;vsV9Iu&z$0F>a`RgH?IU;B>9OxG3_&2`K;XLO!W(SlsHY zTDIAFdkL<1^*d(r9-aHN)2y$-@36)aW1+%vZ6R^>A-qU?AafZ9uBQm73kNz}Sic=7 z3I}RlNMz8*-negLp4|sM2KPJPZ~79t|M-&~shjTq7^ z4yx&lC~Xv9YZ(X8eCoO|52ob#I) z5W(<8BWLT3)~S?{(T64&>hO(>U&67!JP^Z_Yb5W|5EEMSyCG9$L|#+c3#f|@97k-j zd*+enyo00(^W{=aTZ!gR?CPX$Yk`XbY_5Rz7rT9{U`p_rH|~TaT6nS|{>rRUmw|qx zp`TX=!|8l`JXbBvKKgVSH9A`hKB;Ce=O(32!&w4uf~{fw`9ZdJeDyQ!d%@p%W?cDa zas9tZ%XkRU6!2q6R|SgKyCb+MDApVm+Xh}sN7mhx&)&v8c8*(q@9xg8#qW%_Q~!1^ zrqMHgDYVLPzWeC(x+@p`TD%fXx|+b?bSjKpX&F(%OE#syVN0P@mBn<%A(9~q(MGTz zx(VCgP5Ieg;NsfxIjQ|Px`cyps_Bg>sgYgiW#1H;*(og=JB4<@K>4a*edg9c@9}dK z3G6Qor&DpKo1pxewAhulVC&|lG%-DGAwlAR<5*N;V?=xmIiFg4u7c}7!J zJ(h{DQz)i^-;g60C%~Y0YDB|$SrLK2M$K3cExf5&oKod0)ZAH3te$7eptp+sm}#pw z7|EXC)Q3)SV-3+M^)36aqccJiW%fO*|7Mj)P&eeD(#MzIVUz_6UFJ;SW zFTW1_&0yE9{P>NZbTGNS05X{q@P$H^A1BGxzzlw_p8wf8J4+}Z{>s1FCP9|zMJRJzJU$!tW??jad`+72l;y4c zZk1vE*mw#oUWfN) zTqMk@NKq=K5ZTdqAHAif824^!=3^dVe{m(k_1sZZl3EMSUm2_p-SEP9=g)5>U$<+v zCq{MNWfP4xw0yNzF7s8pL^Z!(OwcCOjuM*o*O1w25oJ|=OYHjd=^inJ5+ZVd-iYBW zUgoDB1E^c#Dl`#Wi?owzMbGCN6S!}asyAi2 z`!2G5zOsEZQ9(1^MB=Wxvd)PCR{nIFi8+sAp2F(2yl6_Sz`{K?t?tmZk%aHHsc)qlib_BBf5Aww^w6dq zS=#t|gPEo^p^B7>$eajH`t&jXk2>=0*I18Nh*J{a(&c(Ssdn~r==|_;7l`1+0FF1| z!VFPirGn4Ei1trXjY?xznRD67!xlo}oglMvvwdlLI+y#1BgLDlQ)!Nhv^ZDkNRCb5 z&@o_dc1VmLhWDw7Zthw&VqQ@Y3vf*5Jh5&N-bXP@{X^__;tkPj%tA5c1rd3Ug`K*Vwnx>h#L8@k;-t78S4If4S?v;u$SS=$tV zRX)Uxpqif_&XI;2!wXTm6%N;g)h_sTpMh-%i(<}z#zQCMhboSBTwa7uUl zO%qA#1vYT;DMltgm1)qAvyj@p6m8n89Q zpvJ2F3lnAw=gdEXGg>RLQ8#3b9>tLb9JCeWm;o0pe#}DBy6i|Su{(d9?$dqyLCM1^ z`T*lV?XO4Yx4d41XE9UqW#(}_Md2^Zh4#B}Ftd4IJrEpL5(m08mBf58bS=1`4JA8R zHU0qHa5@6SA7GNjOh0zdZIn8*qa?sIjxM0i2S@d3Xnj$x zT$8Xu{F*5aOasvv-9Jl%PXYzNrptTkzoJhrw97rBwE~kMpJ1kQe}?V4{bPb;svL#c z-60K5+22)J&2HOF&Z_Fsy=rcz8<%2D6dIC_Rt{~FVeUO%uL*?Hxf*wdW9YiBX{=^B zey~N}^S(VwwmU46*e~4E8*Y4CY&Y;A?)*XGdT|Lm+j35DKlpE=%5Jhdb8NnhXH51l z&WZ2*L|9m#m0q*BE-?MW47n~2m4! zLd|)LKJ;22Sfu!k>&~?XIG8oTp;(ICpMIaNHUkKb*f#!i+ZqEI9_!m9`N4GV<|dsG z<&;n@@YT1)7XdId)^I3s7~vWPWf&o)3|8hQ-AKX!a^5ATeX;FJvIQ)Y)#en*IJ1in zEMmO6s>5$o(50}PuE#5|&haZJv9^A~=hBZbdTX~Tf=?R1F(=!`Cuq8}2bue@rO@dW z#fkt3iOiP9;GTHOb6Cc&O0u=w=hmOBq2I1CS%dxv8seVa!<(_WtIwU=L3ckvZOYV? z?C<>nJ6-8gs5MlSC|3)tqLGS8O=GUuVq#kQF*=^N-p{nmca*@&)?<#jAhrLll1yWg zr%R~C0z9;B%Z0M}MJ~X!+W2WJ+D3D+v@4v5$#CMwC_uXwJOXG}fm6Mg6{<@C*hK#X z#CN9FIE(y+PD4!+=e}Q4TU$Gt#Z0B&?l_TU)W+_y^__k~Lr(JjKq$7#LEZFsP9%R9 z=aa50H?NF zt~GBAe}Z{7e{^f92KK&#sury@tuFVI4lHg@ApU8+n}bQpFSHsh1(yfB?zi&#?0~hk z=6o(;ER}1!HhU0w@!!|O(Ig=`q6urR%{ns8_NQ=0s8oJ_PB2oRK`X1{E_Mk1;OTl& zjk%J=oZlfBR~=Bu0{|i#$7?cEklCK{>QJWE$mQhgpESo1XL~p$Pc;D8ngN66;OBIs z!y|cFZCZ5&&W?1J(*V*@E?K%{s`VJ&^$}LHKV&$_(EOHv4Ng9ERvecMcx8ucEqHua zb7%4<%>4WXYiHq1d?b}a7C4_G_40qLOX9gGiZf~T-V9r_7AzdzFmiu6SKzSPN%Rfy zANK38T&y&!7fUp(Q6j>!-W}L&c_HpW&yUEMb8vo|!Cq`JN&#VW$lax3vOA%6UeFO? zjjenEJ>Q?Rz9*YCpRCa4Z#~!o>OShXiPk2^8#Ydos;gY?%@3FB%bV)Wmh+AXHj*-2 zuY#7OT_%MT%G3(a9M+r!#X}}0q?jjld0uX0YZ;$ZOI4U#r# z1z?%>%J`PDVAlyj;_YwKVGLsFa|=|XOvbJB>#S!A!(5~_OJ80_@zX1GI=>sGvRT|R zo$F-?jn}enx2>t~m~Bg{1-edSd7P-jO2&Q3<8$ravAfC=@}mDPS%W6Lc&ZO+4nhHS zc6LrRnyD(+Th}UrU1P3s_fG|T8g0^*Qy#ib7OPtwjC*`XQ&@`^Tbmz$1YyC!FfGUQ zj}0_2dOJviSF4ytLe=G_5hGm*7ZbV5P zx3E;WQl9h)!Oc*Y6dm8A>oaOktEiZS=#$_6)5x@y|%IUrMsD9_xT<*{v zaefKl&(5o$qW3Uf*-+!H2`HGI9WH<741kZ-@Q8yS_kCfwyBnQ8N2EwTQFSTs?UhM% zlrTez$-rGc=!oqq+XvVTX#_`b`QtfSBi~GwU zz)WS8f9Ze$*o{7X{KQt*4nyE}6Q$E^^8LLIQ(7qVeHPFQF zhFohow^jU3+Nsqn34HF%-3!hKv0FaV@;;w1rUi^AGZ)*>{wP(c&5=msSX2HsGqx}S z_o5CBC7NZ`|BIRcNJJ-5D_L4h#EWy@IGC-rjJCktryms<9fh)6ueJyxL;8bini+(3 z8r*t_vCfEJCA|5pW+4HZw`bpH*^uPG(5oxz&UD|1dx$P1Qn^T|*Z#D4(Nl@7D3+_s zv)YT=V9+XbYOSXKR!(o2qZ0`uB#Z)n&DU3R@AsKn30^)EA(_$KDFz0c>wI9budLF0 zqe*1bWRmmDUv2cA=021vxAv(Xx_#`kvuewy8T=sgz}2YE<=xAixMThhWf9 zI$iJ0+TDh(WL?FROaByPU3c6@EYxUiDrz?EdKbZU75HSaSoQng^vwO$Q|saN`P{DZ zi>Ny$P-Wzsh-{Lx9E?ttBaI1?pV##zaV^~d(m)d_4 zeh#~%od*bWpfGqOm0eaGCiwsfoWk!A;LvXY8%kac;&3=ysWO?TEmUi)0^aq4(_ma( z!Bm~DR#GfE+y+C+`p+<_I2iL~J*^@(26SKu1{PHoLYE7d3k?1(fx%rLLVX!MXN5_@` zl_-vm(7RtGSYv6R;BULzVcy-hp+fE2_gHwm)@s0*j()7SBZMQkOdUR(yCTf-(!2Mn z+ksbTT0ngFVRIYj3<2rKW939dvLIk1-g&w`j()10iDvK6XwQTsS+mk3Dq`jpc5kF=Xo*z@cok)xPi-Kq|^Eb*>^TK7Z z)-9pFxrdKa5D9QO8<>{Zb z9`RazS))XW9-Gs?Wa;g;+~bf3ZX!<{_jKwV=kdEDxx(-Nf(=!A!^V&G7VBl|>9p!i z2R76dvi4##PFGzTwZ`(wO}2$-!#=mi^MXq%W8Z1F+8?$;mPW0JNIN@VcYNYrN=y1Y z&K~z?O4c{@A3Ju0WAv{5M`elyXt=FXBKkg*J=q-}8!hPwgip3m6LnB8oa_ArTNYdx zVDnC|f8_&XY27D`i7QA+X+!*;QE7@uj`LwRl84U1_em-7pih2~SJ=0qkHfZYvS>7b+oRT`V~PgZIA0-;_T1ekEP z;YeDax+^=A`Ty*NH@857#g60IJ^A1k7<8Xe7+kpVl;coW5PrWg-_N`!*0)wMOa z*q;4gI(sK!}gH-xIwHQ)~K3U`mRUS!m(@@uX6D!87PnS+U3ZW?dJ0W6bvws?POGyODs z%uM6-j1Up@puWQWQ#62dgAU18t3a_my=~kqPY+mTU8k*>hDs2D6*)sn`05f19iT~xWoc59o=1o?*13^QA~=b-+KNE+Na!(m-Mck}PSj4E*5S9q;WJ01meqQ>p|;9H_n zNeZm4vvg|~lNGV`Zi5W>ylRH(ywNFZu7?euwfF=+-nG@r?*5x9{I!>VB7F5=Gu67o z$(#1LNAbBtg9{pa({)0i(&eq?kmhP{teDI-TV3jXX;?xz!cFcd;t7G@sxN4jjE3`V#|x-);4lwF*<7n4VD%7 zvL7jp&FbfiX+z4%gD=TYSE;&-!)e$?1{Qf!nQ!|;{2#`Ve?*}oF{Rbl!Y_@Wmg8HKLAH5M>XHR@%HIWV7=RBcxM*2Z&}v+a=%{gZobgQ zy^R`*0B+-VMS_N@xx!eP7Qrgz(bIQ*PXhv%brgwp?T@CD4P($vJ9pdWmeGMXSi|t? zRhmC)bwWNSN2IlWD@UGk{j1KW`Q<4y^5$eo_LQqzTo|Vt@59dyMUm!@-N44Y)HltY zn=~%rxU-^LYoyphtyy~Yfe7Wo0nHu!;%kM*gn!c&Lg#oIkHR8n>sxElxT1r1_n6qj zX}uB1(;YwQ-$=(b$#Z^-`*|ypY(#H*>1yd>Ir9M8hXzV-i}l(y@3ti4<+O-B=?reS zXWf69ZBBBU(_WNCO`DS1>Z007^}B^T<8Fu9b>({3UKpq385fR!MwrF4?eU8EsTzyU z^is>W=N08?gF>V0UTBuDylpY!`*}+6fIN!dt`piv{8*thrmgk)Xa>25oGyFEKq7VW zl>{}SO)u<-IY;k0eh9KcGT>8ZzCzgzgFM|vN~8R*Uu2hnsM}QyBNL1+TkK3@mK*8I zS${TDL;b2Hm-G>vxp+Pn>6BvtFeBP&9nW0KF1=-a%*{|Lm*9>&6JwO7h1vVZa=3{O z%>prsxVswn9q~7Zfxm=B$~53UkI*FER-a!-OZW$)4infc*Ba-{fChCzeuP#Ob=(d)Wr?-ae@pP?Hbsh7yr(8KZPl4$nBnkQf^$VSXn)~ac z!7kZU7%roPQxuazN;g5Z-SAuyx23S7Eeib9^1xRc}h`rg}IO1t{Z zZ=M;}JmJdasz+44AZf7W@3iupknlj4vz3q89B<6FV-tGQ1b2#cJwIEB6vmW0wJ=Fq zU!RU~?)r``3L-mv8Z7LmI=Wq}A*+-Ej6hp7kQoA45a`$U2c`6Lbgh(Cri~ETtQ0b zQFnf!5z8i28(iM&{7CwAlG(j`FVcRDg%I|xE2d=t7j1Se;l4oW%$?2KE2%1#-TJ86 z3IV1G$&d+RKh*nyLEvUEV%RBN#dw=`ur-^Ie&0a`Ymnj4eP?OP^At zzL0flsiOYaJe>cA4iyhmkTDwDfKl>8H`xZrjpt3JCjgJxN-xS@iIErR|B_+}MvMsq zH_F31CyOc9cQ?C*f58-<>|S-I61-)%8Ktc>X}(b1)08I^Wl#RKdRldv5nF z;Pruuo%LU=dkQdpiHi68=Mh_1NHl3YRw}Rpbbi=dYhg+to+%mqvDih6#FUA;f|Z(y z*DkbRETSyei=_4o-#j*Wn81WzzvLWX_@!o}Ld7;0#DZY=MpN=(vnx2P7OV7{x`y#% ze=7a#%FtS~?jigZNGHL#8leHT12-Hx`4{HZQ zlz#R9T64MQE?w!#PvM{Md>J_iboiy>OJl5wy2^3*;E8-0{uxAlHu+lu#+Fbja}k!_ zW6bq7+mDRa1%09e%QUC*7>KFaqn*}gH7}Gq@V@L_v`S#&03{;ne2%SbbB}ePyALoF znr^%D>Tv_8E>o3t-F-gP8-z9NdCsNncqO{uqF#`T70c%2lWc=FMUg}(Wf}r+&K6at zr6kh%bpr5sn*<=hO^!>MB*;_Hh0kZoVA6+qd?0}}W2RJI@j5(CE3wwLSOEzi1GmZa zAawAcoOo!X+%(7!nAgMHQ*!tkxO!W`Ab7yK_rVm5dvg9ZXGTUi(+s3UzTYhIpt`I0iXa-TFe9&M4>&gE6EX*#%PfO}Ig zz|);vi3&=&PfuuFzIQenzvHelS*8W%b^+!>S0oVZ)a6#Q`aVC`Y1B41)t#D$&jMdD zTo-Cs2vS&@4AA;t#8T3)8m7&c+h}A|y6#Ow+*C}KYVKH{^X?V(pF-jMaSQhMQd}Wc z97Yp=gF)l`+Pk0L-ngE6pd>OX(cnTc3O0^;Z2 z)thc&Z;!=wY?B(=hYd2^Xy3m-W?Esj82AJ;++*I^;YCf?wxe8$U>lS6`9rAF&Ecvb zW~elu@`G>RkC0Y_eSADQgUDpa0D)Px0juPFf)%LmQ#_q|84kiB4ZizvDJR_ zZ#8@?4s4SfmMAfRwZI+yu!)#$SNaMft`W{~x#3J|GP3ESq9r6@BB<7P2pZnY9jnu}as ze`#}Eg!A8f(Ic#8CpedHKXiZD{wemEfb#foKI6|`+yHw#VE+&`YVrP!#t8} z#voftA1hE%;T`z1qi6EByo4GqvKp6%^heaL0lf8aKl*9??RDB-6|mv)843--)Tnq( zU|Sqc*3<$Oat#!X!RB;lA6iofJ5Ptyn)2EC)H0;A#Koo9+gMlP2r84S- zOVr9HC5>+4GKfoZLBmd8j?7$e#Y@EytmCjkrHje_jP+rnv;taPZK*nVd zd&&G4FJTt|;bl%2zANoZo|xnp)a%C>=c}j}1>X6bNGmHcU-5#`>GGPZuvR!N4?Jf~ z)FdPRs8-nqK^E^MliIb~KzJ;2{^4IKm+j4_?T|(!Kwp`p;%SvB@i=W~jOs>JS%D*Kyrw<+t!-(Sy6wqhuZpVjJnf5Xy(Hr=SrLh17K_w&z7_<{mY*?|_ zD;^aL@IFD{cT`aMVkzHCm+GR6YKuh1&$x39gR&Sb&bU^^`%OgySnzVRHodJOJ637V zN0YSyHOA8-`eGS`-2#X)NNJn{KvS09Rn1`4pWRRLqRJ#3dRt%0wH7RY62}#Oa|x|e zGL0UHm;LS|uIEve*3WC_fNXWTvhtEOKROGJv#bxwu<<1;>HIFqaphB@m1yx0_$hQ^ zL!=3k&5d3b{uxfr{_0!$n#1C-oTZMv`Y9@iRNO(9f&$hm2KThO8ZA6!h}ShFUu;K^ zE4{jdYyOizfw;OjZwFd?_a|8xS6E;HN@ z{1Ih6dd@Wy_v!1uoMFT$FhdI2{_gDr#W>f&J~RCx!Kx#GoAV}x_V)3<{h&I=^-Jhq zsisIgB?Tfb^Gsq}A-?UCCItSb;ux`dbX&%!_wq50L#XMmPKbxr>0;$*;Wfj4HP(8NptA@mL z73-PjZ>XH*M{|y}x$8fY*p{OWVeL))p?N?R4s}81_*9Fy#?J8dG=Ootb4%PwF&>{Q zGvmjSTHp-IlO-&{rnJ{r180q0&cYd@ZaL?(Z88;le21S``0-sf>BX0sYl>SSox{;w z^49dhH6TWh@z&eaCc#ABp{X}#52Kp4sQ4h>sn*L`+@}j}gb9tEz2 zURA7}ZnqVgQ#)%9m|!^72aD!w2y$lA0zf<;fUh=)Xc%c8;=@#HsI=!z|I$}D22mJy z9pZy;_uCjEx#4XQ4Ab5peuH@_85;l8dx0`;EP!C&P?e0)R-*%JUk*)k|LXQ^*TE)O z_qFK*8X~k5>DJxxwB*Z&apVxQA(8(Am;|ef<~=fNj8CMiWEK_xX=iy}Dy)CTwa06&p5J^k2!a=ljSHN1bt_Oe5p><}6T4S`>2L<97 zncTCO6ZW;3M+A(J>Z%z8mTpTDj%6#i!?b&@eI#NS$B*6Y*GQC{K&3#pdcT1KZ z;!DXr=aS|UM}1tf?v}54-nZ(j zQ|DCi4;blQyL<25>%Ojc*sLQDra3>N^rHD(?ZU98%XfHo-eCCJ^AGqRbHx#2_~Uh} z=tC?5T5y=s_3qyaS}|B%)fnWEAZYZD0zm15c`B!Q+0uAYWLT2P6`H@;HrPG=wpeH$Xd1+oNd+s&a((J} zhv(hq3hC8(UDrE)r)>Pfgm)sv{#_kf9ji6!JYgg4+4<0~M4u5Yp6>bux+zViKdjS& zceNShBq(uDxkqIR+CEi6lIOs=;k-N_c#)W_G}cGIx}DXr25G@Hk9!f_DL ziO$j@;%-V7N2>zmFkw~tFA_?v1#?)4BQ$(I=jhMAiy?6R^O5gn+x}eQT->1Kj>PnN z7ZXQd^by7~)fyQXBWUQkTNqT$DDq?C`0Mwk0>0czgVNOR7w?Z=GBz6{Iu2UFI#N$r zCkrC^)EE|wV>l*rL?|;3m==7c-^B2Y*PwauGouPuJ}pc$5oaHv!XRzd)RP9Po|F~7 zz6Bzmd~}~hGsIzSIEL(Adtw0w#t&yP;t*kVql*XMkr;lm1W~>!a5M6sJMETj{u;^2 z`}VxL6mjL!0=3mZq~gf=k3TBgk88OsInqHn{vx#CCjd5fQV zZgi#|-!I0!Z^fAX?N(Oo@OfIYF?BCX3;ke~`7{rXQXub0>V3V1rVPn!9*v+3&6{3p zSVKJyC{fvJeM6q?`o`(=yyVfh9riR%R>zc2vHlK65!DHpeY`Fg?bPyz6Q&?=y_Rvi z1$JD-h?LU-H@bfTcA|_1RH|yYO)JHz(PKr0@|Q|-ZnqO7_nlBGwA&v0JyXbR?%Am2 zH-b^^Q!7^SkoTdF@MF}ekK>4k_HfOsp6LRWE|LVcjUX|rK3tnOv+*On zxiZH`XHKYsTbYlwsX1Y3kWYy=ejViNY`67ZU2a62cCj0cO_0|Jcyts6|DvB?76!{> z&NnZn6pzv>9bi1~A|jY^`8&TePXMg)FFYvB_P_n1*(Zb^Ii2>bi2e~T8F;ed*%~F) z7Spgt3)NRsrAn$dZ{f;NDtM_C=DDpG4*6Td9U{|`H}3!w?M(La+yd5f!RFq6MG(Tf z1)#5d(yY$p)jRaQ6NnEk{P}Kn#9(FaP_qc+bgiT4^_slqV7Ildmcp^M-9e`0JLGnJ zKzSCq4P0x2UR0OvJ@5#z@6@|Ec$*X^!1^?VdQIUJtv0)mDb>=y6ve^_jc=U=mxx7! zs7RZc>63+uwMv!EcS)g(lW4qOZYK-VwKDSt3~s~lu=$)ui$QImO8`fCz;D5{D}YHgJN>RW}#|EG{tuL zY>)QBch|?jp5&mT=F9Lle2k%?{O<9*D97APx$PBG&sb+GY5`8*!br2-(poO{= z|43>=f;X{$e-Xi2xr}6Qv4&=6;VZ z=Uu;7zgGS8ac+n5ww}@N4CGFR{QOQ-C+kP>%N-$TUEtOQ;4GHbr;R#8u?jpDfBE)7 zCXM-L)K&(r!e4@%Pjfs6n^VGn)zQuO9H!7swIj)S|Lu|%Jl6KqDdt$>6#mT#G^Ryh zdw%kBOUidedhgA$g@eB}l#p=5TYI3g2hh4C<X@)1V3JXce3I!_hDAlWlxQ<1Ps6)1)-(IFyOt5WtW=OO-q50TBi+5iT)Z6lyU zs{fWCt@U`{NAQ)7FfqH2m7#J2tChKy^=lkfK83C#PAT~VUCA<4dIozO&?+t;E5uSTJ5CAd2S6q^ z8Zds3yE-a0m8dGZ%IakT+Keicp=CyPG9oi6Oe$@c(cx%eaj+Yc`gZ(}|6EW$DXjN& zFsOWYot^_U^NsJ{=*7O+n*zXGc`pdK2tP}ASbJ>O7&p8Ao7$k$Dps&^;PGVb_5{!~ z)L$Fl64N!7AqKT9y#899?jyiSj6nSA$yR+Vh|EKxttW)Fx!{>8*vP~0@sO}9>u1Pi zOGRYx#+jPK6!5@szRo#=MQowQK0(gcuJn&(Ax}EFC7i%|q9DgB5;^T|igG17xq&z7qXk=(uKE`kK09loB86UnA^unDNCGciy5E$x(eMpkDYV zcUKLajcdK`&V?pR$vti_rWBh+G>)1HcLI0AZ{L@LLsHPfNjfc`Tw6H@(3_5=3TAd< zDUnPx=Aao;CWV4K-#Cxug(bW z4#Wt^x!=xAF$zJ|m2)*%VxutF`?oNR97n{pQY)&vlnyL4=~! zkUM|a3)RaGH4;Ok=uDSj$1aHhZ&Y=?5T0v?xmfOmoJ={`u>uZ20e7;GTu=pkp?8O$ z4hLde|Vrx91iXk`nc=MMWa;K_5JLyZuZ0i_!H$JnnfgZH3E(-rhn@@#W!! zQU&+dN&S)JOItS`J2#jiKy*$31S*n$k9#=E-QcgNUH3PMGn)JB z216Ne8gVzz)Xv7L4fCgsDDd3zPrbLpj5$^glC*ECZxbH+otfK|9RK-cM$YJjOyroOio%+?kgHP3E5R1E}M zWIz9kZx&s|j6OQBXbej^4iJoG)R4nG63pqV+Zs%ge)?pP_hUkDAuPA#k>%XLEyjZf zk2JIU?H&M=y9~uz-x`dPMyBQrgE#%;?3dODj2Mj9%lObqnm(+sKJA|`m$eI_UjpyT zK0Fm02h5N=oh&owdNI>@D_<>13^O+)1cJW7f6EsmHJ!;yx>w_{So&6{!)>)RcXk$$ z9TXvzj_aCpW0>9h86lyyTbZPo1>Kp2$U~-mcB1!#1VX(27&BQaEl?R z2zVu4i}IWM>(BeP?MY!8Zdsw+#(>qojb5&;y}kYh)*9eJm5yhc;DcL@V&~tzL4XQZ z-(S-*V(Mmw`uK|Q3(fNs26qU$KN2q>*W29tkts@luKSibEk2~XAH!zfRYP{P&E&s* z!?tB6OpHXHtCtl67L6NS#?^@6h+3P4aNZW`j3TY(7=s4 zmQI|mfW9mGudBm^U~Rtevr;oM zXGd_WNtlXPM3CCMHd&7&*U*+dzi*z}QLHvs_lnd5(1H5{-5r!uxN9F! z_H7nsh&SQpBE?4!>N83|eZQhQjI(j3aRH*rew8>UfERP{K&QEEEK}obNn=bhDbvv5 zsT0f6yFRxJv3-nO3-d;q`mhB46vH_tY=;)QxF_HZo4|8tb~<`MN<&c znfv0sNbXV+%vj4vDL~LC#PCzd{lq<;Dad>GSd=9IUK!L5D@iRJuh-SrB9oz6+oS>@ zE6y~f97FyM-=!F1*?Vp7r%b0m5h9o@=9Qk4XuawbeB9kWJ7a3vUc^n``}@HD6>t^lF(TS8k>~Ow>l#@9%-^25>Fs<<}Zm&n{>eGECvJ zG?+VGE61A%ZsrcM<;ziA&89(CP(MRbj0Geuk~;O94)sW8T)9H-#sjz?WzuD}=hO^;Nn!oMI@3I; z4eGPGrY7Xni{&OWrz;o+y2lc_kTKmmWz!*!?Pkkdvf5nFM~WptQ#3+d{zn_Ta6uau z{Ts67t1{^jnjx#}MJ7TjLSDO)P$C%5+$6za=iyXO)y|WZp9VPRnXjg3+JS~azPO|c z2^5WgqO56*yM*Uk_N9hrdhDJz__f?-Pn96!S<S`Y+c{Ync}%N>I9U8f0`me2CJ8tS009X)>#!T7x_1?P=^2BweAK;GQ#?KcE`JoHX4Odj@H3;eci$_7r+vpWkK5vJ; zK5tkb?`TY>weiY7q*x>`W&)=%Ey&7Jr87)e6iIRqn(NrUeb}(rHcyOa2Knb3O(BZZ zw{tA2OpFjWc4QP4L9=>>L=&IP>!kH-b1sGhX-qrVdFAZgXcw7({D`_4qkws`=@rJ3 z{|hN)q6+k>9?@}0`-8M}i^rYa)y51Rrkkz?*Ee0=^q_m|hg;1^5I(Cm6Kl(DbX_NV zS$hZL=1^vd&uuD}-|q;n7Zd3v2vtSe?jngh+!b>Hw1AQ@VW-PX{OYWsHRG!k3KFKL z?#DMuk#NuJV8Uxrn+CR_%OlQbA7SOiI0)T`zK)lvuah4I#hCtSPiLmWJCnwVWvS_p z)2s4-k3&kdlW}J56-UghHg23f#rLhkXBoc_bZKN%(?5BMK)u%+qEg_+d9@mh`^dC* ztGvg&tiNXd%<~?z(^a>Uv(medu2<$*w_$mu1;?TG^Dleg1b{hnTo}rLpNv&kL7vO- z+*Tu{Hux5!f5b!5A+Xx3E8}lZGdI~GLRXC}M3l(DZ#Qm_`1CT0L`BHRPq0e9-}1g^ z2=kR%cT#da%ve4?q-9`#1;;xXJR>Nn|I4cpnvN2fHX`2&v#LB>`r^jhy2i@)SuZAL zaioKOXUxTj^bx0G1Gkk!e*uJ1r!J)2?ZZeX%>K|p!{g(tviRE1&SOyhoMHxi?W~ikNw^*{N+MMMd&65xmtY9xH$}A(=sNEiy27RE zHNcX%%q`j8?_V=_dZfGSdoLrZw zptIHL>hqPcGqo{O1v)ej7HDDB*coor*~{qUtZ0>z5JvLuT?z0A(uE1uE2r5jnaBy_ zUK3l}8c9L-<5~Eg%>^A!XloWXvWFDRS`W|R}}f?Z8VFo zmLX<8jI}*_Eo`?f{BSdsFloFw}krYOO=%^;U?Yj)`i}>u>Lt2Kn&= z?9-8MG(6O{rGe@SFP$u8%Q-*J24gVT%LqmgxjE=eQ?-Uv` zJg?Q%TFh#u%D!uOF>df|CC#JGa8#H`WSFpU~&nwr;O1yMZBp8F9?%7THxtn z7skRNZ$FARRkX9v3zH&k_p#`nz0HK*F1 z@*bFGuFnlSS8STsdSI*>=eAe*9fn)rX0&`crwtLx)br2~`2L z5=rpzer4pYY_q`2Ud69RwQ+60fkrV;zVLwd$K{t#Az5?bPnH+fHRwVOJlNO%&E#Kr z7=_h&89N5PGc{)|9b#|6X%3DiW2E=Up!Mx3ZENgFt<`>^acWvN(9n@K!Lo8z-kiZ_ z>X|ZI9aa9|Dt^VQSIqp)NaMHw+ARlu2a2o89~(@XRJhqAkV&Id8~h$=AXX&SjMh7z zs&=l}PzlX9l-@YUn(@8}wa*{hi#snt?;~4gKXIZq1)Yfk+gqSD8gJT$ou4E&Z;2tc z<1gU6Rm3>NunsxZ%7COZ6eJAGubFM~|I+uQ@IU<>^JBawg4K!qWIXkCZiKe;Yc=TY zp>g`A>8`y)!e`U{ck}dbW>4yx8<;&uSzX`3#jq15^0=G_(_PV)XwLH+bHl|e_sxD1 z1!f(4@1vf#%QYYbsBAbL6P^#$vfJg<^QQ{`adc`;L#Q?WDn^%TQVht@4uoe!8)(!@ zRlcF4o)1lmKFs;(?@u!yMiOmtW97P5I%_ynU1-;n^I9h5aQUz5C~{iDhz3{jY7~|D z<>#_J_PpW;ZGR2$t(=0Gc=dRq34H6`(!x(X5LauMIVU2??0wTkr9Pet_($EzMad%R zs9L0%b9xA*VyyaX+L9xp09Z6tS;;i*Po^R9`ca&U{4u!y98;oE#o`B9gW@S$FtBR9 zQkT$(^5thcN`SVRB!-17^Y_|3p4xJvR(X0uE-GPM;fvK{U*L43S^atgMJ?t9-1kek zm(s{6y;HP@2)*n}{K~qMVvH$cm-FaY55_`8(~=z>nr?uk0EhgH-j5@Ay=khGF*GEr z>8zA>Rj|c^w_G^c=lxv$pzwh+(Y~$j%r|!DekmtgNzr)L$n zq&Db=wDcr`>{hUIdZgXtkYrQ46LB9@ko;eMa-n^`N=@_n(r@Uo2(Rpe=oop9k_6qJ zZP(_fx}aD>H4zPp^|q)R3{J+8@*Ja?d$rv4=HJPf+^&w8Crqo(PukGuSe2&*a%q(BRsF%xhdY8E_{kwcR z-#v&hQ*Xp2@Mm$8EO)U-L_jW5pUWOeWH2DhcoSDY9XzO%ZHAH7tN-}NDQuzA?2aLc zPq(tySrqG))wFs(ZxGh?yCzI8=Br};H}lOa92VSsw!kbQU~&GFobP(NM7r$HpFd`X zlhEdf5a68NAqZkpwr>T9N5h$UFI`;L zFGeNgy;N-Gnv;@*u`95Lad#xe@Ieow17)O5&DFrv<@*fnxFRv>xO%aPo%(Gz;k++GQUD130C~=CL#l=l!$e zx!E$E;_bUh+x+6ZH~}L>&pJ#}85)5TZaEo;^G$j+Qx0?Qw!Ly%xv$1r0WI2NvL<*@ zue(<4E1e&-X;}TO*+H$A`G<3@qSlX&Z=RcK0SWi|+^yH7RY)qnVCOG=#%yc#b6UBa z>O3Z{(y$X2XA3B9tFgwVss2UEvMGhP>HJ}qA1w6_95|Z6Uyk<+Lyu9#ildrEm>$xj zu0~cNHlX9u9PNrP9XO@~#$yoZclhZ4ifV<~l->`5!f`SKy+gOVUDHkaXkROn!E?l# zwO8qRaE(GNr$;I0+xQmWRcIP$qOpe}CSyhYhk9Yw5+J8W-x-y+Q3o4)bsJ~!(p7QY z8F~u<|kK09C%@2Q#wy?94VW4GfFt3+Iv#dv!=K-wlPK7zd%x0Mhc_k5 zk=W|>Y?6W_1LWrNRx9S9KePLC+%gsvOY10xTLbTj_PtZSkZo>lYPDE1dy3m_1ZsY4 zJDH2hj(RPMOQV2GgYy>hLCqqw5z3GFKXf6l7iv>5rrg;v>L)>Z*c#u?3*hOP;nUYP z2IYPI6C z>QL7~?-pC3@O;~4FJ#6P-Hkf2<;*z7W{OLby8cxN)|oD<%7ld3+;Fr&p4Cq~(;rJ8 zqu<2u;-&Da(^aknCg-<&w0&*1L?s^X;K66pcVUc$g*8jC&B{~;Ha-6Q>1zaTtAdS& zU>)g?x~mO0u0wPJ7UjC1ymHD;DKk1h9Kg2!y^$Xt<_mCY@?7uv@oxHQkg>d=$#^Us zXT`UsD!34c(e4M4deyGAyy+WRZJrG$<#k(bw=|APT0Fv{WzvQ5(alsyE=hV;ojEQh zAU*O!x#hK!f41FDbab~Jti9z|=eys{mTK3*U5)S0uyN{IW)n-uz^IBt>{d2-ll}qr zmKE0`l9T!0o%`y{sBa(s@#S%S`|Qy}n#XIdQ`rqHTDi`bZ>t+qA9HWC!K&fC_ro94 zqwD2~-gasGrtK_n5JM|YdeU;7juy<+1EphVWdlxkAsb(Tx5=IkWtK_)f|~CVHD2_c zkrV6Pr$2et&Yfvf3lrgx;} zMY3?cmQw{$3mLB4L?c~+x{XO04z{`y5a_a_&2KWAU!A;F3c3srn0K_zRL)-mnKtz5 z_Dfz{B?{KwsabaPhDh3^a0KJZojrDvhNf8o%XokH66BHL;JFn zee>3N_aoX^xs=EMR--riss5x(i8t$~)z8AVL1fvdk40@5Zp7-le`zTw;6ypEfBe(7 z*;n#g$K)4--F^2ac?Z69ei(6n+q{HFcCd;r=PC7VDYNebU)#Plf@d9>IWF!i??r;c zseD8PrGCC%xgZ*jm*4$|?xLq|Sqnfo;f&FzrSOXCu0ut6u*)QMp{7{Dw(4gq^X!xS zMSjZS+9tUHLiIrpTw=v@w~PR2Tc|6!tX=mP(jrT+2eOle8}+$JW)D3oo!gKY!d(T? z=QUOC0-Df($Cp`Ow5y*5wOYt1FihaT@3GOC$NjNRt>G6t& zZBR?WNx9XbN;j_>r^_C5GX<~9{kvOjyGo-?!K;vFeloI<&qDl@of0d}91+~e+Zp$4vL3b;i3m0so48r65Ppo}IZ)9zF)#p_ao=#5ze?KWzuEb4pn zy|LpB!;`&=4~0@gFx-k~pH;BQYLX2Rhj~gs8G7W}Rb~1gMU@LmZ4a3oC%kG$ zR0-tKFlqk1Hf9A$fqTl7ypzvf4fn$Os^kguDJ&-1o52|)s{%v4q;1!+GOJD#7Z!9U zc2|q$KW-!$4d>QdqOYr%_R09X^ZA}HtIkX5FLE%9r^=J)6PVduMgKjIdFRRnWvU!g2)1TllE7E%w-pcsM4mcIcodOfJSwR4B#F99HB)?ZuGd& zd@u;L6!8HiH)*ZN-{fu!T3w;;aTo<}ezdLfdWhk>?w&lqcPB4=>a(li#Jr5~epaF_ z6J%`3FHJ~LY@uk$D~rQ_${$SpzU`lKN*s7Rs|iow5tlaaa7DWNc2heN#;c zmQB7+f6t#^GPCONuk9b>UfW=98v&lZLp#o%pmB)I4{x!?WV>)RSD7c&^11quXO{D{ zitB3n3%$D!`dnj8c7usuZ1V-WjE4M}>9TT)n1lMmyE@Et{<2DhlMB+*$*0Hfb0!z8 zHjnffj{7a+vgxO$5{}E_*P1arV3Ehrv+qbBoVOLP7IyF(KK9z*H=8krL4N$b$qLo6 zRo-Sojaur7mMl+eqF#*{*nFr!+81;dPb}eOgf`E*B-O%7N_c3XrrjbKLpmjXq3of4 zEgostP@{HGjo?LR@%brLL@|Gg?#k?n+(xicU3M}+SQ~(fQlj5z<5A^o4!yfGEE-7J zDdDApcA-RmeJC?s;#m|m+!KzvYhoP=yrBF#G#zDe7t*h~`c^Tpbm~t?D4XDRON8L; z?S{&`D8UE$n2f>MxcNnqsb!~ACM6idz);~ztWHr)>^`%*^!2Nr-0h5~&7>WjctK)k z&Ng<7A&M&4JCa0b{ibNxPV1m^fJs zYM!2qy1h_uIUhqz7~Dn@FKY-WwWeqDs_0R@<)^aQTu(PI%UD~gRO>@S<$W0Umf4EA z-`o4#qT#uBA4GSKBDeYJqDX5+=l zy`}63SW2A+Q?DmwlEp_)q@?1Tf95)+2#c-d#?2t)NuNlXSBH9Er%*oSTSt5Jc*Wc` zvFy3imFA6|=EfF5f5gA5r;Jg_P;PTbvP-`C)Dz3|^98Nm+xfBV ztd~%@>ei<}RMz)=E=GO;y*x+p9~-CoplF1E?jTrp`>1)-Qw0FYHDV-FdeWA~wZ_a2 zJi3M$bokM*xeX&;`J2^}Pz-UsQ+{YC(g0w)Jc`YlqJ`?EnfF(OZBk-MQq`>nm`N8~ z2Wy{E`<`Tw2uU|JB~FTuuMnP`8DRD1h|hIr88S|4i+a82H~oB? z9WkUiiKd4&=dES>xb7JZ8+x~2XCwZrSErR#MkQG)12&Y(HdHNiQHMm2kytIoM%YWu z;%dareA;#tS7-_6W=Hc8U9mp84` z%s%|MZGLWeZEbja$b4RS7gnQj6?G@}&4_c1S+ntXd=%epm}eid$@Akiy;x*q_-ZQW zSOnXsiR2tTqwSwTvB@O!YVgWdd0y5A_ZUj(eDBCj`?B$pooC&xQW?)Sw<2ar-;T~s z+*e)^&d}9v>&HS1=E8}Rpum3+j=z`NH?r)0A$Is|ewhF4^4=+h8L@Sth(pht2ojy8 zKG|A6OTKm97x0Le(RP$5)C=Q!@qQPs6r{|S%RMMmdad%*$ZOdvcV*T-(H5@;;2|4k z!7}_U%?9pQLk5eadUE74DWO?&@@#D)wi`Fu7LOaoY869N=xD?%Qnkl6^)jnA=kcb= znMo<5F3V=CCd%G@a`hNhUhJVcFOWfEXZ`MarP-u}ExV=xEle7va0#y$%pj2?X@jy^ z4KXf_th(!$eG=FjtIU7{XI_fi<|gZx_8`aRKhYTW~nc;GS?2cQR~jIJ_6FnK;pKhv`($J+d+CHWLiH& z@_w>>r8em3n{*=Vuz_B#hEYB0xl}t0c*qP_gy`bpuZ#AM^)|d<5)&h=QR`&g>8ApM zw8MpN8|WKtRx1)aJ-f;Rwc{8w^JIF-$c(%aLRfyfu4BtJn{U6?#%d}HSk7x9_PVOw zO?JKG?zDEF_D_OI_;+#8YFYKW=j)YRRTQosy^A^Wya`5}c zjcPQ#U$meg_g4b1C$52J7S^v@`2fv3G_a7~-P}5>;gxvpPEh6od;k--yYNSCo-9iWRgU|2AdJ;$0#& z>e`4_VRT7E<#ym1MhUV}+P1l93JqQiblp(+hyknhnhlMui5rXV{rDWe1Y6-t1(!nd zM22WC)`V=9UoNvt|-ZG9?E`Fdh;_)xS%6qFQ|NhMRuTxVz_V_Ql|TiYp$uBx?) zjdcClESkYj|CH20!$~*BMV+t8xU0X9HRiL?ofoa+m72=G zvU%PUDyM+)E_<&?R#LoDI4x;U+HP96munq08f?3&{c0(b(d3AwYwa|kgC}ToUL}*s zQtmuU0XzYVDKjW=8h@G9d|?`AoJ_1{vUZVs8T_%9ZG%>zBsRs+j~w6`D}j=)c7fhfeHuh5W6HN6)-MI{ZFE~%K=ekf7HTyDLoN;P=pPtM+-kJOGQbw4 z=NmQcl9`uzT^F~cVj^=O|E>`)l;=mspAD>D`N=moToR0hO|OWX@X%`Yng*O`n(_FO zt7{o?C|WYA)%P&wt5{daaqDbiC<^z}j%q5R`7!{rFRy0RMYE z3D+?BlIa_gLv9(7X3A*D?o?|GVmgLQmP^iLpL}ucbiOfp8%)x{+yOEM-{AGsXa+HH zcwVPgSlTmlaWUu^2O5}RVli`2yo@q2+^@8Zgh-d9*z`<)C+1@aHoFHVgA?T>$L~Ul zTE>~b#E62|th_T@9O}nmWfUI;yH>e>p_B4CK*x9+`5WEn(K>*3DVm2jubw{u%OaU) zME{?TeSy`l2m@Zb&xCZx`IPUa%+9|N-p@$HabfxKx})b$2o9p?i$q5Q!oBC#TC7-y zzP$d$KUL6M_oKqipX2-Cm%PcpgQr|~i1SGC7|llOgw7F=8&H_cJEJ4KC4*zCHkC9bmN42V(Gi`MK-b*1JM)(En#a$Zsu)$KkElqE=aBhz!xwGZ70E}7fN=AU zH{;aG`|ALg3*6|0sH0vtwzzHrk0a13QIp&E7#V9-{hg>}bn~J=gnG}dOc%M22dn?0gJJ}kGTz>IYy4fF@^H7u1I-NpmdJ;? z=AnMLLon3jD(ov@dhMZE{EA-uETqiEh6wzxS)?Qf*lo(8!AL> zHoxSKpwI{+9Ax2s z?i#h^Lw>Jzj5G3MN${`_RH++A%g`WWX|LmdRc4^8SNoC? zYh*=4k4q=UF9y57I-a!&LhM($NU_m&*+Y{5%*8~3?&Qzuhad3hlw;v%ktG^FZsl~= zqbWMOYK=4jIXIkx$pN|hB_oYw$+TR@YIFrB2Q@qpN}3{qv?=!sjO%92y@?a?X?y!2 zOMxY|?i+1riVb^f$8Zw+v?ib@4Q0E!Y&ISL*a3*(w=VZ4E4`lF9Q%+^0)0$gi!%K; z-ff9I41_Rl(J#bpgGoplQxLm=EZienpbM38bEowWrh)K}5W4cjUcz-LRNr5Iux1>P zJF7%cE$L=unOrF8#5#?_rNL{2$++jvnBAEIttKZ1Y-Mo!co|-qGs4}2tZgp)j^BD( zSzwT4I~jS$DX?wXKg8OW`lU|zMS>e!sJKt|28G*?VsCjx;I4JCZq-dAVeKI#<;!2H zRp&$z=5#IIlg@y*Oiy=@>kDnO&7 zrg(Om%9qjvZ#m~@VFFECA{7%m41x;`8^zp!bqv=COSgQGe-MZSZpEmu{<*XF%9@IZmT?ANC9YRc!2*o0a7Tb%4D)Jk%;|d;^HQzJM(Rfv2N3CId9CM>VsX zK<%IJ7t_lO;WLDha7TGQTo?Wuh{@k1>7!U8k@- z!(ubUQ{kth+XQ9}l}6_i77{LtNMte>q1B_Mh6`%ZKv3YvF)@k-?QGT%au!0A6Z)qIiEKs3OgOi z(gjlBG2E*jLn!`uij@YE1BoBATOSHmdXi#LkRtevM^o>ul#)CO-vD}u7y$M;a0=_9 z%4r}$VTS6cuIE9(%~cmu|Eiqv;Z)uYbnP5_JFjlp4fHH8r`4llQwaG`3rYAP%~HGg z2#q5;JEI&QACImBKiWF~_FLSB1&k83oCur-igOc_u9If9w6wl!d1`LUWE_3oy}z(o zZggygq4ogBdhp)y*;e(H)l!A9%bttHSkwDuvDmGOJo z7UHOut1$v-Vsy{N2`aH-c)~C6BCrJGf2~h1ueKH5bcHT|RNoQSXR4IH$4$y*kVvfJ z`MgJmTf;9wm_@2Pw~Nx%%ySW&MN5!#?P%rk$35cMZgUL>=(+J9b_l@tr$Q0E5@6i? zJ7YI+svC3@^fTA!vHH%9`vTd5u-0;!;=*W5l_$XtGyno6GveVhX}+I8&NAHkUBjS1 z9M4l#f=&mrHyS~ul_vwMTB))L5gv-lr*P4IAvC*IqaB+V(D=o-N=qv+eZKVc=J@%w zd@2Ds*0v`!hU`-MUuYNIJ4N!n(v^JmvlSRsV+pU1s4W% zcS;dr3y6I!jUw8?2~-LPA7Wr5ZKAZ-t$G#D+6Z9-GT%qK+gLCP9q7L6vk8ECF3X4C zx1M2oKSdAJj$2P+)*+Gck~OMb?+x|>Id^Pych(ZZ?li7>XoqxbX{g}0!v!H zmX9@zKDz}8<&#%RZQ|SvE(z`c=hVk{-l&R7pLBjnesbxmI>zt^2RLh1S@PRK=yn{@JgVF!AR_VybE zVIYrC0*lt=LY1*RfKivfB!j5V0!(q1Rga6X$NPiwj&I(-!t_n1i{db!i_01r2yrEN z0lf+UDzyVs!_A~W-d!9_zjwbrtaLeBUsLjFheHuhV6x5Lz=aRUH2Iqatm(i@J zwZ)rBbqhA{9}vPnRofuy)}Whz)&OqWaCBgkT#0SLulusR;)L%}s}GXFcL=ST&&2k|GyShct3iZ^4u`u1CX^jRA0N;9$Qc;fnUQQRMz{}d z`vH>nJ080)H;m2q`^AcX^?D$`V^Oh@IE-WRW#S7b^9M_ks;IL`7B9HSKsKQ52#U&B z2|gi0u?SLTbONTh0vZLNgp8L0kdWcbu_%0~Ai<}4i$b^ABM7OTlU9EOH`fVIb12AF z*Xe`Y&jq*B2Zo-gj#mc3okD8y>7O*hbGQP)FOj$V>k~!3gggq1VwH8hM~iq7XalRN}`Ev)(a{k`{~j z2-Tv~k6eyhcLBO`=eS5Gt!IpQdNCiFbnv~6iEuuV&y;ErP=E&D&L}}qxnI^PxH>B= z1}Y)HQ8Ym%+BNr;IDu`Cimy6-UQMfb6#sQCK^_F%iPI;`mMN?(UA;1Hq77 z`3!-3nNNFnW&X%mGZm*N!~}0yG=FHjP(TLLlVKtO@Qi&LRt;aF0DW~~)gK9if}-;b zpg`2G>Q|Z-A3oTMEC6QtE7#jon0hO@5TON;MH_{X0Ag7CN{jpV+mUAq#*EJmeX=BE zapM*6+n=^xZdr_`!=xno0j<`C5cm+?1o(}pXJ5FfG2S-BIsc`LdeVflOAx}EX}l5ixG3m-*jCdFU}qu z2p&%6l%6ygN&dd#^(n{g_zIDZ#kmQeA z<%nP*5z7}sLOulrg$3@m$MYfVk6%I}dF+j@!fvOOhsmC4%U-c7%?ZB<@)`aKbA;J{jZ%VP9zQ=_33sFBjA-n8ke)fLKz_dNo=7P z0L&jC4r~Su@Cm7zAi5f_;7ohm(tfSx+T8J^@Plo%qab(|+4T(CZO{fkoXDZh+ENMhY)3lIk})odBk;lXUp> zwF&p7WGVMObV6}{30nMUqnJk(wE!DJW7n4Bu+-QM8$X3pe8h$kQri5*Ch2RR9y?M) zWW|&x=ZDD?CQi$OjoKU2YP>yWs%?F7kQm9uX`HM(x#4)>0(gY#>5FPQ0t8@|HOA*D z%`io)l}2n`6l?2G0Yiqk=q1eMh`++XYZqtvm4XBEhR=<+7ZHMeB%A>B)XNsKx8C!F z>W@mE!eN^A0D9q+i$nq)zPnz5FW~vt^x0xzPzO|BoA8c+puD&W_)zNjYLunU8e9}- z@GKI*iOx}7P^o|jw{Q#Hhl=?75acW{tASm8hd)i}Voo_@jswN7M<7w^Bm1q|=F7D2}}AngQOiSNKH zix!XrL_I$}Ad4d89|oAye3K?h+krSlb^1>`?`NL%q& z{jA$wnRpN!L$^7#*UsO6u2C)!;Jd}83XNG!01v10@M0&)xZIBGuAVZY>QjNFmm**Z z0)A&*ctfh)l7tna5bME3K*6v#nXAfT`a-w{D4S;Lr&6>armj0V4K8bi23&+zofEHn zKKsusBx7_SfaDIfo70ul{xky!(7${N-1I@|i{!JR2!|IDntNegXLvjYFFEdLMW0oL zo=?Tdn|^Z1eG~XMyRYcoZ+HQ-?hk5YHJV)33nc-RT%9)e!=`wf5oA2-d9q2gi?tTl zbRcC#z&in%{@AgJg_QGfRJUr-8%op>S>J);AE%QqN7|D7HU4LxV~+^*7=C{{y^{IR zPDC>Rek8muES1^B=PUhqxsG59v5TjSCWv}-sWTQNMtFa9l)uAcH)k$l2+Eh!Rx5k$ zN=Nnov2>PUZFOC@4uv8uPJ!T1thl?oODV{-N)7HpQrEBU zfc_Mo)vjH%ED9DUU`67YH@9z9Cvuz#)$N>P@sCv0(JR{7 zvQJ;KFE%E@84C~WcM}cjG2eC?X}~^77QH#T`OqiLX16}Ac44qIxm&k+0^R*6@_f;Y zD**%|@RP^#y7<0_z_1!lJfjbz)So@DJfH<4#t|$_?Bo<%`@kx7SdI8Eztu*)8mlL& zPlwBP*pbUGannyI9Oetcu>QP7_&H6~1$4dimCtX-4!RW2Rn<%ca`? ze^bk0jOmiX^3Q1@Eshw)97rkHR-ca+${4;z3=y`GBjl?wTd7MDU(R*k8Q5U8B3|~Q zrUYYD2$WvV!ExGP*02#wIR-p`eZ%NPx;@{hKKeFOM|{4PSN}I1HWDN?_1GCgkqThv z!|n82e{|mYhhqI7*fs$aoyc9eS#bV16^vo+6@@gKLSMca>U17%0ep;-QHpxsGd}iC3O< zf3GcUbkl5oRfTB`0ESfL1zwsC919@?KLZEEsFn=uxG_*=Q_2dqAGeXnoMS^a@!#N&^q&$2*UTbm7AJDAU5!&il zVz_d->-4&wIlkNUtG3_OdX{*?kZYb;H~W|SLx9iQ9viZA;W}| zzcTkZgJiQu-}DUX%qBj=qDwR+_#?<`e6QT7W3B8zt$B+Bmx5Gl#ihoHXpL)nZN?u^ zG%Ij$K1u_#eV}rkVek;?j%UKYlnJ7Z8spwA{pUcTQ?FNJOw3ofq^Phy;&D*T3CE;h zeys4$N7#=EU`gR3&QJY2ocBd6HfyeimegMy>XRxXP+PbypK)=pJZs5d45-Lrz}=Mq zGLnD+`^yom)z;l`)Qxz`~G5YGp{k*2iyV$l|Te`|8M%vnXGvw#mgs5n}y{jRz5>fuOy2TbC$T zh63>5_Ts>C=@fKio|q;t|Hnhx&Oc=oXhmzPQ?QxGKMaMX|B{*LzWxa*;us%*LDC2={pd)^kH2fTLQ5xq%%`{YmdAATs?E&e!O$j{@Yt{Vtv$>W z1^%~?U>sSK!?aE{1q1}gc@b8dbs-#1ncM9c4_I=vmwb;L%2(qZ1>bQgpS31J=@rl{ zOlfh88j^ah82mH=BOWQ9(znPOJE$!S}00N)D&^AuY(k zFIu+}u1CNngaIJ{Z7#epFFc$J;%GSi)pe;Tm8Z?C&bP(Mtg7*=O%L)4R(7dXQvF!<(a^_P0FP3 zi{ei_<}$qC*^()>@qzcm^DYXnq+r}F*h(fADJ1xA-8<*gYhF#*y650;=i4#u#-?W& zK3P6#i>k76i1^=+At3o@%pWt5O5!zl)#E?sxGXw)k+}BMq!U~7ng;Vd%6wTDO3zDQ z1F3~vXIQqZDyxJ9?qrM6f4e{>T4aIeBd^GNAGN!O2w8nT6y5!S^>|6ly4ch|{xZYb zik6xks_i|9ad7xUn4uhSv#_szQjoz`qXGpJPG(O26GPsyPVC!gDs4_(o~mB}*JpE9 zcn~#P@wOKKP zmu^4}!h(V9vAEf#5Mkt_x9>6C9IZ7q6J}`I8Iopx?%5HNV@*||DCq7Vu6CKwm`}}I?FtPYM?sBWCJ$1Cw?MsA%}ZtVOS^2|XTs`# z8##p^E1!3y4@Wqi+eN&4;STHCi{flt2b3l%!~#l`*d zz$UxpKjQ5mqxCcVY|Q&|9K@;(g6*3{1B_v$4vwF9!or&T@*YdcVFr*ItL-{qNZ#7y z{`KmnM{f*ttsV|zlrk(jzBI9jp1~1}y|tfeun_aTJf(WZ@%=RG4M{LQLGMT*!;L!? z>5dT?&jF%105JLiV702O6l#ATfd+m5e$lN(WFC<>O@y|_e?K2XWae5Ls3kAjfrNof zj01k4GWc(sk9sDewT}ei)2n_s=URyaq+r8x85>usp-zu+@Zt(c;~AW@40X}4`5|3T zE@^~eM`&CLR0|%)xlz&AHZ6;0?JIaoTimgZaIv$3J}!4-KF`Byu66GUS{@liUW@rI z9#b#N1n7LClP$aRCczyRdBV0Mrkm-oTqC8N6HzMOuIk%o$|>3EBH7J}4`HeIFV(a| zgzTc))3J>)xgV}k_VOYs4F07*7qWG{z18A9Y+dmVRS!V_4*~5=hffu}k0m*}wG8~>IxIxGg&=-*@1_RR9F+>8wiLN?BfmAz2$!ExF(&x zz@ieE-#Q<=>s+SQ=wRe$wbxtw6~i+|~J6f@Syi$ZsT*{`NGi&#UEZNt{~2}f1_3TPCu&% z3xr5JX7R)cc~_$fAKec4KaDrDK|OXw0~jD89}h+x?`~2J2a)RqPgfcQSfafhbwc7w zXucy8WLYtu_LD#CD#31Ouh5Vlc*17S%qo?ugK?Jsi1@H+gmSNTu!+`thG+ujWBG-| zk`}lp-~7+-ZO8K;mz3-_TC^7z$N23wGPrzGobi@q zR7K!Px$v=_LdnG>R?tVI^$>8m-_6c5XddV8A$);lAo?vdsfn0-Vu2r-1=kivj~6R7 zJ!L%vhqlbtZLX78U+R&1vhA%>iiP0bfEs}gO7-7o%{{n*UJ zl@h!bk2opuDRZsPZC*zyYKwgpf7$F|CSZNS`@9o> z$7Kw?cMp94>td`GPcXOx$K0uUg#oD}bN)Bxe(+kc?L|@m zh)#>__H-L1g*Xu5p!bg%BLPMV-^TqM*JT~zdy&StUv7kL-9 z)@I5)USG=0mz#Lql#T*VVzdPgJot@rsksgObB8pqST*T|0*~#F$o@Ns73?Es@*Szr zaiJyMY%)c^H=ZbWwTJ@?317^BA(%m23L)WnDD)$)#+e>MaRd#j8oSY znU(MX-5M`kl8iuPyLL<84zJKbI{6P`$N&UP1 zLk#*;EFN{0f0sfr6tl3%-%ii{6T`{8wC?WG2yp@%P(&l^`SB<8OESl_fq)e>3osE~Lg(gn!oDwL<_C%7W zT6W*jjzM({WObt?Ce;n>a$5Eb%U-~%>uUBDgJc%xbh1rd52ZxUWU04Oc2}o+L5pWL)W$-M5Z$VWo z-{a2UErdUUnVpN?-E1);l(xQH@O4e(DF8=|X=%1S?!~60iA0wieY{P)ViK_It|Rg& z3t-97Rrv_00Yj;~qr_hCja}(7Cp2GDkMckpShz;V3zb|sOiWC#CJA09l&JktxQplS zwR{0#-_rPImEFecqD4C+9@*A6Zl%G7@`uI5S4si|a#h|nc?wR6gP292OI=dT6w<0aKTa55OR>0RtTI_1 zkR-dZt|Dqm!u@zgiRrjOzkKmHE;2jaZNB`Q%6k?&g+l$Y+-K2>CvX!?#BgD`*rach zKFG`^%z7$r#R*Fm(^vV%z3TXOa#@Uyeq>P@_r!F4Ccu3@b;DQ2xP$px^SX;fd*1f_ z*bEj~b`&08hov@M3hFnnXAJ3y2po6#>op4LtVaNI90J-2S?dAaZ?EltEibBd+m#FN z0EcLm!si^|`7_)g4i=*fj0l(r$(QWIT?+hT==3ri^ulhj&qyyMPvu;gUII?bdsPTN zJuq@Qbr@^u9AG3R-zv!CxS+@Gm+=*&xThUk;b$YmqK%-aZ;tyc)A8|VlL5aFf&X*5HD5QRa?Y+6IG5yf9Z z;+4}W0KY5#{op+cOpWRNJj91*h=rS8?8A`hlaXqv;(IVp7TwHlQgWx)((Ko}w9%sn z9>m}MjEc$GEtp$-6t>L`G`P0yCKGRYfv|*Fy%#^de4t4sgf#^j+%|-iyP=?~u=P5} z&DYT|+XaUkdrf)q7Tl`5!Vrmt=P@b}p^g4Oh++o1k&6TnilS)#bbD6rSyO#5@+*$P zvyzl*O6#^q3GN7?FSNe>;-s{`3^7!7D-*!<9>PeqR3Vby4>YP!f@8oL{9Mvm67Ew# z#^D@Yl;_L$V=oEjfIiw9gzYS{0t4lE?ht_j(*|0`JJuNXd3qs^deiY*o#eXxB0g`u zQyi!H@ZB#Ht7-*SSbT0vyFJ5VZ^AF%^-+-IJm>DNzp0eB#kyUM%SN3`BGT%88Kvdq z?fB?9^}02b+<@u2^Hst`MjUt~-U9hgI+(YaXO;J-Ei$rULBl?Z(&!oS-as*x`FF~s z;{?jd+LeB!Wh$*RH8ci>l;CrO!W$^bI2#3_1P6|LrO*+(pCg}Ody5~ zZ$9lw@DCOkhNeZ#pDVm1U*+PL!gue-+Vax0`n`J+8$|%_s93mad#c`6CB`&;7#!@u z7Gdc7@EvWCIp{o=5vP!8MrEK`(VU%(;q!TJRe z2znv+qHGY`GnYJs&-#EEhK|Y@+C2mf6^1WixYQ0rY1P!X-khIHW=vGl*Dmt_?-5*- zF-)TZ>=KGjJ6N2^A-Vt!GF!tgEFxxDCA_Mf8Y|?{zGxB_MF?A(^Au5n~E&>hwrzE=Y-QN_Wr)5SmPUh4zp{f@_ES35Vi#Q#yFZaW!BzHZ|?L`>sAiMm?~ zq@yjKA-m#TDD}C9S!4^<-!^#!xkCV8^Dm?#RR?SXmUMx9oLl&oLtej~wVs~(X=(iu z9-`tqcGY&Jun@oCiVj-bq69vXRA3^*yP=(U!7l|4v)KX`VwOs2xuz)MA0Zk~SYb*I zQ|C)<-z@PdQIXZgg-Mn={C>!Gj}P`Fd@OVDyUKk<_U>>U z2$cr9oyNF%kOxJIcWv{nb$Gf*urg}5>Mi}|BUo~;HJ`;(Li!bU4Qn@GdY4%Sw4iUIK_eN_mQx>U3qxAd zky;Gf-73-9y_K0SC}C2KGuP9V%7t)>*lpR|dL#a<^CushACn30Cq`6Ri3zO`MAMAu zR@P$yK+Y$RL*tlEll~9pJrAS>aTtWgTZ7R?JMm&CQV=@;T*bqUDjpasYSF-3>9yo8 z+`()na1So?%h(-di`^l+t4YuU8`9*|!MHcxLRS`Xr^7nGl7iq}q? z0O7P^v**n?$OQ%KM8FFOFYB6))|z1n6B@VZ#b=nIBJWjM@$v{KqV4b_l2OpR0T`{d zR&m1CMY~0wO53XJ((BrM(s|wfIGp}1DE;R^B4_FU%pl1nUT4@{`S2AkUSMfL*>(P4=S!mZ@np%!@~{H$ zWCzEkE|0@}A+uz#{YlAQ?Jl6r{kRGt%7y&$dfWpGv4nw)qRWHzvE{^#aeW{1@86FK zEqN(Q)SBN0te(27_oOf$Htknjh&M;3~iX?0i3zf_W+vftZav~85Y>h#3<&W z$N*wQvWn4p$$d7_Ux$gq*{AH81h38vM01(}z>v zXBw9_7H9tHtkcVu_ElI#n2ACE5!D%{!OKpiC05CHK0ev%{GD(qwQ%AUbkEehMo4LI z*Al0_rVlq_wNw(hG3j)yy>vkDfY#~%1}FCfWniCT>Q{ViFu8#xLu{M+zFzdk?XA0S zYVw?K?_b4YhWUEm9k!BM#%A-$0ExF0LWFpMlb2r=e@77Fj>88dUfBbV#eXcvf~OIg z@d^rL{>9c4by0}hrl9uacbQ8|PlQB^`frY}y3(;ZjI~T{&6pnQ5e$CA- z0(IUGW-tcz34JL}K3VQwIQ}=CNNqW>1|6n_OD0I_Y6nrJru18LL(*=P)Z@(Axysy^ucai z6~%j3Lz3x{l8lKmmgb3AnplW=SHfHCDSKxOlR5xYIdI-4bm8K@4NbFNb=_vz&ilQO zk~@-6jiqI&h)MTR?a?ULQA!ojx)TRt4)uEWC$b{&x41z|z*{qLO^mGMy$Nu+@PZ>~ z7e>Moh{#UdvL<^!LMW0J3l(Gyq!vkc#8wC>t8GX7|2DeQRR6Ip=BW-HOvbQs6Kr0DY%%jBK88O1$?Rl9g~mj^_mr_vdJZ(&1u&d7Cy5@2 zcx-J={m@IpHd95BT8Y-~GSu^N+G&ZT$N#EKB^+YRM*@?sOChGY8~rmf@I&w1&Z-{5 zx^UXpZ(T#0X_t~dR^(f0zLJKOh(IrG6v-Wf=VV=GZw-gcU}1f z7VQagvE7d$)Sr$U$##gcG40m#QXM_ijQ8;*T&#PYPKg#A6JfF9htOW+|Cm+V^J`1S z7maHoaRv!l$iKZSInZ^|28N?Kfl|gBx zbedO4tBA(%5g)Nij524sfV{-Wx$6VC)&?`wSOb}?6`*?3;Ex60bZLQ|6DIrY$@ookA4ZESJf(i^!JmTtHP$v$!26XCf{9=TpC(1wEG8Xb7f>iE zt52W~_YPDT;BMQBg#k`M;vEk(?E#O?)T)!8rY?7>JFebs;PA5Gz_8T>)<_ln3b->r zYNFils857NpQ>8_X2efn4R*`33TAv)Kg5(caR9Ztb3T@I)i+YJ0sSFc3;I5+EDVk( zwN>Q_NW3BT7GVv`+DHlQcYi{I3xwiMW2vio$Vxcs!|gF9KiU|q5cwsX5cPfB zzv-&yG4wDPRT199@BtzBS_(m8y~(jFOX5pxWww_Pkf@| zQrkFc9R&{^y#YKRb9cf@9s<&ehU6WWZahk zl3LTKErh!3GO}A04pCMKnd|F!rnC|GdXn3>);fr`uzR=F4C)E)@3|zt%qe}w9fFvz-KImZ+@LFr<`;BTJ_HRy?%E_rrWK zR=>`7(Csb;K8RfTP`HN0Xcpa0ulpC+ggp}WpV7J=^a4x6M5?URtF5(OaD`HFFuhP^^ zzRUyEkk& zJ}-DaX&pJ()lLLK{EoSmhgo>Yi2I`K6H5^z3#8{hl(OP_VK*HACx#5UV6m?Q|r~DRhlTSYSUZ4=i%6RRxd9X{Cl)eJ2_vs?yF;adAhr-B zizQBj*dUgg6EylWW7;OW|?Fzyh#AV24i_eH`u~8dOt?=1L_@ zetW)JZT>avRJK0@PXuibjxUTXC+41In&16=8Vv+9rv$JExB%K&*{%j-gfce(&ndYO zNpFb2GIlVxE{Osj({zmETkDYs{T(KA$_{7)I`ev5pE&l7If9`$6-fwcIsE z0}H=SeuaNzI7XG}k+Hm0`j9I;lCOv8HLk-&tgDAueD_9%;BIf%c3$yLIs%loN z=?hOjw>Cv_CYP(HaG$OW704z2>d>FVSG{__f{5SmKJ;qtw(gpCo7`|j)oil=^RZ`_ z($oku{@mCMQj_IOch>zDK#B8Y}QlU3m|H2B3f}23s>oBX0CghUmLlacdEZ0!#nz(FKe`{X;fk6Bt|CJ-8T1 zJ-@(umvBj%mGWFlgY1R!h4eZpfhp%J@28|=iWw(S^g*AH$#^PI7@I4VkU44xjYQxN zk(+H%LHk$^WhxA*Z6FLj3eCTc(*?wwk_mD{SW3h{~KNEV>l#4X*sG=?c=i&yqsH*PdPHgG6^5p*jEE zP9B?;?6Wu*!Ed)XWWp3oe{^LtckPo{FJy?$JkLlyZ|f}^?Xt-{_ThXDH-u!k5OEAf9Q&{Nr=Z<AEG62jjBD0_h!T)}dJ;YJz1+A$?(Jk*Bcjhd z*yMR(X7PWi+gQ}nk*H>|@cp{#yH2t=OtjfaaX3MglwifS=Kvhr&14RaYN;B=rQO{fv*CvKfe;s3F5}KuQ}Ghj z0rI2oBW41;ZdiTCGYg%<2DM||@Uq$#sPCu6Nk-c9-}NWdH{xi%(fwi_%i`nkNbLaF zHhNE4reHTVRSIlWhitjj>L0?LDTF%mC@8LCz%5yJ%}K`7$);4B zkLA|Of>*+rT-ii+_PO>!H4VIT;(SD1GRth{jmi<&H_vVNn)WOYayiwGT^_^0=P5t< zPWRzXCs+*7M+ZiJ4>og8wx(xHY)2tKkGy*j0;KoejqWEaIo*4ns^t<*zkhoH6Z-sy z8bL;2q_`mZ4P&^7dN)SbTxKL?cHV;K$v}5Dze;5{Z8;3qRa>!Z}z_6 zM`%Fz;+sCH@7~w%nrgIXZ*;55U8KT{TbRh;v=7Q@^(^cY-sB>c;El+DQ85!>*Y*$J+Rc~JsH(IDYhzJh%IdQ8Fa zE6vPFeCB*?1M-$}xI+^%#Uyjb!}i~q6C(}+EPcsXG8Iat%;JRNoJWyT{rsO#Q|&=q zS;05QiwbesLKD5&on9l=T<;(yNdTV<;U6#)-Z8%?w&|=yEbDy;ErM1na3-v#Fpj3u{HOz=mJ%Y0NJA}@f;yl#TGiUwg4$@`*$Q)% z^o#;fVn*h5L2={yQ`(B1rqI(j+RiVy(81kr4Y1V+BIls(iYrWVg&<8NZx6EF&tuUK z?l(F!GIPiF9HFQXVE}IvrfWFDs!_0%UH`&pM;L5J_|d>XB{e+CmdQZHpJdm z#Jo}6^6Ai!3e3hkwn|<7>GS~cM)bBdM+FGZY9;&5AY(Ls#3e6POTv@ZZdH{%CIlJ+ z8x8IMuO6KIt83fPpvczUtar1}pGX!#44{A_&D=Qo6EK)3c@Z6z>On3g*_U2IM}bF` z<;92EZ^7rdek!BPd<6v`P>&res*fLC3TK9M&d!@;9KYq|jc^2oY=L2^u}I*aQ-UCJbD(e`TZ)+KF|vZ<~h!503^kb9Cz)}b95o-}@Ss!~&Rnr->_V;~XeGmg_95wYlKozPamZin=($peR)1PU3h|uVAMRR!xt*Q&gA;NZ2hk~ zk+o+*A-bQ)jMWY#f92e=z$*uFMhP<%!hnp;o7$H zyHR`hzvIpVkwqtRjmcRd*dDE5EP}l(!Q-M4NkS5ivl1ala4s1W7r{ft=Q;#C0R#yE zLF2~#{~4JD%8>d4g_lxBb1;eY-aa;a$e;B*p~ z!6K#;Nj~*Jfm(CZ?tZEvLw^nbO*06ws0zVOb*l!q{T+H`KLvY$inO4Fr#Wg;lpCbH zN>VC95MK0XlHgYfgVWiFbCB?`lLyZlNTNg-z@XHCeBS-m#hBHtiiigM#D7MLn_sxA z_;D`HN)GhT(Ar7ScW^Dt;39LKJ0ALq z`?!_oIkv>s78l51Om4lz>`ehC=Chmtqb3jrV4xctHJSgl0I_Y^RF)K0up7s?bEVjP zEDKdlHDN|`Le(jF0DTi|{^{UKYOLzw`6ZkawN14}A_CDf;-Gc=HVlq?eIJF1dZ@d; z7MpRzs-ZrisJX2|38vQT2oq>13~9Pk{n)rjE;QNUm?)$qewjl1obLTZx>rnm*1E7^ zATUvg0FT&6W+Q-JA1Wbw8cx6g1*rR!z$>8*M^7pqxwbm!*2qPvW_csxY$t_ka}5by zx;>MdCk&SF6!>){o7SOJQP{r{)Tr6Qy!BE7)(8)4~f#U9Q6K*Wj&%6 z@?;|U=R0r~`1t+($c*tpwyFTvt21`k(w4~ya49N&B>*GaPA4*-4F%k9R5m4l61NJS z7TAJdF+s5W?6*g>M*y!vV_Z5_rN2Q5cPFFV8Mnwy~`^7QKB2`eZP8}B@W-Agg< zw>t4}7nX%eYcCH6{+r<8>8_)m<78u=aJD7>HUfd(UfT>M!_}VNBFZQmDC) zdszxTj62O#hS@$kVXa}+y5&{b8Oo#=lLr1rh)f~B=30$j-`y;x!IX#@+22~2?2`@4 zWMO2Lc~2%`qWFBL22xiiJ>%1r#(7uKVH0XDA+Ysk3=!9Yl6q2{d@8$#e?V}78k6yt zCrW789GVYkA9%|(+t|7p4UBF2L@sov#paCh840=YN3is{{&E34TL!yzS3d;@`NSSx z#S2&WM~>J)@2WlXhb?;sr$=JODDUV5&N$eoJy0zoiODJh?;>BkiDdTpV35kXUgtxk zqjQH`7R(&&9Rpw{poM6Q zQOPX`yW8{$UCGwr2soAE1_lRwsGrR7E%0fDw$-A%zKMKnT+Hd@B=yIHb%tA#)Z8Us>%&H*Wso|^5ZFn*7O9m~>esl~(Z2b$mO5B$zp^afO|y{MHYGXxz6JIVq@i^TL%`H$|4 z9(d?|PX4}55tH9@Y*U&WWkmY~HmyBQRq_Yaf(sN8g2(R=BhVl+2)|^H7$!@ICGLmF z{>~z6of7c9r+>@0b6&2shvzv{KU~UVuvEf7^k|w+%eQp1*!bkp^jcA;6@EPk#R-$y z^i}suzlw5k@^9;=P-M6~{$;qos<9c?fL%f^QhuF|t~e_%_%M+uuLsOUbyH8xbMvrp zo0aeQwToNIUM>$ase9e}rD+6NM(BofG?WE5h^!lY|9$?+hZbzzBBEX=PmazxtE3)M zsizrD5-<+|IQ8Wq`Q74 z+4;5MX7DRV?ENZ+77^%Xf^p1wV8zJXRu?shUM)ZfxYA^lDb5XS*o1S^5Z?al4QEUx zDgYSAVZ>1$I%0-d3BuCD;8WkN!DsT=b#aE|!SFB8=c`GE^5nUDm^qMUwWxd#5q4t; z43sK97%?)9*9O+_d53O%qzE2BM7IX2vzYtK0Y)mUn<2f)ZND z_30U`=W37>h7*-kKu?|L?cdM{%qpjz!jQr|;!%m!))fv-dzp(Da#Foc>mkQ|tc4Tx zdR`RxfHrJ#Rwsz$a*bbb_HslUG*fT$%PJZn?1-=Zyr&o*^zh+`01dz%4sxa5chGD8 zYK~fS<~BhW5=#9seBtx%7lVS9zj+&E(6?M`;6Kl<$@~)`$q>_HWCrcJ0{-ZIOrr8| z_tux&)yB1;_aBd1v6sYvM(-n~DA{2_jhk)yslX++3se)KVS!IyR7HQUG}+H<%;)V6 zMlbfp|AN_cSEmurH{V{}TUkzh)p9N2xU6hkE0Wc9)s>c4%08AIaga34M9bT}Mvsdh z<5WuAqAA_+B(Id)D!ee=6z)%_%p6HFl-Ae9npI?cCH1;@WIO6nyiPUr`%2I^xgX~8 z)(!x?UCT*|sr#om?~l^KZ0-6=qW;f^GNs!2MbEEK=W2~!->uggYB!o44;BXOS7di- zVhX>PYaDSFDJ(KWXD3DEULJ4e`}K}f96&BdRKEq>HF(^TqHthXhBny)<==$^)m#Op zgRwK6-tJ1@V75=8^>*9TsYl_W`~=9ee-WF>1M`Iad;9__T~JdS)_V_5Qzrwx`zz4Jnz zOK*ABdY*d2Id-s@?{VuGsw0H(^W^P0DzyZga+xL({WZ&v&J-@@=$Ya}gYXJ;(6?PS<< zPOu^3u}`X4t{Z`km*!eew(^1tVO$!10lW3O?~!o9A_@bHYQ=se)>FBGbB~oNUbGIY zU6oz|^csKCu+u)5tX0sLAdk|C{*k+k!m@Ql!A^_qSX&RsUV7t(D% zd8%<;Jxfexts(2NaK313uS!hq@{#*+$5au-fG8vhUoK6uCl-SI5A445ta>#iO^>M@ z)aW`+&9y(eVmM-YE=2S8xY){X;Q!)U`2^e6&-Y9g(IE=6c$6BCXK=AwtO55~L9tJ_ z0)e{=9bhDt5)H`I!YwihS2cq~w>j&E3Ikkbl_Y;!7_aG*k^#8>ue))rHMQMx4+liW zR$w7w4X4^u3!6%mp=Unj!>uBp7umBk%DntmIuS~DDFXqZ=D1KkWyX~oo-|Sz5E#Dn zgIJI#FIoU*%RLeuOX9oZuyIN{oA_TZL0b;%PZLD8P@B{q9RkA8l;_o*7yPsjk zhy_k7CQz2zQ}YUcy771fi!}GQJin8*+WHSJa-+(!yqd)0TiK`-0(JG!!c>C~tzo7QJ$pr4yzeI(5vHa@$(bXgjVIRl+ETVnm&lbr`0O`ox`cIU zfoHFxOF0T0bGp>7nlo&;(qUYFI`xA1|D@BUTD5;jO%3YQPM zEN0Uo1D5s=_fuY5{&1CuA?ZEkkimYjZBtNnpVHlCf%m6)e4z2rg(>2L^_mJjj!|_V zk>`f$M64wzf|UfYph+`m6VYDQj%M0Q#DhE0YDGeq{JI09?tKGM0R;=fkRUJh990Gf zjbqoL1=^tPOc`z1+htiRRdI};gs{$KIqs0mRBe4u#O4O4-dp~iEvnoM`?26C%i2R^hE-?@MfFWOk{GXG#fdtVDeI>x$xSHS%8-lh*d;^ zZW#ZWD*l*RY6hK5=8n(xGRv-naA@rq_}+YHsuzbedZ&mQAXyWJio`y)`!#&il zs`u8^Y2OK`PLxASwhAL-lu%DV5K)i9^yIy}n<{jkG#D$v4lIuI&N$Bi@eaSSYYO_E z0c%}czw}bCKn2Ub7~Nu!ZtJ=KsE{$@gLy|16FDju1Z)%z+)6o1SIL>K^MEm{+rR6} zWl}IU3%O-_rLaRp`Kjrv)*1OIv{d}A3(6FS-)tr{scjQ0oS3OAOl=QtbKyDWzPdAPdm?P5fTAG5 zmt;AK35<9*saHvYMA$XkU3R?p5@EJ@0{Vh}IGrm2+&PeyD1q zeb=V~XV=wFNl7lkc*oSd@rO}JRPgx6Tyn}?qKfcz-H?O;Z|To*`8^)(kh4J0{tPGwnrBjH#?mbPt;zRTyuWWUdqHa45br znIm|G5T3019L`ZNIH5(=XKD!<;lxG(F!L0MMKGh~={AF_1IHj@n^NIbZT{XIOZ3ZF zAQsQL1?hQ6Y;FQqwofW6wLjb)tVL0?Ax}b4}C{Q3;}q@ zq*`^ylEs#Bffxl3jn~U3D$8!k1$T9OW(bS}ZZzE9B#Cf2KXZo6hvQlj@p!V?S9Rxg zrR3?h596f}RaaN8hd07{|y5 zHv(iltNTqWi7;N0fD7sj)p(JSrSFpm3Bn5!*$?$6 zhy^F@owEJs5nUFQ$7c`U^f zc~?!VkQl7-)Z)8Nd%m6OXw;N4Y{93igtJJ6|JDzUe%E8tcJ(CdZarBO)GwdTtE`CA z?Mf%z`MyUo-^Npf(|YmiFu(%1$ zRuOPnm=3kx_Vl-W$lm31;zr{B%(V$%Bt^q%JNy%L;<7&An*VzaJDMgNy&N zq#uAI7YnR{e%LXl7v!Ft7$|C)`32+tD`@nvqLde-w2;6XMXIk1U8yNsEAyQQ*_B3f z6b>+)N~4kKVko1TLRt$R4+KXd0qGZZCU8p;U&y`R8B9;2R+|8M=Z`KoqYG>^3DrB) zVM$+aGEdomo+%w0VhKG(FS{>Qqy}fC^M3e|;&%`aZ^|ozt4GCQIu>7<(K0;7vsP}- z=YcCvp!VBpK35*78jJnrMo8weOe6_C>$kKuBMEO+O8ZAL;NL?b(2ff;B+o-{r0eK;j!*dJmiRc9! zQZK^y1?PVK7VrkTdEf@zc`4Vd9-JlABRWlD7mkV%MLrF)ozo;eVfx1y&wFUnK?zSz zCsGJ&H45tN8l|mBu8KFJ^toT3@vq0r%k^FulPuWEmFT}5jvVt(H!|xcfc>W1;tmS> zO%D^bVu)aDo;@ZVcXl$r%+Tb=khh3a;-H$p!vs=?$nfxQ6_v4b8!tO5;2V=34VCi5 znx6kGU9aO^d;&GK-}Qy1_9MfayGgiORIa$b)SM)_MrH<~BF)ucmSOEV8g1En+~13oDKE0o$H0NM@O*7sY5?^PY$&)#N>#LP)*24dS9^ zfnDS6y%W|i<((%o7uo&!jDnP0=F6W?&#OI8h5UL9twMbdurDn~^VzBmu^p9|MES?3 zjF3JyppWFirFZMNtWwJ+F@7UAN=vlnMDLBvvCHWm-Vp6Hn?B2db%GMwf^&>76y|v} zT3t?;E3C!mWxTtS1B(2w~(B>7iN*t)&c8*P{tWWAL zzSUOcFAiIfa~$qSrZSv&KJg>IMQm}E-#xJ`FClMU+D=q&_xO_O`iye_=OU^`-&xH#VqB=vqWm-j*qbXdH6>7==S`me3UpV_fM#B=zr7PP2TakcA`rB_t z9A4y|P+B-sb@pnQCQW2)Q?+O;Yq$~A^xUhxMR6ovlj&l9Elz8kw}>^Iz%}jNlLg|i z{$|y`09-{UI)h~_gQKqie-6-qz%AE>@iT2xR*xqH;sdLK3S#<3VW9bj|BJb#Q`2zJAKbE$kX)(uq6L(4&rDm zRy&&#zgSeNJGJR}+MQn}zC}L67NnSeta(Wy$479gN%U!)Xl)rEc@Ra%fZ;}O;6#{rc5c!?QIuvOp4tJNY+D!t<7#kmu zU7-DBBGY?*NpSxq!JwQi3roDd(LM0GdZjljV+RSHf|s_>?X~+2b;y$U(!~;*1+`Tb z!e`Xpr!+?v?CQ5!_3mo3QljB^5_NR{Y7hq1Ljw@#*0nPYCdq*+E zvQ8*htF=bMT>Z~8Cq=bF*L~dKYJLbVbJBm|yrDG*xBban>h$HD5!*6>tns`s^ASR4 z{E!Xq45vw3{OHnI*JC4KA0Aw)WFx4#t^zG^^*@1rUoqv9hHs3mr?6Tj0PyzoyP z;|L(??&Q!6*%k58!6dNfY)9gT_5ppkydt};5)Om8_d2Jla$j$Bv&^K7(X?!ZGAdtRMpVMj>^t7 zMzoL>Ra9}T^LLI(EWjDTN}q-s+|dv==$989*OT&DlZl%Up2}UL4~~>;$|| zD~Yh1snA5dy_t`2AZACTj8gZ>zV6)NS|VO@GP5s{aZ@)mZWR+>cF${*PO- z3?NGg#(h^hY&$B~I!2x|P)Tv-|J<1P!XUS&@mNsm8wydYZTApfrU!W)DZKvQIm+{>%n3mv3By3Y zYZ0eS^u!DND{P`}DUZE%@(g5aOTNHprg^T?xd~H*-&V~uOrJcT^{?ec^iCpQW@xgM^D6C!pj9m>e-nLJ7rL#i600Uo6P5`^z|Q;kHp~HVC<}S(xBvLL-=1^X{TU(PnnhfJ(X^PnzC4( z{l~9H`^!%~ZjUP32`_g?s$@}2^yW=X6mPy>9Zbr83+Dc~j;!l_51lv~e$R7Jy>)%~ z7ps7f?Ps6^f;b|kG83Yx=4hG$l&|6eXw%EdTVH=`CsK7=X$>>57!$%fOnY*)U+Z2+ z45Lg&vP)n5zPac)W6=e*p7M{YGzxqJYLnz6>qzc(*M{O3)d%V!?hB}403 z5!euLY=`UX=I#*aBOzr;;w00zmMHl5-7!Mhf0}lKJidN7yFS&kOre<;)i(#EDKWm) zv0!7#gl}$dAR1Oba-@Z~KisLVljM*&U=N3|VoUNmZ9aBorXAT(EF2i2390Vvb|BPI zbbNU(JoT?7V5E-;iAKz!d0}WO(g7m%%{M@HcoiFF8v&VUfqckUqCa~v$Whv9t>;Ck zk`cmfTE&l>1`8t+InU`DlxT(m`nu3QSg4kmz7L}h7a<74MFIWs+uDt{CX=C4`3i;^ z!HskqwU^6snNfn!cMpD2W`Y&2SCD`(G~Xf0M;aMkEh1U>p=));;~!j<-Rvw|UkWB0 zj0iH&$Km@quav?)>l3P+r>W1B0F>-74LYaJ{nSa6MkMOqreFGw+)jzhj=b8uW?{Y) zM{q=c8|b~p`6vPL>;N@nEFtMT9**uYTw_Tf(2yZl*HZZ-^Taw_?~Hd9M+dseuk0t zm}lr}6q|SZqlp5u)kLJXi_Ap#^~+vL7%MV`!q(7k%~CcinF2Q*>uxF3Jy9XFOZF3JSp6d@Sb#Jb6Q3T#+csNeu24R87@*Hy z{f;g+uk|H^p5^o$WIG-0{b4sc!^XG_K-r>2I90*=R~s+J7GZqJ**;8GJW=%M0IP2c z3H{uuy~r9OR$R`3pNdW)a-t4|32C+h`?FSObw;u3-}(lxm!K;Sg-{EBt$>4%;br75 zDZ3aQhMl`&J~}w8QoE)$#y8kIeOZ5p?X28inpIZ~{muhepht#C- z7ihv$6@X~f*u|8L8|-PQT}F=LL9mK;$1%)r+B6g|C+(R{FqTOOjp+|FBZBGRo%j`D z5Bg&Opj57ZFS!BfA&tQN4_KT}c^d^fGT^(-Fn2R_@3**UlarI|PK#gP3vNCL)x(hr z3bP|Re7{1v{j`~Z{KZhPA94k5_mpcuVSMw)HTZ;6LVt_G3Chd?P;J82`+ zK8_?Yt6O%lwBJ$WSN5Kx$)cSMslF6ll-hu=6Sc03-MB5q0_B)cmw#@_%R~)ILHh~i8@WZsaAPtK>!c@1$x-T`@ltJpSzR?}Xz~Jr!s+|KND0!=I zo`GSKy3UXZ#~Vpj=CrdIFPxv_NLNQzVO*KcUzH~O<0XlrJLma2@ zR|PweYRNqV&KGN(z0^{K$CQT6d4H{sM^J2jfR@p3@bmyL^Xq)2dMi*3@}MR|52XCu zi@5&!a{5Z;v-)&Ey=aBZ-*8&Izn>INRpdU2Q9^G%Dln^@R;9#m)V!>1$`=c@c+{>&t|2jtp9Gj!=sG zX~iJga1H@n2&Zd0kXw-jm;m0)$<))Jvac#FJoB<>gLUlyh1OrIMMQ)@Gq4UHBR;a@| zE5tBSuXb-S!wY_P7udmk3QRo8_{c-C{yh{>nqnW2dor?B2cDF4qH%2$1Sd>6=-zF< zNw8nPg*hFcTYZcD0?06+wDEHc4j6TacUJ3sh6&*oGS9>6uvL?<;DfAKZVyc_yo5EdOI%t-$G)!L7^snDkW3*D zGsk!8M3vdhj9;sjJ`&sP(PgC&Korzy5)(nRbe5;T64Z4qB3$I5_SH_$`FK`zcU#)Fs*Si-4SG22PP_D%CS+SxsrlE;Uv>fA z(!9O~dGFG|tXKn90$rLYNI+)@K={cZ&Wbv+UAICW4N)cxhG$2R`(>@4w;QzUgx2sznOt2wmme-8kwnXTL22lJ--9HY`i>8M;&#P;XOC8x9Ec;R2Al|MNc z;iI>9?Wg=LqF|5JRsiZQA};*p7}(Z=7#r~d7s;UMw$EcW62=3zd;W6}X+O4soq?5m zH+1>+dF!<@7X5`7F!S;wA1ybiM_NMz zh#{%ayH1Znt}W)K83HUsvtW1o2;saBAF_O&_^?k8qz7BFnAXvSfh~j08rU4{O72u( zFKH+dejdT|d2sl>2nNjcCgId3tU^!AQJ}$5)1M2=2t8;>w}{lqa`UxaB#_~e2BxrU zlF{7S^6z-Co5!H@gcsQ&qSlqq?pz=N#*$hL)7j0z}%BE%nKn1b~ATS5=dx zsty2rwf_--nh#3Lj1G4yN@4c^pw*r%j^vGR^6r*%7U zBQHug7MB*@c>|_(;l~omm;$eesA!L0eF^d1I!?vs&9hI==*5(?NArJ{3S<-1I6R!S z**S7Edp`Kq0}WKfPR+2J1tliespQ?habE*H0aKJLOr>E{LatWYP!`pe@L~3*M!`ES z(G{utUa>$Wr?(JMS|SDCx*3!?|IS#`O}jRUHbKgdUw-`#_ov4Ej56owYCyqspXGi* zxBs^wZPhXN-8~=7!0JBWz>Zk}{mMwAUoM98TLo#*Mo!-6;HZEaoud$fyqXaJnByE` z^^2ufXg0(`rZOB$*$w}qFH{?d4+hxser42Sb4}v+1LCb>_SWC1gx@7=S9!##y>38E zeoeNfr|>Ed;Ny$ZYqG;vnQ40ILu=RphJ220-##*benyNxk(_)m3c3>n>%MQY(C@fE zij;VoL4wnw!~@Ay6XX_|!5SjxUAVDr3eMLap?V1AL=1{$j1`gfDn+cW8lO7nUw%GD zhv)R{V_MLK?!ex|+Y5F8>JYcYiI~Da2Y`@@;?T`p0Qfmzw58=V@~*?C)*& zDTetPH8r{&>KpHm*q;2P0PU@0t{AFZ^TVwZZVYRk^^7!S(GSZ;6J^vZ>-p$AakXF) zNc)p8<4q2x1Y%K+1$AVd3XcUJ!A>;RZ?9~cNx>~$Az%|sL4uD^gE|vbnlO*|ailDv z^SzL@XmMzm^MxpgOpfTAWJbOzuKM(~{Hzu?I|gR-5TGHq_-J?WNnk2Lx@a~1$FvNY zClF2DXZIp4awESb0C8f}9iP75wWd1M>cMf*A%t*Ke`WBi3c7#}k1VG+La&CVTiORA z_;=;(oDpPEnvh@)#S;n4P+6b;z@iiCR=C$s>?><)0bX~6Np{s10Z62n#RB&7A^_+~ zDdpMyR#nxo4%b&*=X;yGQ(KWWjQNnG{;6MdB5WraTJ-}twjK8}yqR`6u;j=pfWXy; z1DN+aOSbWSrQd4{D>o#fNSN{@BNEvR`}dqWePQXfKI+yq8QoC@)Y&ZYCqE9u{L{sx zJjOJVC|AH8h;mb~nUQ#38c3`2XJAr28dm3UQ$_k+D)_lvZPRAPoP^*aBHFgb9e=Ht zCQfHF_#}hKv6=|7TWh@6?U@e~6?|k1HzX5L_)$%pjh-osPM_n9C8)}?KR?%*(1va% zOh$tZ*m{_QMzIBlcT(Kxl5S0-FNKRmhSWWrtiN^aSjBQE6;V=c`_=jzZf2e)R64Zq zqV$~S4Seet-I9+ zbz-CknAF}D;(Watm)Z5D`DRo|s!5Mzg7;lFc!SK1DZDJf+F-kYL^cZ)jvYLL?s?{| zN?flu%RlsImVe;k5m2ZvF`FI!lcoqZ)&3JFZb2ge6qUu#cSLr<*s$F)35*4h{4Zz* zy1`9TG_o& z0H*)f6yUv(XlTMfy_t&+h5;b>J_nHIMx+}S*9oP80%iihWZ!<*9hQnH6%*(eAh=YA zdGxL|_TNM(3b%IiSxd*Vw!Qz0;GsihwhNg)%8)yu2pS-gSLvekAg*k=p?^=Zd4vF) zixb>pd0_FNNbi|zs1VeCA$LBWwpERLS`tb?-W`B^nSR+(+&PUS1(3b`i2{(ivaB83y%T!_W6vvo6(OUa=ZzZ?$q z#ieEGy*^syz&=SG$8yueUvl*f+O_Wx@-EAqmij zQT~Az>UV{ZWD;;|&A)d-f5Jf+K{rb2)obm>gV^jye4W<~F^e4jT^iixd{ig%&_P&P znGOu359xhW7aObZ28`X91aUE;yrY=*#%fUgvc+YYIfxs+E<~`O%}&o+jO+T;Y*%9j z=%3<37o7eum4N?EqRNhGri%n+T4wu@$c!a0;KDU{P^{RMhUHYx%fQJxsb8QH;qlFZ zFGvOJGaP1s$3;@Q2D?IJ&X4Bb{7dYI2UdG$v+8<46L6Vq=nv|j*#`B|5&Sv;n1Jrsn3c5gvSY#xVy>}_gjm`atwH1?6yzE#pA;(Xdp zCN92u$aSW3QAE7Cn#fadJA@Q;tgC?=z2~_9RL!A)lZ*(j<7VIt z<2RY9cTZwdj4t>(8t8yBZ|GZjak(~V{`UcrfXgvDxih*mY0|*n45sH9;4fkCN{-+2 z()I|X@-ef6oJ`Kh4fU6tcss{=9`a%yi0j2RfQgtQOVoF;rY9|u;UXCDe9~mXfK^rB zTz>Pz@1A4d4n0gLa)EX4g9Q>%AC~^UR14xY;hrP!PqR5OwJPn3oxj+bK$m^UVk`(? zz_~tc4HVogF2)K!J1u74bC?V}>UlAlFE$GB0N!Q^oGbjYsoYFd^6gKtg{`f&t6!(C zo>iynj>*j3ui`mvqAfKg$)>>fk?2pCir2}tX6F?6JkIHL7Sl2ZvlX;!JI`wFKTmTS zFc{P;@`oAT0(G;U+n7S6G#Wl`zSZ7;iBJbs*?)m>k#eNr|0vpYL;#(929XgBkNW9{ z50Tu+a%cvS)jJ`9n_gE;MZBfi2|^M=_vweApf!)_d9NSQ4rJ&>5Ptvn+L8BfgC&LC zKM#*F(yl?la<+i0@US}kqvC`O%zagezc5GL8Rl*6;+cH8q%KxB zR#M*G*itr2VEJ#r8FgJ3c3$g#NLMc}kMNR5(={PFG^Uc_uMuOcYWV=zbW?C(0{DnNEp&xIY-Muc%JC)y5+f9C=Qv)X-&Eu1O zP^b8pjWZsI$I#$PRqL+Em?W~B_27X)zjPif^KTVdA*EH_nt2m7b;sDr0)KeFMA{+r z88pEkP4XS#@S?oKc(w-61ZQ`PkN+B-QR>R{8~xogd*G{t zv*B@ZEf4zM5q53MV=Ys)Ew4E>>J@#GuTtwC_I`*f0yN7I?+N_zhk!;*^Lb~W8HG-(5 zPH?_IQxaFo(PoXlrqk>+t(M>f6GtOAb=$K`3GC{}i3JWhj;~HCKdYVJn`RvfL4hWs z?@Yjw&blJG&nef8AX$zq;Qp~XV>t<6GF9s|*b0USaGy*ZHbg8Ljr23I*&X&*)L0%e z^LcH@6U{r3NeHuOu8tA%3^c{w_U7QWyP*%s3aJ%AsncC8v+&xiZ+5uq!1e}KZYW{4 z-Xr2^_vx-1thg)Ho)zjROCpP8E-V{LtG)apqU`CL| zSqwxAG|D8HbV$g`;j4X*W-F51Bm2|d`VUHP!P&!bTI5eXZIaGbYcZTnh9Nd1yURH3d3Xj1 zOvg!jA@^Jq zzvqV$fC)SoX9_Ng8*|k zOc_ZVt){NWas5Oiwf7qH?F*jYHwLwMzh}fjfDGxmwG}Y=h9qoR`yn9q-vf}kd*csohzY#@^1!>NIUq=!#VT5ilF`5CJ% zxT#4l)LCl+htFSjX6u+qd;PE~1G}7(HJvP*BXRe@T8&4sU(oVm-{UcB(A1ia#e8F! z6QHOc=aE<0+zbkf1Ny@OkJE(*G#teC-P|`}<6c^g?U^DjlV1$hblZ#;<4bC6o)OpX z?$&FHjoVM--bQoq+<61&8o-=RbyI)PTxdSLO7p|s5r72y7|h>}MvD=!kOpC?g3fuO zlZ~_Fb68dq3I0@a+LHEvywOQ!O?L>~@O>;4TW)m8ve_l& z{;_A=L*66N8y%8QB?$6TN)iBWaJo=Y^;CAcNKrkV%{us>>$Zt}8nt5~B*txemu@^m zSUu{ICY>|qin$7k1(79L15b}bWI^`%i7ljexJ9ysz9X*9>?bS{Jtn_cpsPc%I$Em-XYvc8i6wTlf6=wuu8M% z{e)wZ^YM?~$Bm%?BEcCR-S#{Ai&6)(;d_jFyHMCSqkHn=h!xP*L&HQzJZ2KUq&(6}y~2y< z9|vNx9YiFs3aHQIe8k){WNgGKN1Nfv#0d{OoC6i!#!+T*D zo1wcQ+Vmd`d^M&?ab28%el?R(uUhnUsnLRZPTMhJ=I3*pYYfE&V5Js3j7SqnQgOS` zara3vdA!TsvCBbCAQzC{RSkLFt}>9ezqW@H2x|)~Wa+xQ9MYI7WW<8kyl92^onjYM z3ya0KH{g+m=_KQ8jE5+A)_%qL!rc3$(xbKDq1!#1^hu$C?aQ#G3f` ze8=1s(EhpBwrc&h68lI0o9AY_45>)o7{fXG!K$RcPwM_~{ss=?*l; z^WrZzR_$1C{K5?sU)nhk+@R4ZCl~n{NGIDL?f_YFk*%uKfi@AE zE)Z=jG~4GXjPkH}YQJzj!QL-{LZ*qCUA12tW;QN9ufGj(Bvx>OMzrX?2yMdB{cWpj zU&<1CiN^^D=~Dn58A`JHtHTKchRii4V+3owB2->8xnJ7t>n;CYVPHuP&$pUJ*=l?K zQCS*RX>f9*Tk1DhB5P2jYH*a|i;kCQz z$257t8%4|%pGelvto}IRWOrxLDK7mas?H71qt*X`&{^M3M)1}X3`~ME~ zqWNbr>WW?foxj3&?{Ild`KO}dKEQP-(Fd=4P!THiL5)Bk%Ye?KuA;lHKos2AI*d@* zxkzMfIN)c71*5$edwRG&3{a%QVlMOwKcu?*c@C$ph_H#$rTBrxcaj=Son->=twuZx z)fap`w14?#@Z(7?zi%?sNLgXFvFIRsld{@qaiP*5}JW33nd2 zP8-H^wiIZ(h0l+CH>sCdVM~#-zOaOc(R~TKTH^vBY-s z3_;z~?5D1Wh}~JR-J2GUo?TGs72s_Ep1~PAy7xB+t8|`a7I{}bT~-?qI;~vby)-D? z{yf)@v`2IRP0=nwJJq`vtid7kG|hqTFd`%3sb}39)xW8U{In zyRf=@BJR(`Su{E>4cpuF(dj|s)x9(Z)i>^wXd94oYe&z{|8u_z^MXDRE_PU=8a<(9 zNqlEo^}UE2%dEE>{QTV?r^(VsuCz)bKx~`fYT5rY>Tp#vL!HQ(u(uoGmag^Tu#|TF z2m^0=u|TY3ckf_M=|3`OLkt}aq9<-(^RX0F+dc2m8XkXiCgA(IemaYz1`QqzBnMCL z+DRezbZU@~%KG$lF`QFZtocK*x(CpWT_1Tx!7ihAq^tdByLWYb?d!w;8DA=-WH?^o zj#*4nl5V(j6uo_zenvZL`6e??B5aNr%qz-dwWLO-pAOx7tmVi(1$g&{poDia9tHV{FQ z`S3jjD59ShO<^eIJ?{jT=HEQ`yMFRp`v`X)uudz>dPaEJ+Q-f2?xT8|FH8EX0y3Qh z!lQ{`C5u{f-4IR>ehk6~WgtF-(E8V11vdRM>Ir0uThF>n+)pPe;#NjZz+EXj8r9AD};#|obE1%^SL%JtD2fg<{#lmC>>>vyLf zod2atDWY!Age~4@3qor`Taj?bms^@}Q3MqVSBz9vnl}{B+;Al*-4Rk80(HB=|9*QCzS6Ts zmeC()5)C6G1{nkucnzQHAHd_#?5ui~$VbJIl3q{X7L@euau4}{1B^Ff=fT1QBk0^7 z$8QF4^#x4=7(a|3O$gDO)L|j~RtpvpM%&w*lWNrAdbdm;_?JPtZsa)(8NmjIazql zyu$EenJdJkOq34ut`$FjYPb?;H#ii#yk30}g;_bBw2iDIY|&VYxxwxiIA2ZePaxbA z!L47B_dt5U=X-Od63N&IC?(g(#<(wteQ*lw} zibPI}0)!Eh?g4AyKa`PbEcN_<4_m59ZU>@p3v~`9hY3OnvRz=cf3Qg)am@ zv*;PPN#o7K4Ev)}nk9l2vCiVY&z` zZ`qlbNz@E)w3ApH>ns)4)EHAPOfp6Hv$m*v(A_w@@AH`2?mfFl>M*?DLBJ2jXSk8> zpga7r?mI+id=_ubZnfzCz5P#9xE`wpx9v!o!E;f$NRrI~qT6}>tZ*Ao}x>ohR;JIun7cvTbYcW!r~rpBFD5jkgUvY3NO zYj`3_f1!u`KXMul^>#juQID;9u%~hIvPoHk2Bl8 zU-3J1*_hw&QkgILq&67Dj{xpG-%f{8Ux5qI<2N2<$CpouUdrqFsI9(IrJ*M`h_ys& zQ$_Gc#SdQ#AhZunnbB$NsHZeNRWJV>A)a!XvMBjmoF;fVMYwpQbdR}ol|~cM8WCe~ z@(5-Asvta;DJj8(T4yj=&UA5wRyBO=vcfP+=#m7cE;YN)!#j-=;YUw!?ppJ=6)?Kt zz9odlOorVIK|1PV#W=V}tX)x_Qo6InSZ9&|Ns?Mv!T0HmaWhdoB#hP^1_e@vribK=K0`JzY z2f~$;2NY|j^>*@o+6vyy1FvDP!w7bxoe$0DiA&HJ+N`8M%n|Qcs zzM@A`BvknVTiI5ZBK+AaBV=sO{UWKf@ZJHBnKn+1#PM1vf8VzCQa1&B&=iadvF%K7 zC@+W9_*AVWUiHz>#Dy|M4m-`Q6|bFk4P-h)B#p8U4?P=QmTxsDgiZxjm&M)ny0` zd>&f46=LK{y_s=6RKy8iFZgU`mu5q`BG{oE`kf|qEv7(0#j z>;mmvXRgU{B3{_B3+t({K9p&O3xX-Dk6 z{}PRNBBEk*S|6k<&rmx@oFG=)5K?_#ihp5W$vMRi&O=mt57y|0=n~&7~)XGAIsL$$Nw>a@x;IyPYJs7@auByy|*n zgliHJ|Mp*L$wtOQl^wAf#s^2vV=(GI5>$z zBVEpUR-B6{EkdBBWv^w&EJ}sJ4foK->1LM<+J$x&>U)_Nik_Y07-DEvM&oz{ZiDK8xJy+_qxg8T1of z==_mk_X?159Qa&MDhOf2ZX9{asPyrSmtK1e-PN=2=rb_3lVIjwKUiSy;5?$&Yj#%6 zyuq2nDl8W$3&(QH^0+5nuv6Ri9{5#<&#SfMJXSwvDbUXH64nq@a|+%S!)JF%N*lcy zey!|~UIv$}1-Q$6v(7y}5Oxmo)T_&FVyj%^JZ$WvGVRJTuJge1t`#BXnCfJ6zQTK$ zUD{_KL7t=C^O)0h2?*C{((72GRP5M$*ryxg=!aG;7Ki9oIpIE1%^}IWOi3NO3OwVT z+zRd|XGzv8uC9aVACNbCUpKrP$szw@1JQ)QacBvDBtw zLoZ%z_w+vB-8P$(xQIJEE_LI!M^1ka`BdF!U1flUXFjab)uttPRb3|hZRavR{@%P2 zfwM}vT0B&m8DA?UsOS^|xb1pRbsAM}~tR{Fls?n^T?veg)>922hhK3YOYvh`e5 z(S+CJXasCbl|=~OjGCCh*z6q*KGE?hogKd(+j$`IL<0pjobVL2_0e-hgClXa8rye# z=0f^M4I}-|B2$27&||*&ZqAhVHhW`tl*-ZOo?InZWO&u9)p&X*f;^qyskDxO zmnR-Gz1pZK{xvV8j(}CYkZFa6STcj5Xm=X}K>La>{BcYp`n5_siP_AeTm!zcGhCfg zzAeUQ$Um(ci#-myH6Ds`GoDgE<@410Glxei6V)DIycAgx%X7%XZo2U)rA_PqH_9?y3?jA_I8nE?2xETI+N@thUq2_zn7xZoHmdn0@n^;!P7l0(Fm^eO;ecTF&Q^gv{E4tl}Dv zuD&~1q@;2uuck#kUXJ`Z#N>gEjJpvxHBYr?0Gijv9(0uP|CpJEJ77q%EYW^T0|0RB z&}e1ZTQdz;A+`hbRlE`M!YaPuef~=|TgH zbFZsu>XW!TV^y~X=WLrt3$(VbL{i`!p^z!JslB${jIG~|rl($a9vNu2%Sc;KCvBii zno3i&+7^g-hn+lWEjlY$y8~=rPyMtI27G>1wVj9AP6%%|0;`}^v}x@z=m0}_U~nr* z7zhgkd1)-F>W+mMnWPj3M==I)!^@nr67&eJoi9fOzc-v`kDf_8lINe8qj3*+@@Y{W zUY-u+h^-h&dJLGk+L?yHxSzTN7c9({{CQgcu)8&vxrNrxI8TE=AP~HfFsGYHt))0J zPyGnr(iyQ5fmb{q4nFNVcLSxt8o))NlPPM$LxVG@hLuE!f6ZyT+U z*VDGn5cF~l@sXOroJ6*}?-zGCn)pN2w-pjOGT9>u?cKo5IsDTz@I%m%dgaTm5YeEl zdmrWQRwyOyk8Hivc~-hh8Y-V37yh(S>FCg{m-!e&hlf3LKIg0D>(NOLwoFB0p@RLV z3kz)`x9fv3k5QJ;=;z$Cfu7Zyi<337qBz=>?IVk?2NPQ))+rfW&T|C z3?28Ff(p55Zs$L`bU(XnJ{^|Coe(x_O*sg8ut!Hd<_|J359yJt6#eo$m&PT0fWCR2 za-V!=Od^K#`+J*+XxfSWsWq^CkqZC z7IOW{P;Hvl)GT8Ek+mdC&{HLngpa-`G96VLYr1xsS9rTyN04}bFDE%%yxgsAj%;ha zVva`cG!Usvzk5qJYrhHe*R4X0x0|83Y#hZUi4*QG4?VBl_28eVNjU1dYeUCX!6&L8TOb8{N&TCQ*1Y`*c;O5_+E87YD}_`a<8b)cK!d%7 zilTpZ-~#=f-tfk2CtjMa8=r!%@yRkOAB3i0KnOJV9FohWGZmkj;p$80rYs+@rbia0 zO^pG^J{kQ%LZGl0j77@)&%abIi(6*|#P%a_&_1Tk#SnJc zZ8yW+>nBk26uOj?QZx zHk-79TfA|-XG3TEIQFMb!Eu>=I&!)gj`sP$NNRIsx`avUW*TP8?vCw#2@_z6+UVO6 zg0fn{VRdK93I+5#DfcRWDnzYTgo zrz-{%415%eua4J8c2=UKisCrQpm846E~p-OiSFGdIx6eq)z=?Y{tN&90`l@xwU+r` z%O3n*mkiBr-W3zg-UmD^@>Al3uEAXI-+bz1-ZF*0uz+C1&Bu)Vb-U8lfjb9=)ADNe z-2CoUdzbC>bn35Z*e+lJUTHJQ2>3V02j1Z*&5YZw-5fMdxbcI%-m=M3*g=9|)2+0r z57Wrx(>wUoF<%A_n@*#>Fp|}%QS03*!Ru)weKN_-pNDscF92Y;Fac45H)TJ(W)){w zK<3VP63hDgBvp7RF#vO2V6|XJ9-!Y`QgxJC7LiHeL%|#cm4b#U8#ZbYk55YMSC?c zda=Gqekp@u1q>@u^Ib*rc#~n8IeZYp2CKly?FcS^r?m>j`LN(5W>6Q1%r-n9pH1@j zF_IOap1~{e76y+!hN#m9>qU9%@Otel?3VPCERtLrmR-ZbY_nln20yFgM0gI!wG4_@ z@l;pom~Cw~6XwJGJRx_(9N28$ak#BH$UzSDG=DRj&b-m2A=R8YuIdkp7Z2gc@(x~E z@3)jd1stBZ|IguJX8#iW5h8|ZOe3y^&j!S6S2D3Jv@O(z^z3yOl6+s4IUGl_JwlFv_@vDjc^4|42vIckSv_Xr)Uhb2b1uhbT zv(icZR~9$tV*RXLhptSvI!+GgSn-9W1?SsgE>A9Q9lEw3K9XGaq3;3Iv6 zOT7x{9Y!2IvyQ<*>y_543(Aj1KEtg@KTjak8k8&EIQ>%10YWbM|ApNDjlVk~6v;w_ z)%0k%$E=U#e0j}tyGh|sBr2toc`{5IhP;%hXZ=4#w0;PEIi11Hv9X!X`^_iPK}h%B ztkSgX`*3xj|MUYmh{%+p>N+pf2=LS}J2zepd zO5cVx=(Z=ALi8JPS*g8lF4kH{;gyjuVexVDQSE66VtJ4I6;UTDMO_v5>+qD*t55)X zd;xEPGsE_$==S8SFiCOD*HH68*AfWaT9@5l7a`}cSpG%nPms%#drD+LHx&LmNtJYN;nt%zE>mdE?(94LFP7Ro#aF@EmobMN>-kdcsaUGe6m`Rx#)G z4|ZT(GX{_l>fD_wt7v+3f4d=Ny48|>e5Pd>(z092ZWZ5-Q9$0{+V@jHi_Z7-t!f?M zmsnB!Os}@U=YH>k&t>pU&3)sI=|NEgm6X{i^osk!z1zfPZ&aXM;3|jOKGrfW_rHwV zf7d%}r@u#dw?hds$>qDG{v8o~*#@_@+M6Dy&CSwr8mF;ZXp=_&|tGNL_j z&7q>MyUY)Qrht)3R}w$PcX+w@>^uKrhW#k(o3g#^+tp*Iu?j6kwu_6!e4_5E!p3-r zlxg(6``LY5f3B!?t=GeBc@5fZA#?RID zt_EBK8}Z`?eGHwiZW9L9+IS2`?O%BO6mVVLH{bvM=)+?RG2Ec5!|@nmWZ=@=O=8vQq3xIPu&$L6iDLj(G0MSG%5{YDj+`o;FYh0yst zYlVjcBGHt1%LwRj<2TJ7>Bl7w`!BjKE#-dS>Fq%eNGXO5BG<9MElM0KZ2kl-N59mr zyX}UD!y#XA8t*(IX!Z_xK_m`n0SQee{vd0g9d>Tbz=<}AGTmPAT<5iAi5S$^7P3?Q8d=xtquW9!aQ)p)b z4KVU)=Yg)@WyFQ!yOTLZP$Rgze&#|vU5^xQOrF0<>Pvz3;~ zq|Z@2k~9b_?XlL+dGxo(2Asf$T7zKd5>6`}>EkDANQP`gP$5D2ft!oJ-_epb!V0TP zTf{hJa@h!fq27^!9S@p&YC58P64WQVWUE#lUp_T#MNO6&tMFRIRuE!Liw4=lrLfT@ z1H6!YT678UpmC2NXi9kx=bz$SQ)(<+iJRvNGE4eD~=EebuKsOWc?V{k6enuXcao`XdC}S>Bn_5Fc z3NG4VTm3kXIF{-gLVFpC-9g>!UN2JsK`(8hoiDMIsUp9#K@IQl!4c zd6unr1xnzU-0$B3n5?OcC@1DQpa*1i=zRQy`=*>I1Qf({8U}}8olvMR?`UaaGI{(* zeDid1FhiC>R^W4qUyrBo`ez>H98RZI6v@IdFb-Tr`PMxOc_nucrI|)Qt)^Dzzdw(k z#P7%2BLpDZH6H_!Fcw$6>0B(^v@JXRzvufO&p?CK? zFLJ3&O%mVabsOU-m+^~RWp^i*5hE~#& z>QpZ!a?X3)(x4X~OBio%DiLxJgt||fSwvLoYD||lN%>UKs$=U_7j&g<7nCVRvWYw;UmVK zNTXjvzRrFa#?TURgRSd{pY99)0eUKLektdkn?hT+x17j6{Xz zmff4$fAUZGe{0lLwm00%pjw&$N9C`mH2;790e08;@t)lpy%IYG!Ke5a zY8`)sAf0hS#Rh3!+m;RI`*-GfMGJoUEGHX0gWe|s(z8Rx9ZbH*r1bdPaUE)(q1c3u z1^U^4>o1zU|JpS3RTT0f!3p?>*c?K1Vjb{Z(Ph@*z7CVN?<;>|6Gd!uQY6+w{~Cuj zuS5p=Z2Y`93&BeDJ@-tK@Q&GW@LmnyF}k}Zh%xKa?MX0Pk8bcL>H&CEYk-Z}Sh<+n zZd}#8m;6D~C1af~Zkg>KDsbiaaz$+`bquhHjW_gjBnu4MNcENhLLfK4Id^n(ki3;= zvd{q`piS!W<0rJ^buFO>H^k$hfoK(w#vQ)_vgX^J{+JbN%h~GBr+Z=wXrP|nN&F!) zoFPk5=#571@peZV9cQjknhxwsSdChcB7;H*|9ERj;Bt5jCc@AFdE=X`KcB}=;f2ZO z;E>a5r{%hNhK0-0)TX$cL<;$|hVt_H zTng{y+x5qO=bEv+QTq0bDT&WC9w0{P&s;7B5q0GkHx@bOYuOJU*RQ=iTt3=-J9wr> zgmySwl=D(2J_Wz6pUQlt-`k>=ZZsMl{_X=bLoJj-B=4O-l*dPXE3ucU)ON-mCI_u6li% z)`>wRzB%o!{hd=MKHV?yd9oyBYOc*xT71zXzya88t!R|DSzl&O#CcuQbp`lBhKrhm z<~q&~wW7mpJm6GEeT>}4)M#Q_+CtxN|CFT0(9}T?bhiX}-sp~;2Z$P9rq`iJS~}#? zs*VKrbQt#R`wR*P(#(!d(Zlxqr*+=r!WU8AEl`DdzAshg$HNX7Eh`arZ+?Y|G)qp) z${V+q-t}%{5uAcD7x`UBBr%k!o0UUyXCM2`L?%Ko*uTgQg>dJ^R=;UF`&W4yk`s~4 zPJ&Z^bc&IBmL&^(u|SE-1e8%biZhgRJ$H#9Cn64i142Uc@~+b3Q1}XG_hPhq(CYJwblewb3sHKe)2(szR}~;WkLjZ z%`?T|%!%=Xm9Sj3lvvvtTJ&N@K61$xq}?!VwwLUKfg_k|ks#!c8Hc0u2IhQXU9I%V zdncCxiLeh4(r%hV!*^YqCfF%T+%_D$mY~SDX!)+&sT0$G1?pdm_VCv^52M12{-7%I zCzKW%edD90wWCdds)+06f@ZYP5`Tl|Tl`xNpmrH)k4iWT+b|re_8EG&a0rMr>DL&g z0&d(Nv~0%hqi7`Aan9*t=OTynn%o*7FkXE+pTlO9$<}&rZ<@I#QHGx}eFeS2+q)K~ zM7|hRf{sXm!+*DpolPEO6KX&47&1cprg*lk|7%hNxYCaZnexP9iE=i2#$VT^9 zQ;v^th`D)v&+xJrsh@h51u$9{z?R3F)Zn!=$Z1>M{!U6rB3$!?amdb#u<@qf|*vMoGPi zqVGjrx7ern%rzfkoY)HpT8akk(2ce420_mQC+HlzIz20BC6Y9pATra(X|U8-zTqR> zEE>?4YIxUgTQ$c@$!GGWX=Ytn?00hy9JTk}zWEc@8D+X{s3ETXY;|dhu;YsZsq4+m z=wwxh0Oe&c1(&Y6>tyweFW{){N-Arw1<2g1_DFDxqcc2?0{p1yrTkj{i@E_~xz54R zoM+$x!R_@lFS6FO_X3=+is|UUG{ZuC#(DY97=ko#-4wm zDS||W?vxKQgp--wd_Z8bna;rHGwGnyXi}4zf3j=>WqqBJ=Lo%gMC`@`Cy(tRaQFyd zi_$xuIF;X7pC0yC>z%*2Ik!7}x%i7-27(R**_Rj>8$*#lf|r8vU1(Z!KJZdv16c3H zgrWb3v?P#uf6DK;J3z9D*?zv>i;9kH9WW>OZK+%7@?-=Yrw4E@S36+-5Y`8Z!hX&K;8f97HXxb5_F#`di9`}mZ-ZG>e>|`GMTvcu?YA6GHE1_g{b7CJ z!zL6d;;00b+-8mK0l?(M2(y~T9luY6_%DwM$^R1)BsQ4!J!gm%ywGQV=l!R%kl>N4 z77Y(yuj;Wj?nMNt=h#q==giCbEzWlqiJkNbA$xL`v?oK38OBQbNf94tndVwQn3Z?B z$#ZmhyEDZEMh;b9T}XRkAu&^vRxMYaB{a^&f}D>=pfR;0FC}*X~towSB@X@OpEjB4!*#0|cYod8fTEa(wq& z1hru2bVV}vGla#^ug{V8ElKV|SR zay7V}gUxoA@4hZP;f8&L8esz2oK@=XG|nz$cO*Be$WHk?=WhhC9HE+iBDQ!4Y0-|r zQ1K)7)@-85LL9b|scpvNMKVcvpKWJ{Sb<=Eml+SDD?Ll6MPXPx=)ro=lQ(sn->^0Q zs1G?ztBO@`G(7B8;np9uQ;U|8Q9ON`9VcAvwx=7x^P#rfAW&?-n#seR&?phadWwZl zo~Iu8_mItgAs;AbMV~>8P@pjT=}|5gTATTL9Xw=UJi$nbNf+r|zZNMJ4I=pg_xlu1 zE24{pP;tIzdmU#Qmhd4Op|J2_Rj+E=?3XE@oru<|S_d@CIs9d2(!P*mTI|zH2KnTh z>a@CMNQ~Y;2jBU^X_y^dHMgsZjEp>f(>4d6ud|U8+rZHXqX4Ve#{&!U;VOE~LOFTIYnO#O|iqPQ;tf|C3)AWUQY@&6;srE$v}HQOQPgzyEWTG0_JX zUE;h$7;p6=i0~CG-d3-&dt1ZWbShTkug8&7woEB6L?tEMGmeh0JxLwUn4x*U3=q6g zn5%Ua*iE*7q{q!81Vsm|KEV#CEL~hSd9|Si-V!jswIsRx`F^$-RmgH_08(nNX!LZOhD8xbnK z4RjI2^53~NbpNR5(Kl)Se)d7kJw~HPt7{Y=4$)$ivw^-7Y=NgZ&NA0A(>K!B!3bgE z1(;CC0t-ng0^qnJgZUtbSR*v;-Eiq($C_kaM_^XPmwo_RsK(79q>(hayb^@;OmxP+ z>iJlK*pxB~F5eTp{Msf+Sm(C&TMOWq8W`A&HRjTJrbOGuL*=f|?ni?q>dsCHqKvv* z!FSks)F;fwP)1TDuD)G0G_%80IkqEIkOh1|9rz6|U#sx@hzy>r4<#}*nBh?ZRjhuC z5d_1_F?@UZ(aZKM(#B4ZahtTcy^#vfvAQe!5DAD z9j-KQ8!0{kofFnUKaL-NV-?sy=`Nu0g_Nk%ssfzZ6UZh;hOJ^B^_~Rw5Pd;FdvQf+ z;J8FMo5=}w00ezM<~R69gTwcHTC)w?z`LmR6E9@mO=jVi{+>H&m`rp+U zkd446#BvC@dZ7dnG=IWX2E-6!c}1YX_~Z`8P=_#fVUWH3^%C!_sb8q&ani!J(j#?J zW4VYJRcgVmKV$hOXWe6b?)~~n8xGgKB7AAdAfAax^F2?Z`iO9u&QYZ%2ohnQLla3A#0ehGdmsEZy!AX; z{|ueWkPTs9d7)c5$7i-FOI-61N{x`s2ARva`jQF-!Qd5Xa?VqhP1q@@gx11xBxkH} z4ul-7d=Hw&GtwrAK+5@CTc+!GXWf@(&-Bxje#SR)g%fh7DoPo*hkL4S=)iVqYa?xi z4j&_16K(`ylDOyJeby|sNgrw5bq#(8AnPr-5`IF;$|m0D!DQb;4!|=4om0Z)IEzBT zB)`PTTYIs5i~)KAmH{$Qvex!QP^a7|hEu=f&3LgkPcBhfqc9Ro_;oMFg;_WkNLTTMw%|O_YY1pk>H804(4!})9L1rsHIx6 zATX{}X#`;=cqz*Nm;|oRAyahfQ!&rW46rEKlJ*u~ptIUleF(3jOYDQx!%DQrp)6la z;*(E=W+)RfLct`sbeD0^_rK65v-~7e0XPa6vmPrERQ9nXz#a$_hFcuS$GmYYz8wzd z{bjBkYHeW*y}aAg8}Sc!;9VkX54LTRBBjNpSttWeE0rKnNxf-8oMB6+6Kuj-OW8}~ z!D1hBRFZ!A{*-wwZp&lXSU=By$?%-`u2q4d9)fWf^>+({r201c2|Hd~|8X5v$y~|? zN{215>rof;K!L$jp`5rcRe%|#YG5NtM^^i5jQy!3IqIoeg(jA8NfT3kx0X9}E7lS_ z-a&BNa?BU9obP;;#-O-oVKUohT2xvfDoA1!^)acC->t__j%st2QrCFzb?6K$5f=*t z*7d2>$73;dOaGj&>o_ zLkU}RYB5QpLxqk{)LwSO-ZL!nGl|rFxg6WD2vUU zf`E!y71--&k^Y-nQTw01kY>`~=#!gbilnRVU-Lfht&wcCvBaaY@Wv$)m83+557<}2 z=nk+7Ow2Edt4>AFd$|Q7`?7^Nu!n#IFG%VoGvq%CQg#4A&G+@0Xau0vHpJ<^FQ3)h z0G`mQA!7jH8cTd+jG6>}cx*W+l_4+d9~{!y0~~qq3e_~yF|+xD`&@RddCO2X;#-%* zL3a`X$V=KyhB(p=zk$T-c{nQtw|<_S==$|H?~!}YC8_%>wZqryY9UW~)|S8oOmFrZ zGzUGDl;c<<=2wzpJw{?OPhOb6hE_g(%C0`>0HVe|k@U=#{s_gkfs{}zik*13nF{Q? zg6Rm)^+164FwuM)rj0YmFGv{-+`>lVJ0MZQNW0+MaaCzJLM#ArV zb%oSY#l709iP8AsaJUj!_&y*gHxZZDP31Mvl}M{cl$;PkOahq1r}#qR*#k^DAQ%VM zGf$@eju#tBRe0p;AGez)U`h`0_8&cJ=lmAI!hve25pJuc45FWT^(aw;C%_TkPOH!s zsveu_j(+p)!+%<&%t!yRtUP=Z)0+}TKWh@zGG4P z%@x$fMjj;xi(l#BUiirM$WivR*|}*fW|Qf!@do!#`6RvA$7-p<+?i}Du_HzPOP((O zmRX!mwOe+CU5~57^{aXQK`?0P!d_Qi88vh*JwrY00Cs<;S=M&MkR$dqdnC(_$QLUZ zEA?RUb;97;F@@%lal9{h_l2K+_LkmZ;41-#yI$dmWUyZH6m3f+F~;pH=;hu;mG4mH zUBq35COKK?oVV}GNlm3~hIuG0l(P6qb6pxGzBx3K1CJDF*qJ9Uu%d}eaFXGK;$1oA#zg&s4mry&HCtZY$fkTrr1JYzNsNqYd3=prkM#m!O>_PLz3$3m9? zNz=p#`nuHwO3rnp>dv47M@Q=hCPV-?*-n8I?U4N`0jN8>KefUkAiZTx&JPTLY*2jA z4Pkc(%qMSY!T+McZfOQ!<)nyuDX9hdY&M6io`eS$#%T&d5kN88+6GT=hl%)P)!NhX zS%h8}LB+fZdVh#0AKaYnv)x^vzK@scfJLYJ@aGOd=7K0bLbaca0HSR>+3)P_r*6*o z-`@-EW(oT|6ZI2>yk}L7vDK@})}1Z!3o6iVY!TxA^c#Z*i3k`v0l zBk_45=n%pQyc9CED!VE6Ok~u%r(3r*Cu0kkk^kPP@ei%k+cno?{kgv2!#qMj1#M@U za|cp1e|$mbyqTJDx40XvGh6%mP}ly(U?j!%4^@D^;|^AaZq}u!f0_C4x)9UY7#c|j znGDidzy{+JHBlYz_K`ijBmGc>GyztM2z4}c(IVP|xE);n1@@A3(RaKAsOsrenoBb~ zMh7M9ZQ?YUwOImbKT(ckjHoJkY-WUfa#tj(FW`mb*thqjUG#z=Q=r`^yEM2ik9@Ew-!}q5*5d4+YS+PU>FH^q<)W@HNBt+%cUhF?uVxc`?Uz! z>L(f@!6aCXCA@k3%FcDmlB2j!#{nDKw|LkeRlF_*gsKv&87sO-?x+J^_Xl*v&q3EK z<7jbgd|&3e-|SUc1KN>2@WiKLa>#OgN64lt*Pg^B7B~LIpvoc$+X+N|>|m-r9R=lm zfd(1FCk0wou@4(2KOj7LX-qFLmV7usJwE)A=qM3JR#uVlRRsz^!Zt$!QZ#&VnsR*r zBD3Xv$`YL2E*!%`G?tV4b`zChb_`v_Z zE~m{U=>< zgKP#&NX?X48|%Q8*_4aM0k>|%p>2-1Ukf+M9s!vwou+aEtkP*#V^>F5uSYs1fM6>s zUB>Y+yJ3W2;7A?yy2ClVQjcB{qC&O(x+}a<`&Zv*XHEc*ud~OfbkCDp4TcPeZ1XLa z>D&+0L8uD~AKrK>an0)Y!bXn+;Z})zVSx7}JQM7A`t*R381Au2xi!;D-RhR64mUFi zumlTZFo|r3qT0Zs=8L08@13q&Ee$d2A+lra8rdF0%kO?!d;!Qy!-!HiB#h@)79p~| zIoF9rLXE`DdZh3Sp!(qE2%i379*t2WKHr;XQ-ECwn7G@ zs9DR%RmSv-H`&JeEqnCbM9$O0WiK{G}`NO8#i7_uXMnBe@bXkr|N>=^3BKslTP3Tp&NhV%pLeeZUQTv2^hk#7D%VM(pro<7{CbQ zBdFckhX588;Upe!$%1r42qIC~?pKO9)f;!GQL(M|D9Ykx)Awaw?usC@fjX*QtouPiRr02{jQuI4m{Y;5+m7vV^GzTb(^P*~R@9nb zK6#0CKH%qg4A|p#RUfUaHsjDirYzoa|6^{Cpko7+86MN9tg&9%7@EYD z8_zF`{QqRPxmTboasS-!|_>yVNcSC`RWGD zUlZS-ncp4IpvqZ!sWbiPx!s_4ad}XsGp1h|YoE#Nq~rF=C&hZFwg9w5CR1LxFb?KG z(b*2Rc&$?^DcN0%Cm1H{KYA})tixv-BiV4>2@JzCZlzCh`m$%$)=r<<`%OIU(d>-< ztXxiT(BR##)eR^@{$6kdI3F9^QeH0}KYf;UWNE0Bf5k-@?71qz@Au8g`l~G8odCmBc{;zBmG<*Rt`OSi>OY=a{j$>b%VkURc&ApmdJ)%|x#sje8Vr>EYk5+10!8BN`%AO+5m*aZ zNTHF|lj#U2qkAyHz5o5eeb=bsdsn`azoSw?VVN+;JHRbB_wHZYhN1fMy>*4*)2Sjn zvOt3$^>9d<-?621%VIQM#NVXT;;RFJDS&<<}6M?{kd28eddmS$gs4>dYE836cSvHA&lr?agQwc-B{PhhVdugFXFa>DCR*6 zef=HPXXAQjRD{}FC$CfRh!%4u@xM%|0*z5%ARYtQz@N*=q(|gDAo7OA87G1vj@aCW`W=AdHkv!`8tE z)i%Gw;}H&b?Zx$3()}Fp!!R}6VY8stKUDS0{&i|LN&W(mz?%rbHAvP^-WVEQ$*RPH zfMC>7lu&h)u=`%0QfUGsAzxM+4j0II3AcIj(ncDNcoeUr&q<$L(Mo2(>2i2z@K1*k-1`GVrKw90f^NMvRV%m z0Uqx5i?vYe`~A7P9ryL*wce!n5sX&^y-x)g;hZOdxv5@C#n>NeU4}U1{J)v%30~1U zIQJcNz9zS(5R(mWUb@h<8x33#Yz1r+{<{e0<>Dany2X8sXJa)jALBM1BKkI;c4k{ zkAo&3et;n;_Fg^?kz!P;gh|&jOLvN$L6r`RVyPlD)$^KiuczQS!}w{OYyS8a@7-xf zW5dlK!uoe@O78&A)@~D2D{07+z|xk6t!97jo3eU9oAw>xA8SjU3jVJc;O)K3H{;Tf zDS5;~^rrZ=#M9r#8Ve29?jOFacu~<4kb*T=3pD-nkCKsk3r%O0s863<3hWB?)t@~D z^u(x(nq>amy;Gf8sCcgxp;vEITIqjs-8bzAHJg^Md$BMw$N#$NZeQ=ie-{T6hK*f! z5q|vS|1GTE%6%pOkA0@fHet5LX=jRSkRN*Kb9JIv)jyXdbBFySS1Fra>7ZKv4^D)< z?wj&AZ#5iOuehW3MX4k?ON8ZUo%SjwlyV7wJ||khHVTZE^7o53(s%9?P)gY7g$zo$ zS!KNYqDz_``-J(Cf@PgDJ7tlkZD5LFm2RsZYEdFIp8b%yRfX5xcI2`q6^bX8eyGyg7)L?aX5oG;z}(Ig4|Kl$v#(YU}i z>{te>Cv)u**v4{zD}LRX>9s~F*P5=gVx}tu z%KY@~{xZgSh5F4_1yh~Mk1a`+Tr$yjVlNLrKPDfp z-)tw(Qx?D z{v6pG+L!dMF91~J)Cr1{QAZ`txFD2pSArg(b!zdl^t*vt;|ewdlIl9Z{mE}=x4Ju<#q66hwJ@NMP@06$BFki2ZJJ&}haE2PTb_MC#g$hDn-AMIp#(@$ z0~{KKU|KnkYg|?-#@BKLfz7bk&v)JzJzt;h0I_b3&v%;>FqwT(AP51)eNqhvF3weh z-QuKQYEtw=HGWfUU3F&M^qjsjsDG8b}u0`NK>4KVU+-GV_C*_qjrQpQG);ncWAg z1Vp@$T!7nRVO5?P(vwge^BF(Wu30tTdR#Es_mu*nn*Y)CqLB#s_OW&*^zEJ-FjX`(NUvF4hOSNmc`!qyph zYK@mCyW99b8l+P8mb3V$-UfdBK(jokl4c$o-=k?IZu+1)JOibO{eFQ(s+7){=(1@F z)vjPT(`oc6^V~Q(DZ4>8dFV@QD=+OiUDO-?;7{Pq5X z(jz!Tl}hMK(p2NW=^gfg5c&-1h%LsYXLXLBjp%DzhC}!r;DtT$ObORI^b%j4Z3NJf*JgXwQ+gu?inse6@9-|0VHEnaZV{mcuy}Z zCkD=*C0i9JtQ^#9IJ35oc&vcY0$<#qzZ==AfP^-!g^ev~HWTX%lZ6bk+R9M?e!F4; z{+Rn>U|UfpC!+jF(OV(a_^+=Bra_QdS! z?en#n;Qp?b8hqoW(l9}?sfEKa{?K2IFHGOvi_3H^pPo;5G0!r)86jul?GY^g5dWE&9|%hMXx8fJY; zolmMsyYo46HybDvF&GxzTgxi&vN)fhHeoO?$v@<3Gy&KmGZm8sWM-ZXcWX)441(60 zMMGX7P5*BZSa07L&_-d9Bpg|JZQ~Z41-XnH+9%88Ph8{b`_G+;8h#6G7h^3f$g4lo zy>|W#;t= zr!bj`O+OwwTJwKTE*Bb=VG3|UGzwio5mup8C^y<1wcE)-M)`kV&aiWI0_>dS0t@v{ zDd2c*m{#+qRXO6kfS+@*YbR;+o`ggBLFp@g54xMZT?S0Bf47D4djuiwTA9NXzODORaWwP)S=h({+E zx#uIS`TI>LE3wJB^t19sR+kFO2kUV-xd@T$KAJLLSj<`IsVwK~YeY+AmcD7weI)Ni zIc$#r{ENBae|r-PE;p_>-$R)gL!T^xIA#f~1@o@xPuc}5+*1_Oh;?DY27LdZUBLf=FODbx_MxZ26$2d-jeKpOIiB5W1g}e;4yN} zle|>(JXzI`na=Pd(%e{Xi(=2@Y!2w%`{dJ-WDmro{kr?&IHCYTWb142`cx?4e-uX;rikCo z`a|Z^;aIq~$e516VKx@VUnIx$+lsy3KlKxLA+mX=(3UG?83KCS`YLU%WA<1_9AyRm z+%fO#rF_Od#-uw%STAKHNrmmBZ&MLaFk(mnlf;M!ThFEw&(kr$4eFAx9V0O`*e|3D zxLtkgpcQsv7#Ms!%^oHQ))y#L{^)zLF*c_0gp<2}Y@|r$4rdD|(bjQ{;%IHn_-hh$ z-D}?KIboqAPA?8jcz2Zdjwz~wz@&GQRc@h`Sc;{aULFmM_ug~^! zkKlZ8J;kGKO9(n?8-CfxepHE@e!DG@-+ICXLJQgL3eQ!p&ta)tfJ<&d=sXmX9;YPC zxgYrcwYp*#$RDi~MHL(;Fg4}*%s;UuaRxq5;tvAwg|4!H!)|{5AOmUw;(U;1lvYUd zH3kaBcMlR%+f+_RS+51b>%uvVmgA$o%163(tgOajDVv-fk99u-6i6 zabnx_dJGiMfsTk=1*~ZhEkhTm;IU*@z3(>DMDzCi`N4|xH1MkF_0#_Ooj-;eV;sg2 zl32D?f*3Mi`!*h1`d6bD0|yKE_Rv$n_~0ecC-3gcRm$`vg`}=HfD!)`+wtGw<)Cu7 zDuy1OPT=2pAq}n_Oi6MzwIqoN-ez~v9Ug> zo@aVCQ?ZoPR`sV=OC3T7t$uG6(V45i^_twC4OcP;-iWQ+gRh^R)l!6hR?G~0o&?I= z{q^p#w`EKb)T6V;8JJ~a|J1pYDAFD+%%kg9SLIc2VfyKNd-p}V_lftx7opRR>?gzX z(TGFoXR!)54uX)Ngqtc3y;@->MDoJ(T!wRt_n9lG$6 zK`v*#7@6{hkg3I;v}#YOz7g~M3Bs%97zm+3UYzaF{#>m*%~|!q`SsY208hTOTbK6d zXbR~VtB!Hn+KBGQaUPY)SE#q-d$0UIX$TLD+FHP#M|($J2-bPqiw?GbngdU(TvZF0 zjaghjVZhGhgWS=e4|{m0HWEJrT~+??X`8ASX?&JHd=dh5ST5uWq?vru=k%(x%Y?lv zIXd+-dF`E3-uU@f&*7{c%~NLtWmF0mX>w~xKRx*s{#U49ukm7A-rK$T*p$)BiF~8a zdhJmnN8J2y(xYG5C+k%{H>GNKk1c-UQ#;rh`?}tG zg4H;9ptG9IlrcM^n!E9v;SJm5-0V(Sr7&wd{vG&6#q7ow=40BXo za?`*(D3L^xm(nMTwW7i07+r0E5A;*GKMvJ_TSoFQn!(Nm);P(%H@5p~X~5CN z1G>?A^{H$M&tD)${FXgUgp!N)#Oy+k?AN1$GIp6iZR ze_Z-+%;`WP<+w+WP1A#|&Eh3$F9zK*j9<{rQqFkt3IWQNcFDyb$}|^#8_aqC7bBO8 zXpiFm=J={A&^_9XGqFng`g|+DyGpYuN#lHHOs$wTHX!<))|Fb1^3uiQ{I;MZ5ysfV zIwW?L-8}o%LKp1hTj)O!id?c5f34@cR#f;owT#K5H$+AUJUDXT%_kLBLrMo4OXGn_ zY`Q-a%Nf6FmzsS3S~5uS%iguIDDy0eM)2K6j7YY)e&Sr&5DL+oMHs{X+8`s<8hg z#d9G+7q=L=KER5_=`HT-l}CrNw&sLJc2kwMt0nQ+1XLDY!;)94aa7o3L-|@f4I8tV z&r2C_K`(!bgCy$%*-|}T7~$#iGiNlsS2fbjYSgB8A7Z`#-TXC5$y+G9X<*Qv=wzd7NB zyai2eHUH#m4cFj*>{e{z@hYv$*e{#bnG^qd+y!F(jMu z6d58rZs2qDYRnMy#x6@dv9Cs|FXI2Pbe2JJbln!l2@)g(m%$~-Ai>=U5Hz?u1O|5} z5Zv8eLU4C?cXxMp&DZa(`>TIUP1n?!?jw7z^{hKwU;Sy^VUMfdxa`ETWpDM3(@}st z;J77V9mYb)5rJ#(Q=gzStd!XkDmQg%?Y;{%=rdm&nh(`15?fDqrxp#w6fw%?!&_G! z+ZKk85nzO?J{Tl1x-OIRG|YbR#Xkz1Bb;@o**U>yPrsKZ5d2maaL5T6dUZBcNe9g6 z9;2+$I@sG>Y$_l?{Iq0q$H)&VK9I7G_haFuBGCgc9~`>)K1mKG zRGCf90A;?ixF(uRVx$pi-} z97~TZxWX2{CLLu_=@40*H|43*PyWPeV z+P_lWWt$pv*;)qka8@d?Rw^EQ0FsEiF8WL+S_e^pkH7gY*sbDr`!FUiA-d~vb8DIq zaZ-ZeBeS0|b`J*_(w{Ridc>wO;g5xSr8)3-|0QJO#BR+y1Ht$qT`0JaOF(tIx9L8H zbKX>KBIZqU)`5&Ve!Z6T<{2o_shsEnVmp!oxwiV>W6q2;!>n1pfVQZUk_{EjmO5a? z9tx9_{^o%ejOXnB{|s&*S>XeWW`x5xQUNGsxEJo8o9LGZ%e) zXELp>El~&Q&UW8!Pv5}jT}NgsUwm4D>3kJ@24v%o426y*PiE7JWXAtAA1y`#)C|zg4+G%oT^){-SibLKv)Pr=K649jb`ardy1SaAQv`3~{Egfgij z9z}%lfJECz|GQ>+U`5Twl^U69KGx;XuM2(A-L{x{WZ7YlszYx)9xl24If~|vQQKrT zky&keM*sn{n(glUBcR3dT6a*)>v?jSYse;f^s_S$)U!2=wlomyW{)kdm2`aIoLF}x zy{P5P2VmL1VO-tx64K~M3zKI^^CnFAOwb4Wsh?Z=j+Ov zZ(9k}Ix4hW%`TkS=m)o`U!z}K&K3*8bLp;fdp6lib{qO1K3t}$#AexGbmD(OKuS?b zMT1dTXDKA=`N+I*f3EcerHcWbUiJ~hp&iwv+hN`I{L8n?Z_D>F^%pB#+68U1t!LI_ zra~0-#RQ;7PFP|N^s~udV6XL3!}G~J>bMKp;OumPZ3Kksx1j=`Ha@oRg~udxaZWz_ zufumfz{S99*n2DI4<&NvP6Yg7@1U#kHXN00K&}M7b5%%qM?jco&R--tEXxtNwQ_Or z*NgQa>!r$|EfC32fdq`OcLiK}vTt=!hmG*!*TaO}Q2nPpSZJdR6x{#+Q{cjaDt*zH z%@-(q)P@&7z)ahQ>wk@hQ1Fn;33=nWOQ>Q`@s36lC9QUM>HUsW(t<7py-srX+qStr^3frkaGBa^~+kV)SKk=z&{#PRy z1@P|r2`{2#Xjgr*7WwXp;@=&mG66+=|7)c>zb#b(S%|nQ!dLTuS)Y%5CL05iU3cHY zi2wq4IUur(BGz(Wk0JqZ7PdFLd!pM^^Y^kOs#>MZRpy)cFxD1JR82seSEbjlF(66+ zq!GsHmZ#I^CVU=ZRI=7BWYxU&S@~;>jZsVLv(A z&~!)PMA+ji^%fEZlv`jUNUpe!)Kl9(x zLc7skHj|edj}0(NgR`B@PFLIA(liU=DVMFu!{qV90b-^*xxEs@6Jun%UB#0({}MvE zu5qh+gv~mJMoe7)v_@JhByXIj?)b39a%;AyJo&+kYIPZP`2|)9;ewiXgl}bWO3hhA zzUW8ta*THoexRSd%LUgT@MIQtJw)<{?KxhxAnY|s-vIC{+dmQ`xa=*}qfdP}=BdC-;~?b&pad(?nZLejXiC16x}U_y9Ecb)s7bWrs%Y=|Jpw2y(d;=n&m1&);7R zVG4MeR(!by3XT*K=W$3MU;avR+=A;@p>a`q1=*;2pL z@k4pbJ5QUT7Yy^h)gF1d2w#=$yGXHiadm&ywUJBr2V{t(Ko~WT5V7YjW*7_2Iy#@) zu*B#9-VGW?l%yz>qBxKednaXYd_UNEM8g!q2<~*l4_K*LF!Lk3+kzoV;cz`Q4;ajN zd<#e=Neyfvv+HLj;Pi+{DX>%i2=;jjYo`x3mxc31L$+??c7bD|B3D@cg_rOUcII$hx#oTI9kYb9 z7f&7U5{)#r^Xn&isNJjJ*+nKZ*&+F-M%PRMujbdCi+{m$6R$AoiZIC*EFJu>p~l1M z-|#g?Y26N>a1>j?@h8N}7;M7JXwd@<5(Kj2!LshfXWiOD6cwB{_`>3L^I(^UACcY1lMpjLBv6i&!8S( zqR&fQrYaa{#wV*cRYC|Mk?=Se;V4BH(7iWPfd-oy6>2S+i9PDs7~lAWbUk@4t0+mf zjqTO0g#*-uwR8o3(el+>uF2u!IU-#yOr(6u_p^0?C;J&Mtg-uBDuyJ5K`w>V{;^?z zgD7y|xb7BeIC@?$egOlVYblz6p>Z9H4lg%M3I-ug=YfqJ1OZ;scSIq$Niu^`gGy4$ ze42LQH3awH%=Y5oK(1w|Nz-R21tts9AH8|Ci0TUQt{}IGviZOgjvN z`d8lCv+N*9l>@ASsBY9hq-&*my5ksJ!#+unM6_8v3td=K?S(EzY`yc?phe5PDVWs- zAUb}4>1`em(~BntI68m^~wLMt{^x4@}Zs==o=&LAo+o#tW1;@{Xq; zLIzlRNWu`gv%wY@IedhAmw^bvBCH%Ob2N3x6{aDS8=VF`^x_T7F%7S8PPX<18tnQh zP#nQ>z_@$di7M$SA`=b#gAUItizxV!%NYhZ7yvGFUiYMD{jpT`;~RE0wDw;Q-=jXB!NTfYw)NNHC zAk?lh{JsCXvr5OT(%=nY&VHX#sn)eikwW4~njkX$=|D7U>B~Ay$UqE=AcOa;In)G@ z#~A`1&dWFOm|t1n6zGnRimmSYyiUvIg1eCe>kN5ALO=bX2z zf~&l9XR67AnFu$}?b4atk~kYD{dB}9v@GlQV8J3(STqqRdQ>6rK}5m`1!Hxq1iL_3 zYX|81dbt5++wS!)e?)9Z5HyWEMoocC(oZgdaXL<)_g8@ALEU}b*FX`Y2}KQELj$Hv zG+1nQ3OZfCt0Bq%ZG+TDA-Thhn!NL$(($Jv^uxqFnQ1{X9NBYp|s8(QAtV0Eb9-|+pHEFg|Byq z^5`uaUw~fX@!!sshFziMa}zwnEwaGm=~w`R_5p$a&-(Tw+j4Pl|&%JLbr77Q-U9edWK7b{?I6q85W{h!XjoqrGJds`D_9Pf|N;>eEhYX~iNDTFPl9j1tCndN-1MHK^MZr_luq1d>I;NX5w^!xT4_}~hA?eP89CH>3|QB# zYIS(Gg(BN^4r(joT;LjJDFl&y6b9y87J!hEP3CXhnGHSdqyO&%+F`{6o7rf1!4B+J z2E8taDsJ<4+@<2M0Jn0ehpO95v*_3L+SXe>t&~-dou@%_0eP=^rf5)YLWZ_f6ChhQ z;r4bMWEMre@+1}D#fbQ7wWttvg@8#_Yc-_=Od7J}+~+A9p-BdvdY#X8G3b1<0-YWo zwcUpb+>P0oLZFQwt=e|-p#R}L7FH7LdgDTQqeHRRId#fWpddTqVL%I62`wMId8=of zq8Y-hHoIBgEkFqwx0)s-d{LdYg>uk@<s;!@}%UAGs8U5zqo4-tUNWu2kuY=A~? zN)CcO6ejh)O4^a^lnb5h*(Tb|`3w`*O{s8nBVBcCx;=pjt&DL&j`?7B*i^@C3EORKHau|5wlxm3QZ2jf{;%~EHd#4;9 zdwkc3ei$5KCJi$wq9}uI#~bIYl<7O(@r0$%szvh*Z?M@Y2N_1AvzTQsdY}j!CXA2k zh8x(5+Fa@G(%PtQdAZ+xo4;VWBbPu6$$?`K@+|r4a2zwjJ>ZPa&AE$-Z|N< zNFf;Fi{xOSq|+)h)XyBA+NuDkPO_I*ftM)NZlavO$OMn;b#xrF%Hf+5RweQ!jA1_q4AuKj+9p{e&-vXfP{+%uoQXwGhC0|=!7_<<_V1pm?+{$N zM^7Te-apAe$i-~s9)dX_8wuX!kBBn(5BBrQe1sT5Gww_(EAQ)(V8r&F8BZ~Vx6_#q zhm6+uCovlBLmJZtEz3OLxuLx+eOf|kB8ci!XgTVr)l!#&h>6oO|5@04g$Zw`U9?8E z`5P0X>c?O#<~C#YQ8{Q-vP2O+hY9RG)em@k(>`n_Ujq~;pYxoSnP?uu*&q64?c~#7 zUQVG340YNhZw877>WYj*V*j2L2)+9XST^>#T~ovwjABr!w=hICTT@?L#60+4s;3bf zyVGy8V4=h})$IR;<&7@^@;@e2%Omg~3e|G#%5{SMsz`;ID=uR)aQcCKUBkenR zRk_N@w_pv+kd%tLc10L)bvb9hocSIm3*61J0cn+T#d;lpY=xC)+f{_b=M~8f2v#;Z zp5>k{kUW%>InyGJ;q`t+ciZ}% zl>ei+nqa=+LjP&5ZXxZ$DjIzOl=%c}-TtHsL0l}?k)%^&0+`&%QyU}lYzMrBRJlVN zZ611V4GO*S=Lu@MGj1I_k-T>)b|Xn()&WMX`k9kfHyPu;&^!ClDT?PG)yBJUt3_FI z7wcgftS;xDBtKltw*n6Gu$l44Xt%F{&}|fWvHl)te>vV_+9Y?Q^i~&Ey>{gd@Q9LW zgm1%TDZ7F;N~hOd-#(h0+A7MXk;a(2LfK+GibyH*4T1ykNW)H?na z*r~_IQjn{-{e(O$gbURMeT#}wrT+<4**8HfbJgt!O9pJrC@dKdc0aSw;H8`2`byEB zpo)B;RgE&bN!~wSJl|T{DT+j)hF?8E33aKZfuEUR@MSE4x5#bUhXEuj@84Vu+Zo^J z7Ps#nRe(CrUmq<*6!7nJrxqe)|4k$xsf)8l+L5u6s2hdRw;W3K%YXXB@u?6aOw+Je z=W7P)-a{))i8p3&IQBN|I*PM1GkGUrQA(P1Ia;uDdRuTyRNJ<{SgKvKt9$}}g(8Y>- zKwZbg3xpK}P2#>%_H26ho{J+YCKB^Fnve3B^aclqtw=-=91Z(e;G!#ipZ z)Xpcj!uIwl_pYTHv%cf?2A?dewPtaErC2O&Pb)2-Ch&bGf`ni5z^l_2`Z>H0yK~X5 znWEBYzQ+7>tA6ko6=ej}-+KpyLLHEUm zK(eEyy)6$|!PUaa#E6%IuFcYWYxZ8mjOxhAv!PpARN{nIxgdDq^AL)`{oWk? z7E@Wl&B2~PPU|{PtDA+)FSj-JoYpF?iI+aE8^fDlJUzvV%@ zMN)9oHAbsxwM>>@O<E}Bw^cA^5(FjUrSZnQ(quWuMV8wn^ zq-5wpL_KeNt9IQh+_R_L4bSa?3aYE5KGEX!9(<6&A9mYfzE#C()b|R1sXl8q*d3)$ z8;7lGnP-k)nVAYM$%5)ySP8>m2B( zlKrn|G${QgAET;fY$T!eAR@RgE{Q`U)%E{I-O=rvOTAl48r$#*b9?JODgr=!7oY{3 zWIC<0xk?IMpou~TnE8$cRyQfyE$ugZFxY~eZPa{pwEd^;?zZ`vN@I=iS`EIL=XJNxta?d5~bWIB_SjO}vLWv1gV*Hf54t06(p5MCoyAA9c?9JUT8wBmD6_zRL z=1#sTEyR@@3`Zy(XcR=QHM`x*7#5pIuB4sMhBvQ6=jd=7S|o;q7voU8>>Ox-qim|= zNOm+;K&J0&W6wUE4ixAsvySU#fS*V{T+Mdbj;CQYD{2_$sj@PdPt}h9>I7+SR~21V zl7b*>(D`o9abi%xoBz2)7h8t}A&s6j6Xpp;%8w8Jc5gU~x5IcWRmI+P48J8;ERI48 z&8?xcCC2i7uukY4<{0&_tzUO6uZuNZazpko2xXTs8|<0mf?OP7v99gL&Mz=Tmt~o0 znGQx`Eowjh;I~>n} zh`bU`oAJR$7EWz^`e7f=EKAG!(pW(??=$}~Y$rvt%yespn!Q=RTXguM#m8ndVm&o5 zq@lGo<%u+k6Reh2qOQ9jPe|YLe6v54DY+8M>7lFAyA2PtaxT!Z-2T>RT{WRU!k%+# zpCHDxtTaaT**2t}GhVL|#9)@J9_Q6bwH1@^EQD&WOBr8Y#|3moMLB(H>MmHS)Z6tb zFI|%qW3oIHK%-w9FgB$;pT{}(pO&Xg>)8`*i7GAeISiF*7U$hzg#|E4jU8oDNl)Z0 zXkn{c%POA6`D5~p?)2eNyIzGQQtw^m@@Hbh;n+8U8TlJGZ4b>$l50d0}1v z^`DP~kc+RnPWO+Mul{S|054J5m%UgwWYM$QdF3JM9sYXt2L#4-ofJTjb#;61R<KG2=2l zbD>lI3(U{O+iEd2n;s8mW!9Mk5g-~b_lFoj0X=d#)uZB;Cw_u6-KOP&&t;tL$$8ED z^!9k6v{i#V{a1zFVv5f`TI$XHZtpQ9SkKiLgs<%g0JZL#n3uAJJP}4Y*PK#7r;P-rh#RFu6bi7}?{hlaL+>tmoYqyUWR=TPQJjkMfp^qCgZy<91tY`jAOQyuMEZ@EE_}D1(%6#=tRX9GcM6=Tw^;}1J`LLkp z4`-{5oP$=Z_nvr=acAeC0ewcZvr+^^7DcPxn$F>1T!n`D77#dnqSa%n{`8dZ*!!bG zqKu&0e283UOdp5Ceb@Xy7UgWI4x9bDXIX^`Q`m@keD0;dJRpZI)-3t^TsV-Y9Z_L9 z_;@i;@@sDO$$=eR+8C7dsQv|Dfl&$4(OuxW>8+XOohnzmejAFV2y;A{|Ni$!IXP8m zLiGMvs!SROB>!LbyHc&WqQz2mfhS<1pDy#@bdrMMoIn9eVY)|{WS%a~=0C>bbvc=fxI7 z97$$=*dNTHh;xS}&ue1~DDl;r+?|E1Ygz*+4nO2t&j#xzI_#PQp zyEI;rZB(z3Q|PoMH8(LG^zS!P^sD1uY9T1{>`Z?7`ZSk6AsjtTabTG}dI*oB+e?Nl z6i5=c#XISNi>S|JmHPCG0~LRC+e!5xfbV)^sl`0vfD6vaM&)1pD{jf|cDr9y4GqEC z_eQI&o$iW-_Y8bY5l+d%^FE z9sb4FeLuA6?tV#&BiQ~6TK?;nYW)~&5s(EwH^J3)NhTtlfC{VSU``9-vK3-4voj+A zB@=UzhEUX5!W|;K~vm2xNZ+dKS)gt@$?dwJD!GyJ@qY zMf^tYVFm5D9` zSNI(c5yM~^Ds;-MJqpW6t49~Tg!p|Q)=|RQ_j0x(BU;=HZo()@k+UE0e?8_H;bk6r zvZDPO`f#v~Z4tV=G-{jP`zoW2tRh(XUUtX0>_U%@gXSCmBqdxfICCm^o>tNo6Iw z5`vj?Tu~5_8miCUD!)409F98Mnx!2v_qVD&5cQCr%F-4~vJiP|31+3w|ch-k&btgE=L% zKBty=I9pv}e0%F(oyH0_wq$>2lTWKC__tYnQ>Jwhzc(6Gww=`zq!H=ji0{f5o# zg-MSkwA;C*lN=I>$0LHol!%|KXg^5kfYkZJf<)4tODXfJWh;>Iwvw-Nw72cU;!sUs zD{s2ya!E~OsG(1n(z`HFb|g)=R$ z)LfhO4H>8z4MZl)Lo{WA(1>sQ{*`u{#d7ND4khxZwcftU@Q@CMgkpoK`@(ROtchE` zoz$1@6=?_#tpeFf|K58a;BT0(M>E;9mYvP%b$FfU=I(>~1mcr)BWLdxjH}*E`)fy>8&XQcG z7O#(2nmzzks6eO1S*gUCaqTyqa(TG%c%sHiczT@o;Z-%rn|5zFVaD6}bXJl2drJOZ z=bv<>(KJE%{fVp!dhgMuFKWdM;-NTGZn;dK{r*Hf%fvH~>3Kf?_EdSIhfwTG0MNpW zdJlkBx{P~^?}}RwS;5)bUE__#s5IP!kD}#gnnCx}?BXSkDPBA)>THbfUEn>7yCsf- z?=Sc)uvw}4Fk~M@7?=H6>&r<4n7~_E0+MxLeB)%vqEpwQha>nvg3j7AoeZQ61NpHx zsH9NIFFB6`p((DJQ}jX?L9L|WH#YNI`y|G&yV1yGzsh^C<@%lfqaQ9nSRW~pla(D~n0fw2*jRMYYgE{1 zYrWe_luX>IcYfVBq9Sbnp8m8fw+uh+y&wE_c4)DiPbfOYr9Z0K_H~ySOSC)KY+c{V z%Ip{YGv>0K8ro5^L$nR~+h#`fRqb`MsJ09ZVNa!1&u_2s9$*bIR-Mg!nLyp42~RQv z;l#0QM1%z9f3F1LqxN(*!4}-L&s!j95nE8{(71dIm-%MZkdslnHpRirL{;^&c+NvD zY-a&YcmXLLPl@ejR)0;&g>b+0VCSyv9uVxTz#Ob+sgnI(#s;w>3PVstKCq!8JzPaN z8D3f4&;*1q5}L+5Faf$XY(fex8r(0oo;T=%?-*XvZ=InYz`4jyjM00sk*-_iWvi2H zpijbJYFpf$oc7!hO!>hOZ~5P}Zs;8?^>1F>#K-}j%xeRA2^DxJ&A-k4#Z`t zB*A8>t;pX6_tneOPY>hndhY5L7ELV$o|AX5NBi$z-W0wq6I;{F9jGQv(0DUJ?i)h1 z3Zo22Ax|uO1!p-QuFl5Ccc8VvLfGpEEE3(2tMof6eHhmh{p<~lE{@B zl?{q(N+bJy+72xvXi8@;GQMUc=(xF?l*ymmv7pAk?9GDjGQH!1ST#?7lIg60AkROS z)nXd`AqBJ1N(AVtS;{&)O*Ci>Z;&JPSI!O}3cYtcx->3GT+6i;D(oJ|+VYPXwxo>)uwWHn^{E}>G++4Gh|Hp2&o{hpP6ejU`T~tb?Co9 zDH;O%zxqY@8I&ILgDiKa>tvZ?WNEdhtcF0X6a(?2fg%)(K1#d3->+lL&-x{4)yyD1 z`^R8wl{iGNkX-Rl2|4`8a!ui3eVR|3@#4N%pB4GmtDJ&DX=K$5E`7V2JqG_Kdc%iM z*4wOlU%EKqN~^mpY=J(QWFcIQ7T z(f*b;x^rin5rQo(b~7{)YX7Qj@@K=G|Ipsn%}3Nv{~<{%cuEay700?0GO+s5r_i4c z+2(0$L+qExx&m+p0V#5uW=3<*WzP@(F?<@O(IcPt8<2{~0lz^wEuoIEtko4mh&STi zmMlH+L?;kVSjq5|32PeRDJ+Zq_@%Vl>~sQfB8FI9C-wPExI)2h{PlYJmsX$df(rqj~OcR;0}gb<)s_2*7Eg7eo6OM zwBAWKou8lF=Ka&&I@oRYsE8v$^ceIhf0Pf;Mj#8=JLauWg>inFX`om1I6MoYbZJ|i zHkQG%Xl&1!)mBjc`}#vXWX`K%gJvzyr$NBZGMt&f9oQL0r;#*$TY;~+5^g}RWn@Nt zO5L5|vGKf=Xxe%w%ny3W1A}oq6lc6=A1I!FKC9U;MI>2l@Vrh7k9gK}r!ANFrx93s zR5ZVRp<{21PkdSE_@{CcI}WhMwH+2F7$S2A0_Pj{s0rrJQ5DiXa_ zh9hYK>(Rx(XOGfTnrl8XwGF$v*2^xGVRfh1x;$;W6Kv^5vU2z|+}Y+3e+~W7Z=!4c z%!jW0-X-@>%l~xf8~B{V>Q7=yqon+B;PbOyTDJTM4X<1TJc{O1YOLjkR%k06pUdJ{ zsrDq37F@P6lP+RRwbH&liyRqwS+{Ml{6OhQ42`4z*>rmpF|j@=(3$MXa)dIbmFt5v z7WY&Flz_F+fAU(pA5u5npIa8(fMOne+o$)5#_SJ|x#>`01JGf@iI!#WzqY{&JO-gj zihcAvC3Cd8p{PJ4J3Zvze2Yn-X7$njkmqQ9?Y;Z-h(-$XCb)D_xVj0wzLErGbu&lf zZjxO1A8Nb-yiiqQg!;)k9U3CL@eE1%k$nE!r)R=)wHP`>q|ZH56p!Ao57M@nCY9Jx z$v6d~HW{uiKoHqKx)$d%r5fqa#n9WA?OY!HR%e`{3$$iCI0T!XaKoZLu+%}$sT+XH zsa?$JLn)f5Cto%Nf^9lSKunp3qtqaJ-tWlIdxoLIuJ)s@fk~|9B(KuZiB(msQw`m> z*dX*qw?NF!G%D2I_;1QT@Z%hzM$rX0CpScl!>VZt8~WiiQb+;P7m8Si=hC%i6ekIS zHLwG?7XJ|x5g)11KZlt!Lc$Le69Hb5akxs?KO5QY&0}vh zLKr_|6)#@_!Jmt=l-ZxVDyBo&&Si0o{uSS<;lWI=@$YwPv>WPde>dtwbO z#y_yYo!@ng&@2Pv0CUy+xEyvzB0>Jb{`#cn(cC}3O2b%#6A_;c!r{on* z_csM?O_6?vC%nLqxEwvq7T1*QA*~qM26=&BOgjxXCy<3vMMVbdzmAG15Cxn(kxw8R z=5YL$!=!^FD>=j!^i)Ku{hdIlBexEDIj0*9PBP%i!aHWBM9+*sgC#*8>s{&`WmXY5 z!^4bIhF$jnY)dRc9~jaT_FVERZ)PRoZAjSr9R5LB5=!B(H9N+A0wpMjOW2S=d_I5W z)4advcq@l+D#EKmTlK)MzsA1-;Q%sq!*koSLtLA}F+4@`86=jtn?NKqYDm~aU|fez znsaJa4VzUd2Edj>{yZ>-?08k3!-MRD%>A+L`-KNorA*U#9ngxE(jMcaPwd?L6@F8;VnBs zQ=*OR(2YS3FhUXJi5ZAh9i`Y7y}x@L2^4zhYfBpzHD%=QY)AUp(S>YyzC=X+_kVUK z%4iLPB;yKu^i30zzo%+YU9nrv{`kj~EN-vOL4wReCdM~a=`H&GfU|@W#Ol~-sTxuw zSw93l`sHIOgw&)GVojs8E%G!pkTvU$`Axq-9}Zn7;Pz zJ*560SK%k-{pfF~y`sqF6YKYyPt<``%*O*_@mJ~qI=g?0X`h7JZ|3{e$ACkmi4;@a z;aBi?xtd{kzjy5l<^*L zX^*+EFPt2D*}BL&GLvvc%hg+d2RNYrvW(qdl%5*J?3UVi%fB7Wf{LWqU9N{mD%u$U zdH*zs9GPYC?DSaN*gTEXMzx8+G%&#Kr$L|^n^cYqO7W8E2ebB`=v3xagWxcqePV>& z$C9K9Rgqs!Bvq{1~ zJ_{2+Dv&anQXh4drPcSV?k5{6odeb<74ECf_24u!3@m=tgXD9K7ShVWT^6ie=`^Gw zRMi`83;zW4X(r=~-k7Ys493h?46$TlF__)HCk)#K9hW1v9jSkV@@aTA;T0y~W!r5Z zTHlXPp4YOyFw~a&lGO$2{5^sht~lnCRaBpIV-vT)Np|_f;qVX+wfogHrr+cKgj(lQ z2LL+PF3(2r;+10Ak%rECbGywkOBSfIx^oP&|teU zM9EP>(ZZwGt2Q^_&GAFcrRUW_!Sb)4*FU<7;_d$M6;&I)*FPmR{i$nBj6YNsv1cEm z%8GpYA#nb$>Z|sQg0=Uf>5w)>&6ifE*EXYc`xOub4EDN>-L*`Vx1Sx(u^%N;v0KI* zWuOc=;mnVdh*&OxEp$O{-RUN3M29D#xAfAHS+2FDfUL9#G(Z@G(0l9T{4@@)zVCjR ztdU)^a5+cN9vN_A(}Az3cAlHUA`ME#gxy63%2eg zo>zkr0sk8oWv>Ub5l!>)QdNP>n%V%Be^bzPrH*- zGwEsOP|VUH!N8B4L3>okJ1uv^7ym?dR&zw{IeS1chR#Yo(RQ)^@MlhHJ>G_cUb{^|Or>ha!AHORu$noG4yUk*CfX)z;_G zG&+eRPftJbqb_~O-tg#k()EE1p_X4!5&q%5R!E^G zcG(9^%^^4{tg!Mcnl**St1t`NC_{a=%FcoKa}ox1pQ5R$c&>q zNC!@^4VqyI#*M#b95S5wIDtN+D?X6(qS=*4##HLSI%7O0U!%e*dMKU^mctwoIdqT1 zF3&ir%_jsB5=dtxIOWf^t7-)+nKCZk#X8NMO@X{`8Lh`VKKFIqq04oUQ6z;N*T__HwRISo=(qp-F+vwof*#A z-Lvzg^Lcdd@nvomhxe=WD0NV#GIHi4*@+s{gsRGxhOgbt*D4H+ff=(rsH)EROtmUu+cj!C%dOk#g zEY3C_j_j71ur6s{l^*?r)etllJ4tt>JsBL%T1a@7>FjH-NpY0M@9f1f{wAUG`0r)ppE3&E zXV{?TjwI<;%7=FMicc!jdhR_1*UkkEbJN+w0blHQL^+YE|8W02nB+2w{$xaTH54r$ z|Hq?@lJxTSw=+uzGfOz;+qgDWjX)BKrl!F8OVxbOjb(w}%;P`pClF)HYOwvaK?C|6 zB@P0kR)ke5M?op&WZ=N;`Lbn20uUIh?ScttXR+9dj7mrrdd;7bzGz6#m+1h^He~EC zlvFbD;gC3GYRwy;mz9mLdH^uTTw}J$=0bqIsTUb~%u5W)l*jxm54A&s$~0|u=$O#% zH>9LyP4|7~x|!H#`!Pf)rgs>deol)j5i^a`dY-vM3TC^Hb1YkGi+p)s%eq~BjP<4? zPH*B+qs6rB$6xhtW5UqOUFRrre@MJ2$ul?8GZ35z58rM-wspD$Oj~_&6qKOLhtnx#ViZHnE)Uy2Y_-D|zZwQv zb~-{cPcRb6 zlr>?P5I|;N89j?b2{0>NI9n4DM~f5RM9S=EXV@cRb-&|GFdiFB*A=-kdks zhvC3T6RpG{n4kV;qT`=7(wE+ZR~LRCwQz&Si@ARZasw2qpLUH-N!RnKfYXB`|GEl^ z`w1B%7RtwdJT}SXR)i7)^Y$0Ebx`rm7@|{Abd#@zMJ>~|i}1?VEeCeUnO$zsIGa)kOCZ-P}eQV#t)e{+oByG ze{Xg2j(2yVetx)5(seIRRoXs_hWrEKy@vj?7pwW+Z+E)_3C!-U*>BduVzBXoZo|gx zCHdc>?2@6k&7k6>$2)W6f=PU34nyLokGXLU%_qknklTukB>wr|LUS1z0Xbu-|A0WZ z1&%J}&vZ(Z^YXz@@u0##B(N(Fq6la)2U#AvH&!dn-{16H`-o?Qxc4W!L=(t_9{e1> zFb|}&J0#Xx%`447Gi(26tsj%$@IHY+(4`Z&reI5DMf;KVl#}uMSG!XP9l@24J8jUe zh?o3TUL(#&(^G9bE^B4O72yqgM%I(~-#7vdj{4)qy_v*(chYzuKW~p#VM(yva&{cb zKX)$)WAsF0aICD$Hst$nlOCR;!I8o7(MEx^-dC0IalkuWatOV1X=p(F+X0p+ao}#( z?)`1>Xgc~UK;?zmi3`O@(^-+9(~**q0!_|wQStGZdb@Stk|pM&gvxvv@~;TXZmbOm z*QsVfmEoE`hY55z3o^JrU25jpXQ8burHE^lqDj5vd>bGwP4yEhbMl-h-8B5OaT+M8?T(7{z?w+EZKm{ z`MFYGmRWvM7U%8#Jm};bPz}9m!XP zSK1F4{$U3bj}=q{lM#^6(RB~Hzz=>_o0QPRfe0Gk205XTi1D}v?f#V7S~VvgKD+gH zvczhh)~)p>Q`uI#bq_u%7+2)CGpSqK&fQr9_13L!=jXUpRM-_QqJ`BeBQYi%k>T!? zbLM<%R2RJM?JVb@F5zemssMB>VeKA=Wr~%prE23i7nJsrb)>PL(1&55}Vaz{51*P%iuPIcG_4_@~aWf;xP)MNtIwn2-(m_%Lw0dPs2t z)2bZdrZCo3e|RyQG2j)8Wiyj=`X5hc6;)*$wQEX3I;6Whm4*e<-QC^Y4T5yDXauCY zySqC-x?4cHVZZ+W-uuYGfn#t0toh7&-`B;~={l-xMvt~;C>-S33%umG+X z0z)c$w&yj%Xb!0R)G4=I=;i9`C_kG$1Ubcaa;m{>dDD1jFz{JHxp%cxiawQM)B{r| zolJik%M)^gdc}ss1#+(U-nXgc`%h{8|{fJjB)~2yNd2{RK zx|6=GN5G|%>Tdtpr&`0TJI>n|*lIdz{NP0*jciKQ3rMHGxmtI$6JgrB(v2&BkeCwa z5&2yxF}!_%!BUO_{S4L2?-WH3nO7j`8=Jw?pl8_Fy13w1rybMciFF4YYJTMy^~kYj zleQL5Y8orTs>6q?Th7Z2>POPTl33<@6Qpi9rl-W%!&p^6ZGGdFFY9-l3@|xAZsSV~ z<;*4N)qHXTlo-tm1rip49BeHTQi}pe3Q4%;okL(m6@+$xSGc(4@ShW&F=j9r8-?6~W@65BepO8)rwW-6((b8(QN#iF0Zd@f zrl|*)=hm9cZZx1nXlYkpPI6Mdn13MJ-&#z_Fy06G$|4kRH!*@r(Vw=%)u+Cr4^wA4 zmbZoM;I;W&8s+2Q-{-{)bkkCdc0%9zmxvwk`#Riyym#U4P&1aiZN6eVJXOA7i+6ys z7;xEOij$~LYQJz8s^pwkE-Y!m^&vMRkzOnUBfZiEkqh<)m+Z1)yYY& zD5d(2^cu72o+A)Nq4IW+Wf+xH(06kmNpCTRLr#ZK10Q(U+&stS%STY86dzY|Covyb zA>SXuJP&*MQrBr1`fFT8PF1~iTYU@v0lvDb-Wst0yQu=m=jM*<0MLA?UFv>3A9e48 zKHlT3Td8s0Z}I}h0J~#V)*puIF7ouvkpi~>ttXk2#r9N2?HbA0H}@UlKVWRqaHAg4 z`yE--@=(9d#XHr0kYDVrG~=vo!1g5ES7P!J9teef-|uD7mE_A?9)3TNpc#xO{<4-2 zsC_+5ulBz#ThP{&As&BE<4<@&XpAr6Z7;(h-qmV!?&>|kg{?rHS^Bt4{!oL3&Vfd? z@Y2lox#T&AJO6=aG#E3wEzt>@^JV4d{?@Mn1@nb`Vn=E~80;p|yV0-u`f?q>aAi^) z()4}5!5+0A;_AQ_*&UKJqhO~wa1hok^;|=J7%>1nnqdM4DL9?R`5x-(kKzo?(5XS{ z8$*)~RPAsbBq9th#qI=G2pbYntgB*@+AAOH6k(OvntUfvMA>1tQg?wtX{{B$GL8zX{w7L>Zmy7DISLxH`N*1P!CdJDjUkSTA zgv2ce^%a1ZzBnLJp!C?;cir4AF3Wi|nO&@I0G0~)A&OCNF{zFzzs_2%+4Q|%mU6bj ziQ@~LP}bwp9I>pJQ&LPJ{4UmND4&Vj)1N@@KWEG8G(b`0rs zRr7(r<`OJ-iBZX)be=uc*V}lU;jxX;2+e9~3s@%LmT0qRpRAq~%tn7C9eF}DAX2&C zQlrJh4?mA_8#UIsUMYX8FOVa=AAF>Ug-FA>CFSOhY>Q}rZ#z2(Bk>Pk>}N)}XuU#v z0&;8zy>KK8f?j&cCL3)26P_pY?Qu=P4=9hrTOT_u(<8B8OvFAQD-f@Q5L_asM1j#| zoU5qA2Edbwm-8g#7{ZY?UE>&d)atsesX4#YH=msbVP3O?M@Y4-O%o;-ImUhm$ zDnKA1PyZLD7a;9zyCRU|V1?bmh_W2V1*iVFepJ#ZznHd(_$~33kNiLm&KocECgL}W z$o2NhG3&bH8}jKXP5?~HS6In7Ie}-yoFb`o#dlbTV3 z7DFRh^MD&UuJQZIwLvaWJu_hpM{1AluCbEpZf*(qY;-0xtQ0<^7*lJ&4Bt0)jCdfy@w&d&pd6o8sl_ zm0+t9&EI*OB8s(Wy&D{UKMkERSnj+%STzOmb5}ZjJibAHs_M9wCJG~3bGOV<8u_zT z+?`>j-L=AD!Ep2AVGxkx6d-D9naV0y!sP}#cu2dEi3kOEmF2w z*eu$Nas=Ph=9R$;Cmh8IP^jx4fm1~1eS}}0XYzGW%`hsm-E5ZVg2lCBp$0$ye*SSvA2#tQCceQrSgY`^{RSS7n*vmu~etbHHnY{8E(+jT(KiOZm?hq=6|tzD2wdiyW~QIb8vd zi&ZW2>GVp@d2U8JwKlLkr8IbNI8H+^Lci0y)ZBirC}#0_cWr0uw6;hj6eWQ|BCcfV zYEDc>4H7c}F;ZMEQw*FT8C5Jrm;x-6^v`-Lz>DJmB+VN*kyv+qXJwbtR|`Y8Xf@+w0wR`7B>pqyA!0U{1f^`+7249A@gr zQC{Sr+#S(iJumHSIz~2cE<2BkBCQCpkm8FbGo?dQIZDcHxy=NbUvpn>|4?+dq&CV$ zakfQD!vjxWzjK}zi;P}k5pT(A!A__>Wh^*e!hJ+O)R8J86AY|W>>8Kusq5kDQ44j1 z-J(Zb+tK1CyLXWng;*uuwFfjdFk0~L9QpU#^sU^MdNCHvV~O24fJV4>**zNYPM{G2 zfDFjr7b~h&-%reN!K{2=zi+%A9hQwO`g*vO^4HgWU6~a<7%M1kY)Dzq*Vjo$xo|38 z^VBiv93CChsP9gMJtLrno^>=4vFi9;dsx}pQna9{Dq?rCGFH{zR<)q7rGxKa#rQ2{ zfje0#oE;H86p|sLdp!2nNrBOx>-E6i3E{9`okPIexWPeBFZ3yb?Qj)Qo!7Xz0L$)n zZ?N|!PmSFgC)GBPNL4;GerY<#Z#LS41qv<6Jg?zrlq^%EwwKv21|c zGWgF%x5lqH2#J6QZY`eIaU&kz{TgrmV68p)KL&B{WhkQl0{7*F{Jx5K@TXe!_Ax^w zPY2;m35x@(ZZ>LR!7fZajc3bc3WyyUmHM69>ebprJeVdA%xa7Wk?IdXP}vL+1J;?H zVNXu?YOQ(LF0&sqyit%$hoKTJ`RX*=C4aM7Iu2{rtPK}Qdfb-vz*WW!laGX#Y*kp z!<}qrOOl6%WXGvYA%>5-apw8^ZQId`&;9~fQqYOSN0R>a((#@RuaFmZNF95Si8-I( zyebrc2~=zUCN4c$Ob|i|;zpR;^2FBm^*M~-d+-|*`RD%i80YerP15}dc!yOaDvpwh%({51 z&FB5fGRFJ3fM4s#Qb>6ZiskA|RDD;F!^**)e~bqw-3i)Jn6#S|WunKD8Dv(j%?iIi zefExa8&#%C-l2UKR=Sf_2UO`|`-X-&{(h#nX|wdG{cMOxBdrike@XuWzuXJYtBrOv z$OK=(z#D3CHKb}7j>MOgN+v~B%LuTi7Ooc`6!ckqX>i!)+UQPj=z0?O5G$HatKwto z=wL;Q_zCRDDj%W`*vljl9HQP)eUxkFYvr$HgDg~0A+BK9T|40N{@y=5PA?*Jq(F1q z?fSxiPb1wK=&QlLVr5em^vhcTe*Yd^M+AQ_NY{>@HaIV(@2yv;`iJpUchC#@zywTw zwZwy}PbfqXU`i$P0RfI90yNYcx%T+wdG!v-yrjSun8G1aB3SqT&`xGGvQOZMvFO6w zx8L#oKDnZQeNRO>WnXmW&kTwh0z z-y3g&!teV-$bIf{c-T~c!M(#d=H7TKNi@}G@NsT@^J~d9?{Wil;dC>7SD}5el|@7s zRm1c&mXG+su#^!l0KTHL0TiI@#*grB=k4AH*2zEXzr^Sqx|pdyt=CVVTw-2ECsE5M z(i^BNT|wwPZsup-H4IdDtCPdPoUm?N{Nsn65TbnofVp^ZrXSOwmk#@ly;J<-jr`^d z<+}176qsoUa@?j>27e_bg`WKa8B8SzvvIK6!|E-bgiIP8`{uE`Wjpy9^IIid|8_XY zJk0HML8)GD;9K_xh7o;79_p#>LP>)Evf*de2cWh~6hW_6PJ1(iD;y7`LM|+O);X{n z24nMw_OWWbXG#q>(PVN1f{yzMP`}Su-DIfQs4j;*8BS?`bNF)8Jk#noK&hhVnaWVT zi+td>lBsN#DiU`Fk*@bu6v`$5Rgr_TDEc)ZbZQ=} z>Uad?&w6Hd?~Y*m5tAn){cR*3e=zSDn1SM3cNJZ57*fgsLyCk>(*19sB0AgNfFO(n z4Ff&c#+TEn@`Ye_J&ape)6rpK>I+QypTBj|f5wm)C=?ET*mtM6Aqyqy6NP9X2YJWB zJh83yB;Ox)KghA6ZmenN0})TFE&M9&OO@vSoixJKoeVJ#{f>ozpC*E{>WXSnbem80 zi9M9FD7Mlg(&Rode^!}SOR?cJ>DOI;t$z%?N!wcCoJ1RuULIkBQp-T9$vBZ6XtNKb z?~;6U>$Np)IYn~rDWZ&uWCL8@0*sK$J zHnZyVx)oeCC{Ma#7au3Jw18~jZ-}xWupz*N&W@<}o%jYIu{pyJoP?d8x2@_YIUJ=y z`Fm5HQ*O7!dI{ecmMxz(GxZvhWdf37j5NCh50Z&hFs2%dhZ|rxET&XH&aLo%;KIW9 z#Gr1L9P#w<*L?1JzAsNcb~=P^`aqN4&G!4y`Zjsy)l!TB6OJ=56o-Znj|KE9f>ba@ zHFCWmPhM>XI#8)UeGi65GDbj`s?eeu&>j!CcBfLzdH|$0MZa%@JKR#27dN0+gHI@) zNIm)cg&cFicj?@Ww4~PS9I(v-ZkZW!yO}@wQ`_FrOV`@_1L5QEjWHq%C4e;D{@fYM z;=2;(K*}%J34vODp+6Ei5n*^|o(=h%_0U2&mIUptS$`#;_WL_(4=z&LR741^!lNQ6 z@0(NRwo=pw&3(xnrD@EJ*%B(fIqmu__CWKV0-Y`jHZjxlv~l}(U=8o=W4e#d({7qI z--4*kdWKv3b`LkknmJMsHTX7&bCz=`mOq;65~@9B-|T4jE%x$Ez$I=Z@%6Ff*tH=k z6m<2DDRtkUJ3#OqM=n(}gjJYyK_{06@X^f7E=CBOe2qrSMzMk(W^~D7f3RH?sp$}% zcYI1bsNKtcJH8Aip^JMZ94EpGptd|mvNkbB7lR9I)o zKlBN3yl60qqPSpQPb0lBT2BNJ2qeQg#grjP{Zb>3yu%O3|@bii%Lm7njOZw_B*E z=ot+?{#>&WbJUABZ(hQ;4@|6O>DdvKq_CDd^*Wyw1N=8c?H2r8s@1dtJp{sabyyzy>5c)r%B8LZzfmAQL5DAWM>FQ zF0|j?{F5g4mDr$g33Y$jpa8i%u-d7knH z==DO5?QR!E#$>pt?!mY;{0zJ2PJerP%N79fIntLHRt1Q5nuR194PpV>C~w=2O5!dn z!tlF|l1o=4?#Ia2`$}lMYNz~Rso-a1RZ}}Y91VS^!pJ(w`5JwZN$3WLHT_{U>fi@v zAZ~Tz+2JNFG{P#Ht9^&K=V=Sb-(C9@Pk0+`GJ2Wk+a=?E3jk9zM!VmZT$?-sP{IDg zQ&RlD@gDc2HL54Zh>!Q9?rWfnT!)2#AL)@eV-l#7}z7v4{_; z6rrCewB@mQ!zg@gKtb4aiaECOpy#o^=W+kG6tc0q>T*552d9B(`oBRwdot?qXba#I z73cYCNv&KxhG+J%9x5zLmlkb+)euj~OWVwC;^z^F9|a?p7=j#n|E=h&B;iBlePiU$ zPh{8r>$nE~^9Sg9!NvB=`E^Pu<6B?5#~SY{;54{C$7za@>PRfbL}%7BVaypp%#K;8 z2|v+p?yS^2H64}}E1nEXpE|cKC2FtIjC}{?tu&dRvcP;2z#=E8u*vlorA7TIv-)$F z<{5E$-7CiXrzukQ#7)7ebo`V9)s^+A#$p?^$HFn*`rW#oHmsj|b&}6&$(rS=&}-qc zUYXl&Rohk3kagj;_)jj?=wM<9UNl;_U7lLQn;!}6x&$*GEw`aE3QFFh4&T41H-Amg zA!%2;@ISF9vf*bi9t|9pb0q;xE3-Pc705%tr~ED+2n*ke<8#t6&Fg_ffYL9F^L!Ce z(bNsk^jJKX5$^nGDSfbdZBO2Z7%xu3;aaB5zgR?RvO6pBalzM>RncUwSvafO&Yz}L znObH8QY%I+&t>IqYO#WSQ~!lMvX;=Q?d>}Jqh86d0i##+s`@av|CDE-T!d;J(O?&F~$Y=j=S|0 zm&GXDB2IutAUn~nUKn|?_g_iS?eU5TEHW`A=>0K215^GNui8?TK0~>B_1xK*8pEOi zWEY$e?D7S&Mun~u`*cPlyWg`b6C&aLIqTifVuO)Nsd{CGJ@h}vNb}TxoP884!1+~F z$LBr}^hr0Z$$Ck8bMm9g)RQ^5BjE;l^x6n}RFyty|DCsa-!f6;vpWOrU?^6Sg1BFv zrnjzDDQ|a~YPN<}*+ezD!PJ}bsa*+8jQiKbAwGNt=eX|A7n!yW}CdsRhy!G^b@5M&bnToA-OrCGo9MDy= z!J)wWsZews@}_p!uXalss!(#ysR~B=Cu;Xw4Zp@dj{xbZYz1YcD_x#+JU9x<@V>hK z>;LbGZSo{I84w}aq0)Dbnd-0rk=q;hRXbWIsE zm6)W#Pit$^ylQ2&Pb{ansz7#CwY4c9sGKp1C z5~gWU*#}NmV33D44*_W8^3Jy(UWlsTnQn)GMKjiV=~s;0enZj3vy%z>D|t&pJ7}`6 zj3oeHzE~En&a-LINg~E8FI{an^AjHb#q(u5%bfnaQBPjKD{d$Xe%a<{GD|eNp6Y%f z5YVJACx;v$J4S~vS4V&j;-IP4qUwGbLMAAmhSQbE`~>+|s#HPsqX)rOaIEL@)Ype(9t=7saWA)-L1a`lbo34ADuNUT#xzeJQv${X}Z@=~NgBu?A zOO#t9Ps4LjA?K59kwm0P9FQfnet-W&r`!-u%$A`UIHQopEll(ZL|`S%0qZ&@jjZaA zvriQeu&6|Ye$s@|00Zaw{-T^1-3XT27bHym;b^`D^B>Dz%9rX0Zo}*EQA13WIsD6PdVbgu{h>x zQIyITpjwKyI~1gZrg&YOB!qVx{LJAC7M@PY6`T?cl*Ej)qS|}6KGFDA$p&TlH}ew| zA03bNfueXY>y(J$&uS;Z1ehHX|C`@nA}#ew=)XsX=YiWU3jS$qn^K#2eg7Hd+NOPD zh!X6T#Df-HVMQsRh~}m^1xQkVc~FDdrTPh-QS)zwmr=j69Q{NaN;|XxD_mNU$Qmx^ z>$!PQzqKc@ZiYRqb{4y(607s7leB;OhO@Nr5d6xjv0!(eM?6(P8p}59n(Tv%wc9eh zuy}oB|BKIx7b#mIrN{~5_jd%0>Y9;Ggt+t(p78V|v14r{QhheksbssSj;FK5N}TfL zRdaaU-?xWB>3<{HkE2M8&m(_;x`UE0EBmW%mmvhAgMG-2ogK8YgnvtLwx4#GS+%eK z_dE{sXH$n}Pb{d#x^5U2lY38fRlq zW0iQUhd4b$j=V~lTlo~&MaSo0=U%q3wP!xPVq@d#|C|JX?wT?59|{l59WYd5OG71zMCE}ov; z)ZOCF^F!I#6M>GaE(?sWB)$23%S0v0N_uL=Q4-^S+kj`>x8xeCjA!~VwGTSy2sh#; zF#SRlZvT@6EC??}LNgH~qUaCmckE(aFp?zZCm@1_mPysze=}gU2ud+BLMuN3$)~D~ zYYa+dY8KcD8N1O~LPz>nWVBj{f&BkG{^u|s?{2iyJGM?>U*Z>j$_ztxkPvVexfpZ> zlp}uj^Mf&hcQJ#*ncXkBJ0obM6W(k8lJh|z47y~CDRxX^nwUpxXL-VLHz_;L9)^)= zP0~*IVmH!Nkbrw)tU@1406aq7DoxZj`S&{Gt3-3S7qe|bxz(hLgVz86`_S8%djjE~hXzM~aD`@6% z<)_@ZLM?l_gA()otcqcJ3ZrM)omDkPo`$TFTh0M8b}UuR-C6gArlr|p7vA^GUVKMu zr~B5^7vb~CE}!m2ei-52&XZcTFUF|h_j0zcN;V^G{CjNhRXCQv-JSnKBz5E6{ogWy zd1fZnO2u_dv;%E#$60{KiIIGZss)=#xn08+Te&D>&k}okscmo)$~YV#a-!Ws?bU@< z5w!a6eMs#qb-<%!Peol{@fX#KQ$1uAShFiRhASu2*>5hVvUoENf!Qz-bq%e&;# zuU-jE9?SQn_0kpee?z(5Z#B(~1A(vLv3B8B#WHzFqwKsU=s)hN14hRS5PJEU^aIZto7(L7e`+ue7W;jyT>bU`^GVp5i(9mS{(82NTnd*OB8 z{spafQ=kuCrLZ9#=W0KA{e|7+842%UgZpH)vAn@Ok+in5uS=#Nar4${2*K$O8pr@e!h z%kJg2hjS^dqR7!BxO$^WSiEnx;C0`PN#|+1hmt3yJMG0n^YaYl{Umbb22&-k?){f* z?f2#VL0uO5uK2T`D)E0)ox8F6Q*SydytbI0s(cmip#{Eo0K5_J0_*810ki)Cjo7_j zO_QyvG~2!#R4qX1hmtiUO5y_-gVA7%y)a1BhKF^(xYcU|m%Gt<`Zyv1Y(5zXuWxNA zBb#jH`=-Oy^nO%7KVv0+ymWu47{ibS1wD=%yyrc`iQn>E;CJ$Jwq7^AdaGWR?H(HL7;yFy$kBw#EeS1VJS z<-jx4RlZ>!e+mI6)t~*?d^#nJAN7}lDo>zY!u_c~3%LeDghm5b(em&bAaqtyrPzA- z8ZhZJ)v`W0^u`v&3h^ul;!IEvJv5I;a3k`5T_&@N+3*8j2TSnA)`=^wrA)sY@*RcI zGlnPApQHCke-+hAiW=q~sBrSE6Ay%0(ch*m+K^UrhjVcdN3)Uc&doaG!Bm|r?-@CuudHsY7@F7T2PB^ z$M$IbR)s%GQ(w6RUGufG;z@`?Dd_~f;j?i}u8%x-lOnaNY%COYt%U+PovRs+AZml? zn4kMa_yzj@Py&T|W0O-snO$45MmD5yChoJ>r=uK=s5rDKq<_oLdW@sa%Edu;{C70G zb-H}4Fa%O_|AS%hvI`2S{AHt`!Zanf*hf#WJ_7<634PIUF3gzFn?lo2HA7jdY`U^4 zMDz;@Vvz>93ok=NVz4y5OK=2v2+1!vS!}0TBxvD`|FkeJHeJd8Fd~5QPcn(+f8ve~ z0<3EsDnCJ!Jzij=~D7aT@n` zTZ6~HpQy$PetPy>8Y&3^G346+qP)4Wj^t;kf&aa7r6m=g<38+>0&~UsX#%|{xgn-C z&weP;l{d~qxALjF{h@8-3A4%{BxKkm-KDV`Ll29uhV@G=4>Pg^1Y@Tw50VsYG_V8ol$;fZD zO+nxw>NfyoSzUaX4cGvRBu3+3L2@5bIS9kH@y{y>LOgM=Xda?aae~h!_W3^JMRwsT zme7923^{eGM$<8fekw^4T$~{$vyeM^-M3-<$7YI0Hq~84)RCTzlyVi>C0WD_oPiVt zbgB1AB=w4w^H&7C@+uZXyA?>H7`G%x8X*+)wVWJ>y0d&e&cV+Qmn$ST9DMM9ta~0g zXcmH9Dy=}W^S&)XO@jG8eFA&o$009;Q7L7|2xo|6L#AI0n}0U>aXq)E@z7zDf4p4= ze9J!S>U_?2{>mNwip#DSZ>v?|V19)7i$&=%?I-=tIfj%`1EP~ZEZd=e#kt4+(wXrcE**J7 zWW}twB?DJqYC`qC@HO7b7E z%m}W|8p=T<=lfoxhe^>`8HI+M4EN!ZGGtl|h0QZD0r;0(|5wKN9G@wh8uuAEUl}y{ zjf|}r3MWEDg>mx=te{1N;hD}}ym2QzP@ zj!-L70k0%cgbeu`1z6bNZW64t!Ain8g?Q$B#jWj&MPWsIz7&auUPj5*5PkjKu+>5off)b)>|Ksrubrj$*>`(pD@go=z#X=b&XmvUsMeU7& zbgCjAKbnkF!*deeMG>0!zJ@9W6WKv+Pou^!6QFoAu|zvTc*4TpEJ=Yn$q~-Wi}ia9 z;tr0oJ6MIWhaaIgc_AF|-rdvb=-%-9A5!KH!kGUxTP=aaJsa&mOi9cC!;}!YGrVJQmhV>}pd?)% z{*13(HErL>Q*M~Z!sd00%%((&}$RnpE`=gv%ir~gS@X7q{l4QGR~bYzS##*X4%qC_UK<$7*yl|EU@NE5nL zO|l$s(_i3x!%LbGP8$fU>22@_2tB<;CBBPc$Yqo(WJKm@s6rW^L(>g2a%6n9&U>=r z7{Ek?%SI1#ye2Qo`Qy)M(4l*<=l>&Su%~`nu-r{KE+bO>B?P6vLaW}@EE-M};G5ot z`N%tE@%R;0Wf#k4U<}XaH>1Qi8TmnI;`S_k0E(`vJP1)dO-@Cc>2P_~h3zxYUM!tS zMb?(67%PN6o)y6^lB>L9^Mn^pnvJr3`ka}nZD1_CH%&NA$3LE_dMMd9LWdZSA;5iD z_9Xq_&~|1d#R`A*^p)G7Cg8*0O!FD}m^-AMGl3=V*uOy?ak(sl%leQ z(7_+*$erQ{@qV{oh>QXqafOk-!;2i8B&(eyIJ0zLIWP7F0F|QC9dzW^6d&y}9QplC zG<@(0cM@bdB=`}W>pgbM@pTzz8v^3vR!US@6=~#VQu@ac5Nd;7p{V5b=uUeCO6cE4e*}haG_04aCdUlG-N5m4N~;EB@Cn4W%;J%+OW2 z$$IHYouH0k|1nLVuwzjW&1D4U#(U}J-#SI`KMh6 zdoT8>T3mFr&dwA-!s-1c2};DUXby9{AXB8QT}Jn4jy&8$xnj+O{cVvvwZw51A_Bui zMDpg5(S;dq*2RQLw~S6wwUBT2jZlyhy# z5CV37q98UGA9p;du2(;XVPG6!r$^RnM)X^qjaJQ{MGQ--)IXK8>tKqMVZRBMbmaC|PL&atM*OyC~)E;YFJahdW z%tOKMdi!Oj#bKkaF(tUw2*A?ZZdizoTc;60O+fL8B8Zy+1{-Z`l+@S{cipa&`xtN3 zfh_0y=oRZvz`1}fSdD{5BCq&UeD=m?+1KaW)BFR-hKOgaHwxz2*Q&}tXr_}XYTcEa zb!4hUtL`&;uybU@Gj(|c!+@4vjR7Ul(UrroGdfjfRgSnqwB)9eYjZA%+lpYStOCWP zZi0X+B<>dyziJ{$l9~<33}ds?#v`Vl-Em#?!-$@zB!ca7sW7uh_#~bo1#>_Nq>c!2 z;HmY!pn;1q+aKg0GA0>4+o7Dy!pVhJS>3I z^R@TTGDVnMt82LZt2#!=nvp()-@8|_2{toKC>eY_pOesHMx#xE!KMGH-k(vL9Mx$( z+v3F%B@yrYQ#zF**#zJwOjjoWbKNiCxt$w{@*nP{eD8F%WRb_Bf~`IJ>eJE0A}wVhV`h!rqc_6Qwzr%!|vJZIrpu z$C5JfB0O3M6~&=I8>2KG#~9_>uXP=b*o9{%PYRePFF+cqm%wbsAn4;k?s8WVZ2LHv zCr5t?^w8;AJ|-241`bB$=H()5+kx-%MT<)H(&GjdmTaBQ85Y1DwWR=-`|-1idK&bq z7@>r}%MVJ&ai-=H5rHvJ08xz}iohtUH|^;7mZuLKkBZVHP)MX^Dlk5fCaTe$ARMyS zeJ1HwsOn4x*l3X1%L_K{Q6NU`)mvRnPTi6HnriIMIMdl&#Bb?B8c>{YL!V-exc3D& ztzdMBU6A@wV~ya$0VrPk3L6rLFO-awnMw%{mg|3^ctoofj!nlIhB;Pp`zOdgW4E@dSp}AP;gtoVv-)a#vFmv_4+VW(mZyi5LY>uZEgm*@41<#x{VrB8WAu)Nbsq*+AR zxYB9dc0O2!Rf>HLf}XR7O5<*Ka~HrWQu*2Q3>@09i_aS}ARI6GgUNV*ih7!wk!PkU z+eGL1V!ElPl_r+SxjdC&ZJk;IRM-KL5Mpm9v1V(gssBaFlkt;K)A6W;c0f+VUOEW) z(cF8Rxd;};I2~nww)JC=LTWl;a>#l-zUO}^Y8kC>i~{L8zU)<0aFZiwV4uiQ_6qEc zbPlyQS@M%3EC*c3;Xr*Az^+;+#(*LU?Fc8Mq^k=5t{>Ls7nd()#C^am4 zY0aGZ*#-&D{Db4(SsWh$f5%775|7PuMbBdhTEZxxoPyq)qM>H#cg&VAm!*Tp3q?fy zfg3tK6hJ-M%%uT^AY8)TnukFOhPr}%^dEjW2(BWb4HB+Jrs^f8@g9-z6O2Ts1%1bw zN#SCMkg4ugt%RJJkT^kS@;&WmFply1wjZfet@u+!SHuOo_XJFAbrdEb2#$KktR?OF z-j{|xmAGA?h;`*CAjdUv75aCp#wAt8!RAZ5Db{QJ(|x-%9PoXH%8N8Oj8 z=r6`{ye$1feEJM;86C>IUrGgw+hW7emBBc*7@qfU7Rf?Ox|%9CXbM^_TGZL@<;erJ zN$MC2QaW|&(`9Z+C^De|de7Ckzd>RrS8-F;34!BQL)eAG+ELb}>?QO97;$rZ95l(q zXUyg0ez|fB)BHxid%2CNgjZ-QoW(!wInM`kjRre_N-o+6A1R4{-s;#$$+ z2y^rZU%UA~qTE&o@(A>;*!YTc^1~S0pGm3bPEFb9$Thp4Hkoei3G1bW1c-QPNg5Nk zu|ybIx$*HWmkQEPF-f%p12tcSvTGY4{)>RP=-8A0B14J-i-t^EEwL1v3?qn3yR~X4 z>0?FG!z|S;__qJ6Bf-v9l)OI-Rl_OdSCy4{GK62o)gDsgf)H7DaNfVefl1r-(KrcT z0YXn7r!LfYRgbfAD-}tQdAwP7R$4I$8=EIayQ*`Q|88fjRyUrR+2?;bI)2h{;ey9%pKFKc5mgFmJdYw9MXq2?uYbwnx<8|ez1vZ#WFK;?#gWRFCs z9$>;omyw&FIU3(3d8?CQ)G5%caB-+|UvzA89Fr?THqce;#SJSAX-}&v%B>t)K44?Q zG>`1|*~(p`KfBGMTsvuEMx(%_w~&VJ9ELe8ovAhJ6;h`NtdlG;vm%1s&IOi-cJXx5 zeiUDW3QsICUB*3?I4#0@dS;kyIG79Q;KorVsEo`39>vr`OtT5Er-U>zeW6+R+u+(W zv67&9b~M$U3fB_TM8Fg_PtpYI_7((YzaOX_^~NNwTrjwhYj@Bd>M-FS2_V}+aW@~M zY$nW$l1A6~86MrAV=vQr^4l`)PWiK%8WSQcloy|9T7IokP5&g@vnJqF`w>TAJVC{? zi8n8ForD^?1Ek949VdvY+wXbpIFR}e?=sbjrj?e|Q&K$saJBB;^DX$XlVgLa+29Ig zn`YVO3%ZX9(O@xefVjaeF?A&8mg~HnHjO`V%N#7g{6LzT6MQXsR;#HKx@E^5FV6AT9uD?tdTI1K=d~o5i4oxeQ#47% zF%y``Yme>mx?QUZ3`4;pg!bzUKMk0g0eIl8YT#7;nSjd{TsmJQp7UMu40u%vHIUX7 z0ScmN=KkR~kh*ctDcG^8l!<5n-WWY3t4nglcG!jpWz-@CW?dwn+XgDBJ+@nHOpM`U z&!1$xQXzX2HMohdZNot{n_Xy^%w0QFMxrz1WKrPdMV3(K*d5h+O-d$<;t(5$GLdw1 zjY}k(tWOtDVNN74!bF#;lMQl-rhNGtZt#D{Nbn@bL&LDU9xmL!DH8v32)}_7ca<=> zmb2ZLUnRD%xvp~;_Yln8H#**V7dZ=`=e`Yo8_IclscJ zFr=aIsx$J&u4mN39=^B3sUz|vU}!1)Wb0XAlvqirShNQtK7iz@wD0#XKYDvPZ{QEQ zZ_maUMS;$Sk?{x4CWfVIWvX;^YI)#wwQaAWRY)|U`k@_1I7*eaElRwqe2dY5{aW8$=;j&A{AW4m9Vel{Yg1sfmbt8xR zuhpzr8z&cJCoNF|f3I*VuiD~)RRZ^YNM z3TH7_D5b*pJDVjNFYvfLN>|{BPLr$_QGKeuuTy05UPrY?{^b0>6p@uOY3cbClx_|{AygYev9SrwjgHx zj+U```T#90JCnFKcg75IFCzeO`n)IeA?xi0>`@Cuj!@Wr?RmMwO9H;O`B`lW( zHhcEUMDQI97hBCASoP8aNIfzv9at8NkX>e>PgcoJP0yc zsWB3nNaid`U?o-$YwCsM{e1hg$wb}i2}nDI?z);MKQaROrt{(6{L@ z`TquT9=_0qUJLQGnO9U8|1W}&-jYT|dC;d?wI z6^ZVd#E5^3xhpa7W}ffRnVHEv4_hrv0hQ15USH_rvOz%dmvQxxrAeK|WIB{@v9He7 zA47mqVzEqp-b5&lkcWJu>x8=AKjTYG3<*%3p!#%;t;nMTe*_0yoSHK4JdYcZ<~l&pE#fO(#%k-VlI$LUD(M_~ceBMt;qI-Q;_8}rVI)Cel6hQ`?yPDWJ(=k4W9|0{5?yMyd^_4)I zy)uciG}+(#(L$c};r$mkQjR;xW<(_F{hz7VpS>%f3Jf^r;M7R(H?njT_;!r$LIRgp zwR6!CbT~=Do5E0aQcA1qjoqyBz5QX?B6vE|nlA0CDe~9WAJaB*MwKEE8kXkgiZ|rs zKwtT@H{`)nKXZSkf=Sl|d{(RSc3OUT= z(zr=y+CccY&4uImsPv~-%%EHbPKvp(`$-%`mpf0uS9Eu&xU$w}Qug)=&Ss$y9q&Oo zI|#WsFHjdl@HouB^|?3%0j%!PMUvR2A8utCH8Pl&>Kzh+wB&$})3A-5B9lAmwjilF z5r6F-;6f!7Q(cc8Q^BqNF#0Mx@SyBCwd!Nz`GVLMz+n5X$*ri8_qUyS?Izs!NU~0W zJ8wfC3;mmm6@$68@~`AKM_DBwBA>s`q0!zuy(;CGptwl#7cErWyxm(-ue6r@(Hjd3 z@%iA6i@HPEkt7ZIlOQdG%0}?%_$G5p%9dfx3-3rmwX2Twsa%m#K1&{wk*7L%7r~Nwgvu0U>}fbdW;DN}>O0N+fO2KY2j$E?-FQd& zSJnVC!g^GsX+fHu4LZHE3~j&!ZMZzjI$B2;o%A951sp1s(!c(kbt$f%{7=c%b|jY+ zexdO45G}p|P25a_P&?ERBDGAZXqv0~nC0`aR#=WwF0k>o@+7=hE0(l;OzsYU&#^bO zig>+;VVlMU3&zjEUPtEuR2|;1O2IO;UN2m+n6^P`9cK)RG_xyUK%IOfSIYDs2;NJ{ zQo7!#m#HeI+M?F4;{H?Wt=)JRupviqx(Nx2KZfVvB7P-52_=)o>SUWOP5bT~2r!8A zPXf$Mnb&_Y~pDPt!7Z z=^TH!K0|#^z8`kCJms}-!%+(bfrZ>c8O#k}EeuV+7Rzas!)R&ui$2fh2a>#REYB0R z0U|lo&XlLg5t#bQ6PWBobn9tS1}&iZ&7a1)FrUIcQ@_jJ&o8_5q|)KamW*VyNiUTx z)SkMXe5@k%7&9YbMd~Sg{rp8bx3cMbaNh7YEBTgu7-)TEv{H=WZQ?|Jr zKi`bkZ*w~+Io1R%oO_K8Rl1;~UdV^gL>BYR-YPkoQeI7nr?iI&wU`%8pkyW@UAsD? zUxKgH#88=XeLtfXWjq7}_nX7Gh5lrti$xRA0IvNBEel{%^9cJ;@j-uo_IK$zE*;>Q z+jPtH-Z2J9@sME9!8%Ji#m&dkMHCO-graXen`ea*Qe*UCPMlt(X|E?xG#*_oeY^=) zbF!Lpt?H9b4FX#U1a<<+ClB9*_R>=aN!`>lh2< zA?qiKODM&4I(LUGPsZY3nGq-H|F(P#3i^jfgHG13)wb1gQ9>_J-Cuy~BI9IDaqTOC zXyQos+ESZ+2V=X_0D4oz}5A_3hG zh>H&`b>j7Uq?j?JZXveHyCrsehPRw*zv0eC6i&v!F!y>;6gNxu_nYZn>%k<3_2*Kb z8G?U((4D{s<#`A8^qdTHwSD+94zLgfa6bh&yZkQbW5x4!q<-ir>Vw^Ov}-z2`5{DZ+=8f5G z@B|+e8}B-<`bF{*^-gFcagpa=Xco@_(u64c!*QUHt7upA&4E~xuQQ2~{ys;Us1`Dk zK?nO=rP`QzMtiQG*Z2Q_DYV~f-Un{jKWTx-_;qevH;j^~jaq=awKk<9u^FQeI3P1! zGFfzRApM8r%3savKO|S`o=oxPCq(RA39$Ch2>Lf1%p;%RGD=XHwc_p{yom<03ubR~ zTe04W(;hve$PL{F@hC}^eZRhy_mf33jPy7A`c1{ybFx?g=61_Pp04=l70d zH->y@8X%m)?`i0cBjL6QU-L7wVTc#^7SwqzwCo)6;_**ohEl-&WdGSdTV;uWTutBT zCUEfYArONr0#5r1sSHW7&#w0rjawACH4x7RtP< zt4yZ@$EW^XJ~G3%(3jhNA?S^5l;_zg>cTOF-1>gVnK=f4xhkVr`&`F>$Ll%{(B_dpF=Z<>!;zLH!dWWN8`!#sWkgfpIcj+eNlNN60u)sl!y6ks6*>O>LJZ| z7vFv}Xw;kdZV5;bz4&1SQJLaATi?Xi(t)U3jV5THyO4y=!eQv!8NO<)cR=$rq9k^` zkKBd8svd*+VOp@v;yd}1dH=2vlB%Hyb9yw*(u)jj-k1v(06T@VOn}ZPQg`rIXKTRK za#%T))A-ZC1n510w)Q7ih@oJ~SBOr~udyC1(=TpF!dknR1s>YfXsOY~#q!d&AOA2t zjgQJV(k)OWf(8i63yah3`)`eua0N34%zoC_#(x-`&@AYV$FI6ZdRRlC5 zAjsJz*XvdD%a1mqxPXj`Ra7*EKmw7R;_f*BLHTq)f6A(xD`HV}tK4x&86=Hf)V(TD z`X=0bqdEFBCl-&K;>UZh4GRDepb#N(dv(N?$f*9l>gjPhY6QHi3CD7+>#NU&E5JQx zd5}_G!eoYvj^uUg+?kf54L4t*X} zF|4D6rX_;P@m0g~UcGr)h4MY}YyKmKS(~?L&CqAiGU7UHi*b3@UI?oW2;-py7xm|D2QrXspPpIRS`CKVTE(-2xQuNz9u-W4UDRbrUOR zo^_B0>NCFv371Uw<;8aoo|7vdd)Tl*6r1wF1f)aUI0%Sq?51s;;=yM6`OC)eIOyGC zs{t486Hz=u_FA|LYBWP=s(BD|9*Q^8f6gu|hjxuDF-B2LOb@)okFx9yk>fRNVM#t0!<&BjtKSu>Wv7}9k7??e@h_r?R)%YSn>9w3D9@+zEa&L5zAW{Hw7rJ2?d$YBPP*(6Q8I#Ib%OEHy`&qP-lejpU) zF0-b?gec&_Rk1j#7UVCRduuF%o}%+_UT@pFHfz!{C#+j9G;)0O`Nm=|-*U}4%InKC zE4W^~aXE~<>bBJeh`Y7`{Izi+wgLY?hrAdO#MTmKg8kpp_^@ti;arccXYI|^j*zCR z=f`LH!>T{)zh3wSQJL<FN77n8`T6|kuRf`rXVTwkhHug~OA=MP=ihZ463L>k z^|?=zVMK^;=K$(it-22Ol&9OBj! zx2obs%VLlbx14lj7a2g<)GJ;Z#s9!!|Cqx*<`i+Tf z@R(c`L6kCL3@TuB?j2FxV9&+FQ$Ssd$xAct{yz3sYM!>3DPqFLLd46b|N9;M&6xQ4?`z}G85RxyE zdW(O*)CvKevJ8}Cqx#R68tfBj%9m7rQi*IHPvt5#SK28x6Lm8f@5G#qbG0Xse)FT- z!XZh(rhm2Mxbv(n_qvt849b1`Le?OhZ^k@`jso?H{sHfVm@xEO9&2brk&43H%4P{! z+#}Y9vVtx1D)Rj6f7zP=Kf#&R@Ya|PoofEckCmVRG{b4^4DB{Td!0xPl%R#xzbsmo z>(_}^5n+T$!0~fLaip-b6g6L|_-**}9{{*MEAL07HtH-zkR*bZ`RPie{kYqO+hm?R zF@I?fgC|TJyoE%-Vv{$7N;k>&D75K|GIvqPHc)=)Z&5RivhuhUXuKggP1nxr4x0Z@ z&YBm3`qm1dj^y#v^Lfj8`CGrxf{Hu}2Ug~~1d1O%hI}{8pR_9G{V}{&M~Cyiny=^+Z~gMP6ZO97)G8H*UK48`05&Bu{5^rW>ms zp-vZ;2;(A;_^}#MOdw&qBAb%yf6dIvVRo=ko>J#eJM%#Yp;sslI3uzCrM0Tn&rSh&-I78l8Cy(*r^$0$S~Fyf$EVV zD5NY-IZD>7Z;p50HfpmU_jDB&qSAwn>5PYGftvp%0KLb3Ow;Y7rEMVfuPL+o!$nMu zq0sC1jFLnX(1~*fNu*IXU45g;Hci80Z91ISKY#phbV44r`H}qD#UYIEYUH}g5RD$4 zUPj#d-Nbg-CFN%*vm6@C6=mPIf0FmMua$)(K zdps{4O!PlxDe29;VXlOc8Qx*Lm!^qRAyV;kd=I+#&7HKO?!R)3A7W*nzJ@M0B{e6@E4Pjr`uCA+tHsWhjWCb=IiDK$^#zc$YINvQb9pqxryL=H%m}rUexIk)E|%tNNtL2e=0CK6mBWbjdYI3A>oYE z;V@JN%D~z`i7eyd^A-1h(oF|sFy5?;a;azXC;y}mQ-NZ52#jvhG7SSD2FoNnLo8d& zG52tgfO%s#At_e@HHOi2rE!hUY^6b^!MT{=(OaumlJTJkizE4|c|k8f8_w?=p-tL+ zVB}g@F&m%c1utn>SrO8FhXUDx77#kYH!I_^^E{+a{3lomcZy)C$sFc=HIDA_`mgD_ z{Rj{Wb%F_`&i;sBpM)d``07X$?_E!BL|T*8A4-85U>;FMZg2tDBmcRfx?`U~hDWA; zVqPKf0NHwB4D+)zp7&+%<(tl$c3vBw#=eHe#+K;4y{F%!3`Oj0YXSc z3qmwmB!Ot>>GZSlJa1gb@?1`;ByS_HGekU|S>vh3jj9cKXd}MM+|iuJQ$r za0?1N$me@qthr5KZ7b_6h;m1N*u(z^)eFR*IO(dXDJ{n}jjwea0~>I^V-0mDADpjZ zFJ?W1cj@2O?>+yqMyrK#!GpU?2r)iAn&%q|N(!EO<cxqlY#>!V}LuS$C z=N2mv)|($9tyf-H=U&#-FWS34=>jSPsHXRyW%$0){7&s}&Q4xhyaG1ZNQ;WS4Jbe~ z5-8U8NW^rIaUJKf-DG85Uxpqo)G6xPw^QB9U%{bVINKb~rq=`gSp;4a_~@_D*`IS~ zev%)Xy_$b#F+-IQD@uYsa3bQYS$9C4C_|+Ys`rz=FGxvNPY-X&Hac6(eYRSdD)K(< z`1CtA4Vx)DDx@cz*IAn1VUhp0NqKWhup5KU*rxT)mwL3a?cXbQI^cB#vCpelHzL#h zhJrmYwHdHWY@x~J%&{l_WP-0>J^K+YFU8EtmQoRScih62Qf#pG*??&~_}prI+I?43 zkDAU5xzYx1NwK>zY_KT@qfnVm>c$BV$0qm0NZurdlSidjH1l0iXqibHnqqpo|_fW;fNHs*4aldOYLXuFzKFl=;(77)+0bL_W7{WILxsheU!2qZ(TN-VudHMS@u+5o z;!6k6aM}O_AM#LZZ?4jmUof;5(EdSxg6q|)xfvjfJrCuAmM=bL-2_c-*jbuteF+l` z>>KAT$y~4YA0Co_Y~6i<7&QsS=AN4%{LGI1wv7G}kOoLmOXgKB@2&f&65IGZEG@3V zrXg>L`$r35!G+QtXY=)|roGNge*CXuUGE0`#OF3tPDTazpS&QJURQ4-gkq1q|FLqZ zGdJC6KT~bnk~8@D{rR-rF+?F!peU316R%*wIWKOS1W=@=DN}P77bfnyBxg{G5VM@B z~a~HZadW|Fj72wr7pjN@4b_!1VbO(%g+>ceTYZh zE?wB~UH01SHMi5Hf%1s))u;QJkh$E4yji9%fL zXK}T7VNXf*lF;TzP1ON ziS|8+%v5vBT`B-p5>7ynK&@7PtLQTGZ;QoFOt$6QN13Vf%$#Cw+z9G_y3j6;p@tsM z=6^mV6l2e5P#PUOT>5*Q`_ktrXAAqORkDR*Y_ghD zu6z2Clnb$Nx&9V?F+ePB{gg(a6_5my4!GL0rg#i|L8SCGOd$)hd#hx|3N+_A^}DgZWoPq~7H0=s?FuJz zjoxNXACqb34pRY2chR?ZeLqD)FwFTqcn-fO%M4qqzgZsqP0> zrZhDZ#61fAx~ED7t)XE5~JU0cgct4ua(AvTM6r9xQ-7>&HoH6`CzEj2jK z`Kqek_`sc^XC6!&c{Uze#WO7~bvSOiIwl5~w$NlP!=?mGsfVh@EvD%SE4%Ps-(if6 zO#%0DSXGs$t#_@7s$xJ?EOEb{v!-_~`xpy~29>K@{|A~|dRHg&cN4y;A2tYDe0M|c ziuPNXat>H-B3*CXmr}gXP9%zO6$1sJ@Obwsar}=6&Xv=UW2t`j6Aa zF8s%BM?)f7*Cl(0qSxbKc2DIF;EWssB+o5RRy+4?#(V`XB;A~Lqj8=QJUcfDIS$F$ zz=IkQifad zT!dm4gfQ6~z!$n@3o!?>SIPyNj0U!yHGB4_&YjtjpI+Q#?Wq44pLH5WpEkn{`5fO8 z%Uz_l3WNG?A-da#AE0-au&z{5tLurMa?N;Npw$P7yR99l)Nl%ip}R-t0!x`q$W#$S zPvm5=9v(Z2Qr&Oc?7_w}OLt(KagaWs5#>X$ zSu2N=%jXmm&=a!g%K7d=_@_0i8N&1*!pwqArs}@(#XYaLw{Brl@I>St{_p&fj%z~n zs`tGr@zf#6c=+oTj5cy0QpN%)#q6P1Y12S|T5OH+jr4)yN{wr@|42Tf=k%Qv7Z}C%lsE^HHI4};Ki`x|u4lnM185gP+>Ga!ir_72mMKFO=+ia;Z)VzyZFS zr@Yd+X+=g-xxiGK(j0qQZFw8NJ%qWGbFb)bIov(jFrG7mPdBB~ zd1I5T1OUxGEz-0T_TN7IFw9LQ?)(=2P%Q3H441A2Hyl9|Q^SYR{U-p5k%c%iOztL&|7s*3U4Z2q_5)A zAYq}ssi`|Xn+)o2O?)MdO@@h+QoV1LOouuY5m@tOQetTBw9Tpvd1BwO{@kRBq{W~mz#ab{cQ}DTifd;E=9}3FVoz(rCwCY`M!vuN&mYSfzWo-$vb-l zD2(i51Sm)1w!Ov{JFyhH)BN~wHeS${%QzZ`Kc&yu+^&D@BtwDBgg;mfxJz`DF99Ov z;T9BMZgB|c+G#b#yH8p+q)T)w*uw#t*M><+???YuE1z@qg+HFEdioLn zzE5OVLT}VTg9o%kd#Y9j{RX+JY%1J-vbH_sb^WI2`?(OQrld%KQSKIE<#J&P?aCp= zls80BpWQOz=}?qIiqR4HTnR@jD{#!+6C{V;6hX3(4wf zDd(y^iYN%3<0^Da{v1zKSfj(i?BJJlzDtw4J=>Z>)bEYGU3o`ePS9(`oGKy+F#EO< za^WAC&M`sKGA$8oH4#!8c4@)f5rZOXm+5bYpusi>=f%CTBXKsdN_2h z?Doee_^wRdxB_xm+;DE_wlTbOq+MH-`jBz7|JtFiKABjOV$7Q(yZ!Bz{er6%E;@R^ zk-*&Uuo8%iZvY?0rT5k(aO-6>Lgxjr1z}|C=^IS6idk1EbH)In@lussU+lNWUyl$! z@XZj%SkN83MKf}fdRJQ5Mz5j5WW_T|m?0ykVuVD==Opayez!1PZCeD zJ!HGc?b^6vNX|Djzt+8Fo=SUr8Xg6(X=L`j97qM4;B0=r3COodN*T#yB9I^x2`cg1 zoAU8LJMFi;-5jhQ?Csv1(AXE=CJR=mfPY6HnRaM^QRFLrJ=>Xs7%w)=9RLI|C37;Z zzIMJr4J#2@kIr-1UPHmzVwbmni8TjkV`l3-9Sqj{28ZqMo1NO#s3r6(()=s$JvPe| zj+d5RL*ItyQ^&YnhjiO=`du9*I6hD(IR5PJMJPBcKl?e;FfgM|xhbjOKE+ zw?ramB)ajQW6~_Pih0uhcW1^O1sX<(M)25l_rocT8vL39ykf;PI{L6XY92bH_MHQOJoRX4V-u=o;`KG{ z<4?4FS+NC&m}yT%rXQ$wsT|W6p`F|xO5TFr?@Z>OWe#gf>98C9x;%o`t5+@w*}u(@ z6nGLd(W-Zu7dA>Tsz zt{%9k>CU)auuPALToYk$G1^*}*`_6E8Hc#*Tp1Lwqe&XJhH^Y+uQ?ZWiy#I9cf^bi zozs0yww$B8J@AEcn2g|Yf8(_n+eO064+G+J>|(zDV&7L`>d{Qg6KYWtF}XX6F&UN6C|;k!Ah5%m3AxA_89*>-USi=_F6kVl3f%(3@iB z&=*0wap7?)AW?Nt7Dit8%YVlf8b@5whkn4CWO)X9N+-GrDtI5Ep@mf(yzBh+-?6r& zc}OhKOIi1CvP!5C_~3u1ebHr0Cu;Pcv5Q}Ar2d~UVWuO~sf~r!Hzp3rC-;B-xMSGB ziBvS|H%SJ3$JR@o!8ni{z}SZuPyP(V0IZ>M*#1oEL5ri_@Ari^YFL1j={v0A@Lgwy zXBr59M#q&l|H)#)1a$X=Dc(1CE*h(Te7HclN_68E^=#tm9B%|@QAD2vX+EF#1;|)j z{Y}~fT8#jlmk$9MU%Ub6_YpmTzdE+F$JaP4V4L%T+2z9Vqb0LLeJ=O2FU5S@NCTB^ zADbNc+>(lb&38is8ihSO?nC2v38~LscLD|W=ixFZas%9165NWQESkLTWSho|DOL0p z<8MAY*7B4wF*Fhscp}&^#601A zoBtil8q5n1Yv=&UaqMAR4Jhp8^Thj^xX`Q^k)2(4JXW9{E!IU5TLIQB z-({ZmGt`e-qj5SdhS^AsVFUFvao>iH`%+3E>1|beGJdK~$d&5#v6Zm4yCXJ;HYgZL z2I;5?3=h5qgYgd0{Wv?5X=R_uxDO=azCgujEtX;P*4Z|}2w4dg!h_c#(s?0p@Wfre zTa)Wno~BDM0S(y`WuQ6ypBYxLz71n2D2srR-7Dp0zljB4n$^mA_ZVq+fL=b?*6P;01Ujz2MKtHqlP$Ra-B!+nVT zfok6O=7~BPZRd5y=jBg?uXSFpX?EA-n$wL8+n|J=e#vEX-U^SC1$Mj!%~w+fewR4U z5$HrA6^sa^GFO9$SxC!X{%NJ6wa8AoLKVLw5BuSYb^ho%_Tqoc>=2r}bA0=~{A_X< zn{}45&wqmR{X|1n?L)S{&W*__rb88UIkc_v&^4~_5ud_)iHtU`@6J&Db0#rPMcMzD zYR~c1Av2**jU|MUz0H9SeTX54bhJgAgtptPl!419c!v%EESc+#y=**_Qvw0*r8=lJ zwq7~K)xuI|ZRC|zdh#ax;CsjT z3vW2`BNrEiGwqw&4f-~jU1)j@l<&lS)pBYf{zUd|0GX3b+!iWvH}?~BzZMvF@NjDV zzh4fZ7>RUZK^k`;g(v>~ zQspTs_~`42(*_y@_rVxHkRk{9%qFy#iB?i~bsrb>^&hW;_UPHtI^Ab>R&}b|S))7a z1jcPdIEDCtlpy_zVpX(yPOyJ0TzCXAjPyheV*ihkt1U{N6L&E3_&T^7AtCVj$<`2P zErt)&oLp~XJm}k=TKP#EkkOzAnk;dgqK(76gjpOUiJdPC9e~)OAN=v?=|$xiEObyC zq2+NaEu*0b(HLt#S;V!8-g+EERi>GTPq6h6H%5mi8S_%3Z6Hl4ZNXoN=#UQn(|^-@2Uo2s#S2!?6TRwI=R45>6XNn2eu5Q}zHR&jV9`1Ama;F{ z%HHw%*v|6Ls{e58`nB|F^y=L&aL2%Ub5#M9=et*Jl zGdLPN+-pXI3{HomvQ9U_s6iKl_#0&XxIP->m0S;M<1<)?KHX0DHr&;x9kh1JCAEm$Su>uH~8?9h`8^~2~iC{wo3zwtR7S zLez`D8Ua)Vr}eR(dcs`BA9dZ7zwW;H8i)3eA4Z3Bz@OqWgiaC2v|~%Q!ML5MXnB35 z@N9}qC%=g&p;qJUShM0@>m{VRL%IWmr?1FQT3$sXJ);;|Vb;Y7D`>7$)iS-Pd284gOCVMKJ~U`gjE@8^zLt zCRT*zS7T)oXXLYgSD)}b%bQ}BBiUM7P=RcO#ycz)6dJ<|IPS*9nK1P!`J;J4LLPR5(_;z|<&Z0}wGYGe46vi0zdKq#v*{ zi|gdrFF0V8`m(JOxSlnL;Ls-=mMXNHAN4#&2v6+|)(%Eo>zx!cD2GOK=e-cKX2dCd z=~9hlnKRFLg+>Gs?yYe@rU^AK;kNLv5h=FyRaX7pO;~w0A#pgizk5h6SnfORO=sXC zcU8wcW-{@|E^kJPEg2F3??ppAEZlK;>*X(t+tH ziUF6m@ntxf)LE+LyxLiN=}erp0YiKL_D=)jVWJNKX41qFsUOZu9o%O?cfS7J|W#lmTq2&bNzrGJDU zIpy2|lc<})w^-msPs7e?fETiPA`%5=J67@@36i0(2aCOhGIE-)6y3=Xs~ zHiHTTPBPzy4FP{B{6T14QnYfFi6=h{63VF&dv9s$_Q94Y9U+q3>cGj84&>litlP;c z;x`4Y_jTL+UHa+e_k`nmF~>xJ7NiHQe+)|VF~Sxt(~Fke*Qr!sk0gz;hfs?xfbXNu zArh~>6;z~f?+A;TzXP9-3aJA1?1Nzm0x4>oLvbJAWT*(+UdTS+u(f=}!OtuMFksXM z?sR>G$5WSwpjL1Ac=K%V++2R}#8a~A?q@L?Untqf&+@Q^vZty~`~LGJ=zTSRQ0(Bk zLQkO7JZf04v^1$&a4t+r^xvn%u-#6YnubB*z1#%%OhKm<{224yZM2X-kH0wHdu!qJ z%Fx$i6k+r95X#~5^hmLJ(zY9Y_9SPTS&S92C|h)_ttC54Oqn+S+ttAz*%f0^Fg(w& zn1XQ>rhLPdF;x6AGZ!S7SmM#5G9FpFegUWK=N5Owl|%3h7a*=*7O#iCbbYL~D^zZF zpBhtW(A#+;UHcl3cT5W{Q{#EP_c2&>Yof}a)LF;w zjh;J1Ebr9iJb@9==DFKGYkL+K<)vAQ#D;*N}oO%=gy+&(&+-^yp@;qVp8rM_U z4!wFyyA@ZI>KhBWv8|~iwddPq8lA>V-;7Xdy~;FyNM{QW(#gqdVXBP&3H2gx5>AN` zyr6=U5FO{7?(={A3FC7*T5fT4a1^siIS9YIZWJXG8j6G<;%_!7JA^`gD|!rb9cTn# zGs6&p0}1^=v7U2|Nf%GB9PX)~S)l^b5*lN{>wkL1*Y_I-+Tbw{U<0@DZ%Ryr((C+m zv#xEU>IVk~RW)R~TK7oGp~pyW94jYh7ad?Ot6cH=*~CVp(G zFW4`W>zSUn=};hOxMHOc&3(Spb}dHwxgVT}z&RQ@bv|{~O;?3&%myvkbN3b0Iys;l zv1onfnI-(BCAhzy0;j|aj*U-?!F&xvcn!-e^rW-^i3aImJZ7$+jAZGG@;U7By5_i9 z`kS8tTS~Zlw6A6y#Sx}*GKv4z&3BQ4{>`m3tK+UIvBelvzJ>v0tOVteT>;l7{n(OK z-}!AOJWjXfWt{nWV2UqofI&(9F%t*I19NOqpnP*+yU|&|wSJl7qDJ75AJiZx;KpI@ zIHc-K0G4d4L}nr2HyHqC?KWI2B0;BK^KiM9)T8MOz2F?#M^$8paRuJpNg1_V?YgzB zxou_~IK>Zf+%^VOBpS&NsoW9847xkKate<>FYtq2-9AJ+Z!G+p6CO0p%e%;}(mzLJ zw!O>omdvk3UNLcG2CUo-OfSat`M0HFtbF$<{o?fLBr97qr96BrZk*CR9z&w9`5N0` ztfjDY;N0m`Y_e&y72D1B-OGwB>jLC}$oo9k#6*8*hk_A`BMjwn<p{E5!|-vxUn1(#^N#tI>puN~_3G(t+zw93Nz1o?w;+*C?3l-b)}# zLQ9ZCmog#_1RF!%$enf7!gD>vo^lR+FzLi+MgkAx^|J!|p(+djiok9{x5{kh7%ce;7n=nZ#HZ%WSX{5%hs_2Jw08!ucsf)ztuPYZRGN;{_o z{MXI|3cH#6?^^^@E2qbzZ;qw{Z>{!tT#{c@{*})0JHfp?Zi1mza7@Qi}>s7A7f2-Mt#%lnkibhFFk6PG>0G6Uq%624kpO z@*1ZloKoxk`+AjymdL{3xpASy zUlD(Lt}wjTO)%5}j~#4q^f0pxYmD_8Hrd!xRXkA;W?W6=q8vh~;t!OjpAY#HNU<10 z75AbKpKg_6WLuV%awpz#Cx+JwExU$)8p$xl&~Wh(d-ch3)`fOjawB!9=Z2__H|p|3)=ER;^oc)^-PAuiq@kH92jcs5Wg10Hs$-53%qETtf2ip zwtPpBjX=ckXu}GJdQ0?dTk7v+JxOLQZPu9s=9Om8$($W(^G5hualrtSHh%zwzQPN3 z9>?z%x}6#C=Al+pCeHfwn?DH~CDmTvLG^HDf=kp@lcWTjlmiL)I9ZK%KAPcFf;#cE z0g%}`ZoETHuX+ew-oH&9Cz_u;wz#XN%nXP2HzI>*KgHRd*Iot=;8f7Ah*(8mc~bBi z8z8cGZv)%ayuf^0EYFLNIb(hZ>|d1MZvfiB*Ed+dyIc?!&(O2k2N{O8G zo1Uqr{iU8_O}lx4wyj){l-}qaPjEL6^Y@!42uHu|2uC|aRp5O(F7JJDd6myu;PvlM zV|T%AOycJ`xNdJUy}|=b{r0vJ+UJWaVA*F`7A8K8l0v)uYRBLcm+XG?81I04u(8ls zra<5d_V8wrAv~ed@G4p6m^yz2A%2pEn_Vxnq5P;nIxCuskh~~Jc{fe8ZFt3otb`fgKLg_ojTg+9tLU;x(W*mlOE7^U+_b&Fx95S9ra; zyMN>MZKem!QZx9INq-YUc4_TTS*dk_T~z%flJg?lYe0P5jT=h(RC z*}o}O)ZPG3rAiWm`zvL*Q?o#+(I;1kE=!`D2kAB2U%SnxaF*Jx;?tjWhHv6>>2W3{ltOGId?Wi$ zsJ86qE`x!M7&#XxF`2l;R@+`O@toa1E{Nfvu*DR_?zANuCA180ai%UCa~@ZawluN@ zqkwv#*;p=`aVQ|dWdUzzFW@KjtEJ2Sf$3NAF1H@%|q0bKCHt?(PaA z><p@bFZXzI{d<4%ygh+h&d5H z`x_MX*`I-JUnfpJMt8Q9d4o0ZRc^pLc}p+*B^C#q|^v2&2OhuAf?m9J>SJX>3<$6M(`NDou!vt18_>@ul5E~=&+=ReL(x65O+VBAF+ zQ1b`OVIORf&Ao-1tefxgSsb+0QZ=}@)~Hsty~7u~ci!9csr5uIG_)sZ8WFU_*kyed zt3U9!E*iwEaPb=yIF^caiOy)5b-8#3-dPHBZP1d28prmn2Q2-Fy65+9yKu!b5GkH- zD_PQqJyyimk~a?+=Q5ERgR}p_MNKqZ@vPY}^BkKR%04{W{(&GpsQ1>A?&1}xIiN=4 zM^a3=$Hy%k>5d|#F#jn9Tey$bitv`tLttgR0+eYOsR?0q2pdgYkA={U-m0ahN|mu% z3(HS+T>^b>wgkqI?yXa0@Ma-FVfk>k3sZK{7GmNY&Nv+g4x@kctlhr5AAVrU*A?kl z?NG#=^L-5|^-X_t8FFbve{L-%Eqguf1GeTP_J#(3psij4Cy4XHlsJM)uB`h1<%lP+u7{O0E ztygAv!FwSpXS%8Vk(Bad9<7s|I9Eebv0^f!AsMBDsqfg{RJ0-kz%bO7*mfrdk8FF6 zIX=9zc9Y}4!=&h4`IUc9K%P}_|MyGlu(mR^!#yZ({@zKd8>`0nDsuPQGg%?6f*>JXyL+aVf>St$)+FQx7r9)qJ zjp)(04=21PY)4dX`dHo%lU74&Di1u9m`omxa(oWhQfIN6R@>)3r%Bj3%-u0%0|y?1 z=$h@v`_rsVrGFF8^rL6r*MS4eO&gYVb$rxdujf|YspR+h!e#1vk$ zj=0wdxR6o~(3_3qBu+R@oSCx@ z2|_u6s?AWpoG^U|Y%Yhhrp;eb^07#{dB<)|lvBB5!o26%-XR55uHR8y@^4#9P+w!mo5yb>})#z}pVwdld1R#{tqNE)oeuQAU-0 z0$Kh>=$Ev`D0eLcS+I^@g}$3Ju_el+b3d+JqiI@eCpBb@U69$lpI|wg$d(vm`U-m1 zKsU4e$M3b9mrO=ZP;>I%kLP6l`H6FwqYEr^BJvm8)&fSF{PHw`m&ALL0ok}YEr#Lm zL=2X{RS@kTcD(t`w2~B?{ruo)eMR@`sS7o?wr7v+1UTzz0<*F2y01lLSh`pp<|;AS zHvQh?uSJ=bpExa|&VA*kxdJz%Rm+j>G|g`XQkk{klYY;6*<;81=|Ka0-PDks=f7~7 zMl!%+oyA5BMLKd^#i<#wRVez_A4tR`Y~#QE5o&Gk61}of33w8_TAJ6RzM5W^4JsNg zt@rZ2y{+PNa$fD&@Vz8#l`+&0A_rfFEvH1|cVG{TP~XaJkM9RF)X4FT<(7HI#AOL5 z4~c-G2inK`*zLW3LAeXR9IY&r^vVPGgk$1aW$WTCF;Ppcah94u+Q5=+M9N2kVMul! zd|eTff8UdAFYZ*71<@P({;<=!j$Km{DUOH9?VkHbN7Aj;F6U-(NoAnM$)BawD8R0k zVt2@=Y`eX`2k&fk+UYaFr9&$I_>9LP5M)_xpJH}|aX3pCU1*(c+T}L8uB^r6+JgUn z7E^=wimm%Y_EGe`iY1iJi&5PQUD3D+6=35n*Q_qt`Yg!5nYRI81=!22!54Z2OsZ{< zew*(qFx%vZ@wkUtUgN+%B6T+fie%?OdB0+%ZF~z(V7`q8k~Wleiv3 z>ktXvY_*l;)4yP#2{1W;ZB#6WjVzU$ zSNgr}MGf<6R41=S9q#`9LB3(l_s0c{6Gz=}>APrJVcXvt+ zNH<7#!_Zwr49(Cy*Zun+@B867o-goa2KKeDz1BY0xz;*YlQUVpB%MIP5C+Uo>yy@% z1Ee*_Qw&d7PeGD}NaV`RZxb3DHDE%Vp!QwvPfb#RnWmfLQswaCxX>*t;kOz;8l}#%Jk1ppd{Swea2m4UAeMM{f zHVB|^|bRT+9!6v6{;Kc$3$EXHZ(r98%aCAFuEQ zzdU$P=_4e(n5on)Kj`V@b%QT>_?BsoLWZX#BO5Awb>%}eN_i1%Aic3fLx&CSBi>J4 ztjnLc>h~W=%xuOj@)abo?H#GqZS%*T^tzyq6{Y8aT&4iA2h`Nl)-SH zrm|cxnnwJz1g&M~q!P1B`FL9!c5A2lqJ@XJ*w{8Dqu8;7s5&G>t}> z6kRte7We5pxTjKXSuHWSw=HR;vWf5EP;@Zd?8%m3Fg!^acqy7n34m`#qK+=K(gY+< zRomYV2%>XY%j@&g+i9%+NK$yI_Gd~tWM$R9)jf0lU(xEgBP+sVVkykcxt95fc=cvs z0L`U33B)uVq%DRMi>i=k|D>~2o#-{q$VK0GXJi>Dtei=nQnxO6Cel|i%gsD}^ye)7 ze0T{77>!lSIzmKiC56|Bfofa6)%)?N;v$nu?N5baM02>j=-9iWAC0}vSPqhGb8&Kg zbNgdXxDe&6#=^_eO^jmQcG;!|Ic67=g^r9j$QN$MsL-~vv@@@ZAf&VQFLM$>^X3+$ zi%l!L`_}R))H74K<7j>7otccEphEy?H(&hm*HnwS5?{#3a`B~kEU3J~`y>Mk z>+D|jDTOq|Za-wlJAZL+o71b!ic6Q&Ye-M`xWL~t#B~ghd6LL_<{EgSm$e+_ajV1L zxas?RT+H&vLSByu6H<;ouk4#@D&IuuuzLROrnya805}|1v>WgYRJ7@6?bnbU3QEYv zo(e|levlW$QKnFBb5upjvz z5ABbids{Un=~}KDHqlKd52C(KnP7uV)hUlhO4Q?8E zt0mU|VPEx{`mlUnn~nelJ3wQS_nrwO0M^CvS{pge(~8jkjfh(k80PU=bUAiA+Y1r! zdCXwf8@uM*&QzWKt~th7x{cseq^oGxd?bU5F-vp-OOI}O{O5)S+8>HSp(kQ4`(I@=ODNkhMsn|(uHKF{QoOT@Y}OwY}A z^>PN@84IH?Xs6r>pB%+Oo*)vIotxz`_?cK}5Ib{|h1gZ0aTb(S3|Yy>kper(=e;az zKF5n@jT>(57IZVISFpM^>H2|NAtwEA;Y*63ov&_5qhSS^aj{8D9}3tS1^RS%_M7t$ z_DtcK74yU8cPQc&(q#Fo(-D)a9OC^PedJEV|NXd1vDjbYRL!`FQQ(Y#>r7)Z)yt8} zJ^jR5n{{$UQ^h~u9ZOol1)~u07g{gSk@4medaop}$;SGU-Zz7d&V?A6q5s z?lh<%Ej>@2rcx)gLSzKBf$>)km?-i_)3>}cS^vl@xcYV=)vFgdN;S`L^I*ak@*^y6 zbz@64!@4ByG}$WfwYgZM@SyE-i%wUat;1z!&uVIujta%1Kvkg4bc5%WP$~;@AmMO! znS&TjVF=A`$S-drSU?6hGL)<*NSX-vwZkC5&jcM?b4_P0m^WiV!&A2Qu|~v$i|Hd+EaagRJ@4n8TgoJ>}r}&kBB}( zXjKh$BL$cgB*_y;etUfyw3S_?^PIf;5M^OuQMIU!?{mD+WF@8ry>W1&kxkD(;&te_g;37dA zqm%lzg@32iz@oPRy-ipgunezpNL64#oJ%N{ge&+hZK{GhP!c7jK8pL}`_gz!Lt%E_ z9D%1~RmrqCXD<;EtwASO0!e94&RSKpF2V z%TNOk`xlhEl)rJHABNKCq_0-lYdR1TN&1`hC_CD#ErF#5{wX`;HM}$7%JD+Qwn&@e z{(Iu`3mj3+BcJf~?EB!7|L+rqe=B;PprGSUZX9C1j4~n8R~SQ%+?8uX*+7>;F$X@X z!~D0mg%kkonHst#Gc`sMrzk>U=bglM1hmFG@D%s@`q`*T^!A;^CYjCB5HK zZqV9^^R>+VM<*3`I)b-SB&J4&h2MMBO`jUvJU_b&N$d?dj`AeXDxg)BEOAAv2W9=X zHbai6&4S2<^9D$1@InaN{sx6?pE0GSbkMbL^y%3V5J9b?uOT>Z3nQZ04-~b&-)3S<8KC9~=ZHPQ$F#q0J;cW;#_Z3O z3?Y^k92)q$k@HD7%r>e^MD#3a3HScdv}}WpcXi_vs@DuIs<-KjJP~nNTBBW64B%uZ zx!976{GVkZ>T7=n2~hX8_JOSMHw%QS;9?Xe6Uz$6ns2-kz5USwi<^yHR3S4c1GQ z%940gTsP5jw%W>a%Mjf?s5S^r-?^x)gqoT)8wsc|^kl zE_@;{w4q~3yS5>6rVjcCN+gASiv z0&n?Z|MYKF_4P7?T75|z3ff{am4uv`p>I}mHPTbuV`ysO@(y=M%<2^(nOQxoA<}-1 z(cjj|aa9TydE5`in(Iy%V{Z9J7zDm-CUM!+AFR@2CdaFo z)KSn7R?=fRjn;;n9MwlYH+d(kX;wvE9UC!dr7|m$hKidNqv5(9la7Hmw5`R<@jhFM zAgQn9nst~02PH6yu9@n4e{fmP(~OgAd_5IC&HrzE-~7+H{^u|tVv(gxZyo=)#|)vN z#65470W&lIqU_)e96v(vFd#+N8%bI^^@;~5nXzj99TJSQ7I8NWa;Y_W;=qfy0r zWI!E+=b%%?%BS%TpdWi>TV%QJTl_bNJw?>8Nf&7OoNROPbl)E+a{ke5d>6^-x}|x8 zY=10&=kKXrrom@^sYk2cAF<+%2VIHCqX68 z5+{YzHZ+`4P@AoB3!l-)z(K3tC!^)!URL_X+1l@)YP#rMs8FapT*qq`bL-ozxdD-B zqw(USlzQdsn;lX^WzNQg%ajv_AUpENW2bLbfl>o-@I&OsQ>Kq!8LIO)Kh`m1Xm_lRdKZSp9R?Q9I$Mi{3TI&0mxp>tO^(+NSUk609q{bkld}cL#24~bUne2s{c-|=z(<#k9VS67N?%C z#vL>@68w=htv0^ASZ682H|mJe16QqM<$GKCTQq0G7~j&Q&2o* zZElw^SHyZAw`%BnoYJ-iANuG%M*+zQ4KqxX#fv(e$tJG$21+q{8jIN`SI5?RN^xKg z#}{4BK3JtN>gzZYC-QXURDBr=W|4YIuN*ePvDfrMWUy+vaci}3)0=y<8FF$5~0Qi7=Det9{PB-R=I8UpE0w}_u*)h_+X=<3EH{}f^= z_Pq&EV2@T7my|x29C`6Xu)V@ibn0cAvE_w^{eqfIpgdZsOt}hd*gc@AW!%{rx?=u+ zyU`LPQ2H7TYbbF;zCRdM%8JE3B-rYk8e+LsSc!>hR@u8U$l#9RN(}s)F95NmnRh^l@X3$JC!O@a{#07wt;&|N z!wEor0m9W)P3dOvAN?no>xSt-0aSFqU5~TVT3^L0un1L6C{jh#eOKCDt3RG~!sscX zilI?`_?tdYRK5RI^(R3N4>%)a|{Nj8U#oYCw6B?4;iE z8!VK2Eq8hgQ~udzjc;#4oi;-ne7FJ|^Q<#%Z1YsDEr~gfpg>k5SlV4da7RSKy)4hlRklzvRVtn=&xJ=%+Z8~ z9yrm(v#a83ambMO1fA98rAXwachO+$aw)A1 zd}TkXt!QJENnbFgDU-S`CtXhY<;9CwuNyYD3N%Jcw>NIHBn^oQr{*6HB2#V* zPfQyV6&!-&p<_h!;ss>X#{YBSc!6b1kJ$H&ulhcQW|-4$4&g^#yRyfr11N-l{8d3W z9yhb5{os^aM|Y#1>Wc%v7Y-v-;%CW9ErwcRIO>PgS`QuZpgD!Pvv(6hDO`#=Z*}yoQtmkhM zcdkQ(%Y59~S@?x(^ZyMJl@k4OcDpYTL1x1F$rK8_*srBX2_t=Un-gQ?Y)#8Ka_FB; zVZHpHixZ$S$=iAWhaL0z`TTG-nfgoIW?o-P2<1QgHTF_eb8`JF`Q0x7#&H8@9@(&p zF!;nF^G4#ORM2rBTiTria3&&SgZ}>r|Nm|u0;vJBB;3S9_6+=0c{vV3v(#4M2YviI z)L@_-MJBK5GT&one}9@`CmD;mTFGT-hqcKE4Ml6WPtc(?qEYD_sos{fBI0qt@M@|A zbcYk4I%6f-8zHb0&|i|lR<*D=txl$dc{ACmFFq$>w?TO` zU%r&L%CScV=o>=?h?g_4>Bf^!_i@2bCzuJrvgd8m88FKf6-4hkNC*1P`t+E)FA zz-X9bH{Q5I2^{`jrvZ{srdzvknwsp?#jIy6zLUW|YsZ{BO2Db#<}5La(lt#2?$-S!0QkKw zZp-2{P!yD7J2R2ZspB0giD1o9HjH609N-GecY0Gfh*4m3ng9iu*=nFCHa;w5Q14Ma z8`PEy2Z$(|EY4B%$MZR?w8Y(+qaus<;7ThP;iS1 z0R$3*NKLp?l+FiXlUd3bBQ=C-T~*d+q)TaVki2q_qju20Ilv}UPsd`jU zwh55iNt?C9Jp0ztuw<~|lkl?07sN%~uE|+?(Q^yqQzfR(Cvf}d| zo#LluaN;cqG|~-}DJ$-NsGRNuma>%RB3t%W!s07@E-Z_iYi&)I?oIrZ2JB?^TUV)W z+w|`sd+3f^?UC`Dqaq6WmdM!qN(4GidrfzN<3By(ugu84RTQ2|tJuj(xCvlN2n`s5{zL(yWjoY=Nzx53soZ^h`- z-$F?E?I9a1*}nIiRKKf-EjGG;|E|6-7)I@KMkg^7>;Maq`Q_*d32aTu2Asd9*^0h8 z+K$c?30QU6@25`EBhn=k=5N*gY25M3$;aLm1%z{cRKLPDQ=*o)Gf?@dYwNU+Dv3y5 z=eu~d}reBU%Jh}oU?3B%UYI?ZEmb5%?IvUCpK|z4%#1? zMwY1@MjH^=tyC7UWfhGmo>^G5W9tz7MpZsDw!+M-08)6nu%DV`o+^@Op_Qp`eP`69 z9Q$=eNj0^pUHr-Gj~D-jcyVKKq3-!LkK=(={h+LkLF^kg(NAqD4-ukb+vEKY919)C z!Jecg!#Tc!YWv4!6)=qE-2l(6_JXH0s}7V5&(TJY?urrPDB;50RrOnib}bBFE*qWt zgm0|c#V>|`<sRJ6@xs(xbL5U)=R5R$L|_ zR=mxDov6e9di@^{>>~xI(~i#>2jrK47=N=WzO|fdFWkI;_3?@?h0_$1M~R4Cv&_W} zP^>?LwLhwhwM2vV6cq+1}*)5|L4C^ zVFNuq?6Zt7R}8m<5(-vEJv>%c9voV(;8xqq{>Ou_eT6=oHlocnY-R{;l}J^nNcbMY z`vzZ2QBY?^-l-*s4Xz+UIvmQY%IoyZ`3Q6fLR&!D`%Ryzgzg4t>Lk#~r|yY-E7zv= zz3!soZjnh~@+kitgn4^cGo>&^b0dxb#%NBh-pIS7W9!rpQd?NufzUe12c@3{HzP2i zsN_a~+dy@2MeS-*Kj6m2Y*T)abiDCpS1d}#6FLO{H&0M-#!$xzr3=ud(!nwQieHIB zh1fr5EJ}ahN9j~HoZ05PDXth0^X#n3X#aKXa^ln^D_ z-T15Hr)$rfqhnuY7nOtWQMXS%cfNvKJ{JR0Ls?Nb)iqAOEsBz$6|xI`6q59_9nwK6 zTF*^cK1+TeYjm)l8tF@Q9a1Z{_v?k#b0*f355D||cmp*!C?t>{&Bu!iQ@g3CXwa^$ z0Ti@6!u7i^4uTyW$48g>8$UzO>_3iYOcp6N0sbl8+p7ZEA1O+nQxHnl4_UHrnJOVHY zM+Sy^DN%Ujw-N%m2o!>Mt{TB@*Be+OJA9^?Bxph%;9))T*td`$w-y3h1xZ`cbT`P~u)Kf5|w2HVC~Li+e53d-hLcuvJUp-?Ia5Ynf*s*H^iiC`fKd zz*`w-m?R{O{7<9j+o?s+2`2)PV?=Y$s`SJ1<#u40!dE71Nh%cV5DIrqd~l7kuW36w zGt%ov6f^|oBe~Dk`q_N<*w2XgD4a<5VhFR zgLK9GgH6l__l-7PP`)PMG6o-!ArJbZ|tgP~8O}J|Dn}rM=D8Ar((2a9vk^iKVOePPuB9D(u5mO1&-f#TDr?G1Y6Mw&O zCLw9Dy$ptmr^x$51;)m`xF`oB$%ZJGmDxZ-H>zLW&4Me(W1YVeL#xU{?w7@A-c9yB&>t+st{> zH_*E;f_7IwRI@_(dDWl!J^Rx#d&4urrTX44yX6^L8&2EnHmNpKpL1{2XZ*NS!n*A) zvs|x6pIL5%Q;Fzz-4sfPaDQVP@xtj+R8X{CsF5Dq9m}eaf&!9R@{&=MqdWibm{{U*W$Y)fW=B{5Kb#7N%r&``%4KngNcrV}9WrcOoMk}Z zSA>*19*g?}tyIftjAHu7r_P}wLe{Epk`i0EGgx-<)OL{?Wb@5R8>0tQDc1iR?)WC6hi5vzU%CD?kUL{(CP5S%O)$ihm6 znf!JJ(nMo9NYj1~CLnlnMCr0d|KAaIqk?GeQ9>`E%QvIVG?<(_$xeeWUXb`4wJe_o zM-*4N>d6eduZ=K%(o!kZ^Fh4F+F_609!ivg8qT$v);rHnOL$oCzji{kN6Igo!gmVp zW-dV^E$2MIG&rV4dsTe!u#?|E!vFF2ABb_RHDeMm$VH@;aX<~&8I*AYBq&oBX>oV{ks-8q~iSnL!ZDisA)yF?9 zTL4blFGKBGn=j_H-D2SRv{+>!JIq0>z0$%#vH${C{;^w1cK9!X9p}7Ma}NVGSqQ27 zhf{KBS4i|k-6RbZO9W~SH`*^F0My-0=(!d6Mj`r*qA&-r4yMiQ=R(`6idf03hN6gu z&1tn)Mbf~Zx`CWa(`Us)M3LIrkyn(P~yT@tV%=aCbB zhGih^H%mS6Qi)NqU2B)m0a}{F98}o7A6_oBxL11!1iYPHu3miDozOBz82qvP3dk?Az0b1BeyGuRVQARR8@HO%KroPRt1xrP^Gy-daZ(7v z79t>XHlP-T%!l|2L}b zfc28Nb1HX2ipo2lKa5WJT;!w!#A(w1>cNJrmz*W^8TO72<(XT4py-qEi7(ZZe-0Re zvKC3stI}1*F^UWMK3kSDtCegM^>CGNWi?x-_1oa?P1U#v@;P)^>}KnY2oZqTDK2Fx$Z=3${xEeK(lC zr=D+?&n?{JaHZ9g6BofI74<~0amI4~Xq#53Wg^GpiO)o|ef}POf3e=~O#i>3 z^_;5@cSsnGAt38Jc-m?_N~XFZ!7pcXL%yD+#e$Nwp!1&FY9ftC;zJUnJiC~ zn-|H;J_hrz##aff9^m>J?kZ^TuGA8->cG!ObSgjYG}+A~#5_lJibx=}ky9H z+Ps1?#aVUg;=h&SFYSTsLrk2k+Jew@l2)Q-SS;uMGMIW!YQvp*!9kVAE6A9yAdR4?n{NBXpuf z+qn6IH-7#Zb?M<87ZXxhaQBf^R0#j&G$d{0e5_pw0P{4!jI|iV%){dyRdeu!bV7d~U&QoDkf_v)Y5 zmsKhSkiO2-%_57|t%pQ*s+xNCmXpx?V_%r%kH1Dex_u0DB7c?hcFwlq#=h2C@?0RV zj^1|#M?`N%iG2b@iCkN+`e~vLkQ4RC5cqs`J0MsDbJ@5p^n?y4+a&}!c?x^~^Iq|; zZ(-4?TSA>>TA)BH$)C+ z0<%WkWC2xtFb*i*{c3)f;SI0T&NJ_?z?pI&fcOcy?C)R}M3rgR{wrwKVWx+xx(|qr zp`r$|H6ON<@|urskS`1Ev^bG~{rvtW&LuX<*IBRR;@AYM;=lGo^^+C3znEg@p}fS* zeO&Q4pg23}ih?)m*L*KN@aK7)(>cJDQ1!N(!m8fJ z{>yz7>_GV}QTEjX1z+3i^-?`o9!fn9Hf*M3JLHKjL#(fIP@5YeLKHeoP zx()C37C3ezrci^s{B2H363{Ws|{cEk#UI6b@rY95?k}T~04Iivj z;T~1`tz6+58i60`Oo_Tx!$sY|u`UgsmM12p?cqx3ash~Y8F?(Hl`Eyu?T!{k*hb0+ zT1d~(C7^WeWg)6L&a;R^Kr^a@{SG)!)5Spw8G`U{83(iFuvH*4=f=6X z)rrMEdCka}re{}JW>qPs-pCDGcx^Y9*$UlQf$rjvay!d3hg)TnS&;{Ck$BJ@HXpZ5 z$-sB%xmNxf7nSMN&Y}kE!J*Yyell`J${OM-&vBDF=n?0Wd7KAi7xCD zUk+UXeLx{hqv$`)V&>Y>Sw=*9x+3P;%e-!omy}!nF|V4DU3g-LAEmRIeeO@Ch~3h3 z58L$Gln0s96^3ty7WEcGeY$HVQ9zeGgEH@pJ1Un@g9&lL)+ZDp@(r)&|QUKm)8R|haNw_2Jn-2t-G=|%i&qy4Av^FA1<+DpN&!LtTj&c@KxE1 zjd4(sm^sW{jbnh)Z8~1bDAFfUYxrKe{rsb`OCM~P@Ogc}iCw-ws@m#~4X57Y*+g6* z23O<##&R>VA0Uyj_F4{>u!i2<+2rH6Z}6=K&7OX7*IY_I4?*(3YZR>aqt1=s+R$C> zd(LW5rF(mcNJ5Bc^ShuFiq19HHx1k=#>UNfHV7WWUjI5j-4^DJqMRL{udee9C&rCu?=eiMO3{gU~3mbLts|W{N@WR(}$+Z zD#*W|dncgbFd{Z!gwyU|r$pHME@QT>!~LnBL0AOfO0yiRUFc-FEFXNLHpKaOLC?eW zn6cI4B6eFgX(+|~Fzw(4c&lcGGf-D^-^TUFX>1SY*o@s7B_ZYczfHh?h9+s5O?qqI zPS#o}nXe<3e+n*E>nOg!GVG`{c{EC)iqV&B>94g4$CU`8urH)!b6hR;yy`zm#-go$ zms-pfj{7H1uh}_)a@pg#jTKU#(`4<{kuC#7^9UuN3+-Xn)6XywgzdD{(})XRgF77d z|K&8i2z);@MJFHDLg_TIcAD2GI~Yu$)n?@Gho)QcVT5`!9!JMydEce!+#n>9Vr$9i zS?P;CfWS$*iR&HgWPIcEx25B>h4Bqdmyzgoi}NX`1*}a^iCQniRIkfSBZ?0yA440K z)c@yo-t_P!I(8QtPTWi5-1dOnj<{ZVrpcQp9?lg>&x^gBnRrHz{ryCF#wx>QGp)Z~ zLBkn58bqv`A808c$;T+5IY=}f$&NrrWrtq+7)BS||599;ZrND0@V#sqCpt-)X5;6O ziAr;Wzh1S&sRD|0^64dhb*80>z>6Hw(EmU`Lnv@SJWM?Erm^)gxW6(Pk1b16%4B%W zUz^RnMiPW`QE&s#Hxu3(QfnZ2$ zZ2sPQsyBkucLhI1)H^cVA)|Hx8j&I9*7hv82N&#z6KsY-GKx_~70T%XpQd#nQdRyy zN=*d@_tiPVF;h6aq2Xj=^1VQT|NG}8woPY70*j2(VZQ&oOdlvz%Kq*DZ-F3EZ1A{M zb9?v0nZwQ5**yAof%>@_r`0VBoUa2lcpRgvs$dA8r{O#Pjrl)wIf;R^IA6_W~g()!Aj3 z^4E9uJl$H-ea&Oqo6Cp_91VR67v>tiac3Jj6!sjx=@W%O#HnU`S@bG33mYXhrFwf6 zLs7-$f_|RilFagVR05t`9*)`t46kx&p3MTJaD9zki6oM3-x^kNxu2Qnd0VKJYxD5r z2s~bI@`&v9lG#lge*)gB=-nY#E4`V+c!_$c$zVLAWiOzi;IU9+k++K-M7Dl&2sp6m z17RvU*>uG;UU?N2m3*~g6;izF93Z-ob~QRgyoRSrQwF9PZlU8*mTmXD1Il~&IjAvg zrXvp+d~UttFNk%sVUS^60R1(7`PJ|HL^ikfq{6`Nw=P%2u-go4SW%y$Wqe;WvjqFY zmH*V<3n#l(wfc1rmdu9<6C+5yn|=I+*kMvzp~3IfC7Yam<=EY{0xsGkqor~!@2nIfKbl&7Us5OV7Cg&prGZ15|GtcATse!%7X?m)Nn6a|EY-MG*61<5zTj!MfX z#+Tnz&Wz;d^*n9Xl(DOG3gy#EsyaC^Fkgl6yz(=g%$LT*S1Jo&Bdu_`oM@DRPHEN@}QaVc;fXe|!}x?XP=) z4rXg44!Uu#AJKa!`C{+#uKN$Bg3O%gQW0QBbf)uaW;bWsg1ZBI&$fHN2ccpPj{cf> zdxpie^xx3z5-t{AkkHMRcjmg!%XnOHzD-;!(88D~zv%2gcPJoRl^)CA$7tv=XWpzM8=zmy4$T3H{Oo&iu%d*Cnfx5(PgXO&!66K?p@Ynn~MclxK zlCMoqE;5-Xl+aL=kGCuslIWSP?sx8qAWF8@5M6BwioJ$$|41<8$INy}i`d*JlYPsW zQ{2Gx4bi`@2vzc3`?GS^D-CN7V(gHj58pLrEA*MMh*=YQ!V-tJUZyBYkK>-$=$NAx z8F}sUJLJlH0oza`9srxtuC=6HTnmr~{2PIv`8&ID(zVu$eLz) z-m6w_<@bDPNHLRJorqnx%+GLu6v&A3k~C2RywpH)P@3ue#t0|t-y5}L61sqsRH=gN zjWBk#e|drM*Ln6f3(q>h0P!o4qK1DMXH(BZ1x|OF)zWUv1_H^CZtBK-vXhU1w)&L3 z(^h=$ELaKtCw<)_0U%yjUy_`-;Np<+%0&T#6J=7fNH)fBA#3x0_UO~?KX1r@xB8tv z|7Zxj*W1Gt*m}jNQ&*-7MDqLoH~{Py>qI%7s3fDNyzexW$nfewNoLY-2~=~-KJO#t zF+Dtq;WSgbLItUU!tv7&7gDLiBz77@qjGKIm9Dq%jzQQJSJ);McK9g%^|wI&sjbgH zkg6|~JL|nluYZ7s?$Vq*i<;9yJ=)HPi&JH!=Wg0RERX5;g`?lbpTSQJcqAkujb&~9^lU3}(CUuw z&)4<{lcGY|#C#nrBHXg6DGm9-K(f+$j?Z?1Ax(G8=C)>@(?Qs_vG41Xb(tZA$p|QP zkTxBv`C#+j1}*bT$}@=^#|X(sNE{wXxb=tIU^3frnhaJlhu}=Fvw`juP?KtrWk}4f z;TK}OvZKXjyy1xD^1Th2YDqV2Zi5e@A3 ztnq4ERS|A${jTfw^5B4nM5)7!8DRDR-mJgwb=~SX&@ci}7H_UEeIG7iE}~hNsQLen zTd=Ke{QQOE=v1-mW|+i-$3*YS^wD3ftt2^lJheZ0uX$1!E&C1p^b+tzF1zszOX|Fv z{dhth@*A(u_0#iLEl-bkslf@vexa8HTYVVBGxDk26ZUt&{UhYC2SnuKXCXcVs`x17 zbz-#%Z6cnsc1S3ld@>8b%?>$E7>9aN6bZc;bk~7xLPbVVRnc*^<#7sew3}!?q6A@K ze6(nXd)#R7V54+ZFaNA$s}A^4-?$=}hk0=&TaV>&+D*Sa$~H>DDv>IWJNrR+AEoQB zx&*0$Gh~s`MUslN@%-D=7ldmE2-5kz`@u#6O>q2E=6)ox#d>6dOl#fzy;UX?dtRTh^tJQ zoENHPkY$R5=`V7>75=Un?B_qB-sWETRZHkwtA~r;@daR;`!15k0vj1oS20e4SmfV**Do=Uha|-y$WX<7vi(R==^Oy zi&kdXFwN+*(ZygV$$prs_2GK`-VF6UT#E#`#fMxcg;zmhRs*?YKJxF@+5U8b{u6~% zg3m?-!h~){1aAI1#YHqi-gba16LBkK&f#y9$EOUgA-5Yf46uWWX`RmGl0* z2B>wu?dM=WthKD29zW{B&ob+bzdetE6xidvak1LE&Rc;Wzd+b2QQ&)(d2}Fa%!UTW zK88tsW7Q#)ld@jOFB2*&M2w3_&Z)jXBo#!%=AifvV9Q@d9ZEG8%L^`dL?X6}g$Y{A ziEKJ-PP=9;d#BDY`>ZFdouU2X#AdRx2<)(%wKEG!NH%IAb#V297p6vwL`o>2KoQZs z@*1eWX5&BBbtw;@WGz{0k~ru;;2_2^8|e(LC41Hp&0VU?HdQpD9+$>`!s+(qJ=dl*xT1mS@@r0v~*{1P;gv z1_7b!M1ivpi4mYQAm_%ImW)ZijRx+vA~6yiC#W#QP{t++WUchVHeD6-Qlpxz{zkb#mKh)+GpmIYt1ef9hS!Fsj z-Aha_t=m`e{P8t^A1Fl`U>l|d@L{JjN`H+cL;$Nlo;mJpf4pRm2svkJNea>v-(1`O zhfXDTkMAP)Q`k1+Z`RJzD&DisF4|1W#agSP^@bVVPC{-NMELUxz})|S71RFoaQwU4 z01W^`b1Q;e9ez^$%*-v$W)gGF=3lPIz_}}Hn8I>%QQ~ub#2uEB9ABT zfGBKwb-vUmxPiu=CVmxx*pXxSf_JiLxdunwVkm%n37Aaa%_+DYuZnMeBh?n!S&wV# ze@=gYYaiKXc)w>K8HMGPxvQ#hW{O*zZQo3$UdWF<9C}}s+e%$=s9s~g-q}5KU+@fs zy<<4^MK2n z>6;v}2eL_QGfo%o4~4*k*8lhx*mA|Z2DomaWwiclb$ zhtUMq4^>)y*KOSR9(Z;+Q1S7Q))1O=GhWQF^NsKfxpLAf;Bz^nAuZ8ceT{(nzBKVO z+%8Gbj)9fWGXNYzob;QbfxVbblkqi<$rjTvY*g`v6;jQ7wt3H^|0+6yZJXe9B0#s< ztK;}p{)s9#8yu|TeiP|55iv9z()j0;f$N>H(XLfF+W~ZJ{to|s1G%7pvu7k_@?Bg| zM#^-)Pt$4sAaqNzPv$)>bdy@AgxQTX27tW0tKd76a{Y}@6Z_dVnSIBP4|i6}x$?bm z%5F3Fc_4~iQ~F&;`q`aq!c2S*^UT`zsnHFISiWollGg*fbhz6n4iv%Hkt|PM2I-re ztNnucJJQ6rePWL>{BMpr*73SBU%!v|ug5_MT2o-n`P~ykIoMT<{Lmq?OIR=@o$TWV z`L;q)Y*lyq?ACZ#A*i8VZ#UyrlAsC@)|nK6%>5-BdaqV#Y_Pwi8N)T(-kl{-nCk-+ ziBFe@^m00rh0bLBISzO&0$VYpMYBQkuhD>fGJs;{AJMMA@F_u{Iq(_e(W1sD|I01u zv`kUp7iA39UT=K;V|?;1*0Ij zN>rjyF7^HS?nqK&^==8){Hbx@&>Y96N%x-^OH$|2`MbUHA%%$f0*f09N;gXhe6>Sv@ zzGIryN&+UZ9mIm{&6cMjM!Nbx?AB;Z%P)vxZ2NQOJx;j)jiIFiYkJBH4KSq5i0Srk zIuL0s@Wm5m;96C~mz2fkpE|FG*V6`Y0sHLCqv?J?MOz}4h=Mj;&gu|a=i%)U#PWGO zG00}()L)0AOd%$;o_&~D4-}{$xxUu7xd`G-d#$lS0)h0I{BoV>KwW%x5+Q!r1wtjr z29i)}lpcH3B1L*qa84P8lxQi*WJd8{3K@(pR_4FD+>Y#s`gs;v!j<3k`(c|7x4rY@ zTx^&SoC=}z__MRP0@}7%!&?5NMaqyTXa8ojbuQp}>kG}{K~n$;o_rOwykJP#5j3tY z9F8Rn`_?~NE_9Ad@*U-q3xi-ZcAETYu6&I4HrE%Epn=(;9ijAfYEq+kZbFd3tQvVC z`q#xe4q9)_2)*~z9{cJgYRXBu!XGdqEyBY4(Xsy@n$E&2s<;2zGa#WfA}}G!cU#JfM9E)gITo?X>{{=s1) z!wFs^xrepOMbUpauLDw0$R}SozGWGyS>}(o_nj!w=Csf#QGK|D;_X9nu@0U9$J|~r z7MR$Msh@vijBA^Kmg+2QVN8O8jvr@5-~h9Fy(%4h-+XGOLq#;4^~|@XfeDe)DC?$j zj{Kda@R^9<&}<qXi~Ce9!wu0W0CSE&%L&vKuWa+Z&7H_&k#-0u4BsGc&A5 zdwoD?%;FKay`w_t>M9T4Mpb&oz6ZHIK~xP9j(#1J-#!OA_>zl7_g;K|FnUOT)q0NZ zulcOPkLkBk%#yP!GZ$UVZ?2?COF@$$>=UY<_EZL=V~2P2O#;&n({S{C2awfvRG!hi zZ@9f=*t+fD`L#iED@f2vKr+1Cw8L8zZ4p=@_8Zr*z#T}kVmhSe&+nJ+YzJlhpJpjo z8gV%wei__Y4R;MACmk84U+>tWQ*2qDh||lY+BOb0ejEK(4KlgE33>^fNLqzoFRVY+ zDVUp2)&OCZimI|kwP?7^oNfxxxF814Mpu_1DM91d1gWlCQ_4uH@27vSQZat;l8RoHR}z+dG`JcM zy!ZuN^KNxP<`RVW`LM7czj~zoq8LwIXQ|MFAIl5jFJnAysw9L~ulnNVEC1B@eA<|L z7ZpJe66_{DBP(bHIg2O4584{r{X`CxWF$!ZH9tB(davtwwdO{x0?JsQKP|mdQt^wj z>gjgws~D#DzhZ<0%LH&sZP@M~unhz~n{cttX%sR$1$Qb@-128qk(;be8w674q+q;t z2-^C2QUtceHhY)C?*j*+9|XY1<9jt?G?d26Fuwbk+YB1B#UlG{_z54!~1cN{|x=CZdOr6VX;iw=IXU%l&cy0{Quy zSnXNZEC~tZx5(rZ*kpr2h2_VoOvfLNI119_S_lF`m|!9adO)TkFi_2TJY`w85%dD5 z@@Tltq}(kn_lz2x)mDnA-I`?f7TfWy_g;X|`&6sad?g_KOE4UboTKhAvtx=gh53I) z9tm{TV|1{boqgqx)_d>&m8!hP!%-dD>+0LR0_B*v8|m&5xD?P2nCG-w#8o)}&Ivu>%BOW_ojVtI$bDm|XUT|`c zHaYg{wsL+K&QpxXHV1F}D&n)dfQr6N`TzdYeJrp^0P(ZbssMB_IIoq{Xc7LF=$Hx~rT=(u=9t?~ zF-9Q@a%?oTeXXx(p|FDO4cZvFRQt&asat#rhIP1sHlQUe#6BTSZ!`CAr{BjRQktUh zU-0KlNeVVs&AtWUdTMV3Fsq&X1797yR8T!AqXRxImoEM3dd|Ms=Pi;1)`y7JGo9! z_lW@_%_&zYjkx)p)npd7bLr)!AmJ)7jF@wkS-8>!j%E92| zj23NT{U&57j2Z&Uwuq4(bg2%jCTe-RpT=VS_AtDC@nxjLdI}CGDB6A!55^e4inAlpR@I`XPlThDc9yLBtNm{&BuI?Ozm zpOt^fiJ-^L7Fr5MsD_MvpYLUEaE8gcPqo*MSI zWP#BaOI-f>Ma_B{CnpKt>m~Qn(5=N!)*y6JUD9|>#=*FLjlK-;;xbw&+xpusU#!Sj zAd7ypetK#Cb4e4r$wh>&Qm#l5(q?yBSraRPO|SR@B4+9%!i`4pLI94ec*$;t@D3m) z`{@GqRWM&RAE4ry+Se6_EmF+?p>rEJ&EE?C8Kl~T2=~c`!WJR6Z$dPSABTNFJ!iiA zy`|-Y_d-Q=c%XfG!jmjAW|3wEy5g1p$lEB%8SrFI$!n`!UC;#+6Fy*9|685Vsmd%p zY>Y7LD;4>hspN`*OSty#yRY~oR&|_m5qu#qtF%%@)y6Jh;F8_mE#jAA=FzjlM?hdd zY~y)C8IGmJT6t8@Uc>)~9lQhmH@sD$gpzAg}h$=yhRps&72taXqd0!h30wet65V3{`70IIEU zPQQ^X0qgdI(;O>gQwC7BtcP4=hH`wW&Z<#kB&edKV4xX)-m%A5>@)a2zxgVrHjH00 zI|+3kd34c&nPf zPV+*bz!J^G+JGL%DBD{Yq^eK1U9`twa1(Sm!{02$k4yghYYlOr?dymGH-4f%hr>w^GW^$(C1`I13#f=^=FSzA{p?{dGi-O?3@-`)yLL$WeSoOA5m&H>PQc5-d)Tq z=TQ1}B7T;t0cGYv%GqZH7TZb-5hr0MSp;an5hoB*Rt*FcJZ* z<=*=McrI{LG0y_M^>C%C+9O%6kRqxGK^*G-I#>_Y*?p}u_;CA!u!v$;r#AHc1zpznHI}TS7{1SH^F-{UR$Q(!%m-gJhtZ;KPiFIEK ziDN~_qvU2w!UU9L8F*Xszv}xG($}|tqGMH<4*dX}8rT}^E=GvPUEv$%6fqk`C>j{F zQdPYc-aYicE3E-w--~vIFmP4PfdtM*Bya$bIl2=+s-Wt;O_=Bq3E`88yt_Uf%~L_3-SnRB z&a172Q;Bo4*eK`@06W#6^R*Vab=-m1r`uTuV>~(YdDTV9)juHe{APIMLt;A$RizekSzGo{g%gUK)oH!^JFfPRzUCie7_(IEsU2t4(hT9qmG8Ut`vgM7oWF)TKpNq zQZmddYN^j00eIQ%`(<|L<1YD74VOF<7)AL5 z+h_gXfV{UYWIB97?G>ZD4o}6Wp@~B67}4s=H=pyfQMhNjQ#?tGvNga$vv*+~Yk5Kr z_+cgf`ciuhK!3d2fBuW}e5LA_tJrLPxX+UbOio@6;Zp(a4r-zn4t0Qxw&uQqWlU_u zIu}j<$+m0n8p+dTN|Jy#jRR6yX-@#ceq>z5Vh)Zj(+ct7sY5TgoX4O=tc|}PROr} z;dAv)OyKt4Tq~n<3{0s;EeH`qw@qkq+#nwNe(V(NnX>P0c+`&{nGybiShzZFTff{Q z^uJT^Qh0i%By>P{OkYuNB%VOp>@wiMcx6+XA+)<^c2{^49TQICZ_j@*M0FSbbAXT6 zk4$nFD`h1Qi^fGVk`ZSrM}{y(CE6LDF6OwcjzP1o5NzN6f-A#dbNFX|5S@n5jM(9B zP@Vz+Apa?xI#`@g0A4icZC~_vdb({QJb`(lsRwT&x>S3+w5~e{0 zww3E7mzp2q9F!172&@Wj8m3x3K=kncw*Tblg%=xFQ<9<%Q@LS+jPMZGrxDqi^ zz)AbVLlW!O>a4Bw=-!pDx=9mDyD#J@4dLxry8t^X2kSAY5j_9uVd zITNoi8%zjZVU+Fa56R`gZFyS-M#p2A-&+K@38p!bQ5h2)vzpuFy3+;l zhm~0+Q}PeN=<`RGB#YYxCF23epGLEXBLxCvH9|ju&o#V3SsbQS?^Lwj4iw8OB=@C% z!l^*T8D~2aJB^mK=pgjQFMSXw#^hV@GT!sIPcy#J?aON?uz%I?dicWfSh%vEcp|j7 z|31nt=df{GB=45L+oj#T9KaBiBr6FUQ$87X9aEJx7oFemC)4}wx~xll0nCQS1Qi^) zD5M%Q=PT&RvlPdE-DW>FMxRfxPL1P$%TWp+?8_n*gy#XmW&XhbE1BCQWFS>Yt)1N51=w(% z-2&TAO^k2tJ0MKAlb*6TUf%E5r>|hJ7@C;(1|#TLVOrC*5c@MQx;fki9XEBfbhL+( zD<2G|!ZwRx$H|>Q@$s{w&Dy3dU{WAm*CBV|+&=^Rgxsx4yFnWRxjf}9AD;GwioLX@ z#ElMUo}s&X+m*h4$@d9p6nlfkXfrH|Yh@w_E-Hv9=n}3wKM;0oM*LHrrtP0; zZsn$LM-x5UCU8ZwEfsnpM_CJGaBkI+U<;k+fh1L&k7L>p-tIhWFVvRkK<(3U7BPbGr zjYS}-c%62&#JHVpMnNtyh7nIFBs@Jn#m`gYJku`gT^E~uBLI|4NR)E-(BOka2d^K| z5&|uy-_#(7vt7s%g+W+q8w8)R@qr@=-f}taPO74`+hG6;=Bmd~OtyL*kttaVG?+Rb zAD0!>4Q}4e89nDtTaX7yZ8J^K%b*FG8H0}3lAKCiFu9}tU_|>(Rkzk!QDFWqWr@Dc zW>2~J)q97l)$MR--qS+=qmJdX)4^mKy>c4w=N!f490uIPp2q9rg5&A_l1{1HqG}MkD4-_c0_@`2M6Hc05*~>oHOi;SCyycTj=e2 z9c=l3s2E{F^+ni_c_nl zl7qA59R0t7&k6V6uXF2>5S|~W(*-5H%NU_16X&|ys?j@1YXx+DZy35Zu=HL+26L@C zwPO~l+6v-}2WVh|2fG!<=L1vQ?=pYe-|+7-lyvLxx}5(0JAG+P2K_c`^2VgAaw=k@ zg9=W+HdX$u_0vcA*#s7@CDdU<*ExR;pHk^=h4x|iOfM{zYw$?76ZYmN;EYl(uJ5z) z;4vSC=MH;yesvQ>&Gb>?{2vu*-Ot7Cf5o|9VJN`*X^nEsDg$Tg(&}eAjNC{Objnm3jOu2@OZkSh4YxuGx!$g>*6KNPzez)@ z-~9ozyA)aT!C28TlRs>%Kkp-0c*wY-5c#k0(Yk;gz0YT!+q|smiF5brPrWp^XHN>* zy~(d!Gt^64s1JW#IGeO`WEOp`9Cu(0PtTs%ikddmm^X?n7E{OnZ>FwDy;wzCz@=?U zLkPpGS{BO#_s*7Gq(_KQE9gR{%0IA#%a51gwv(^3TK0T%#rJ% z{@TlaJjpCC6C%Q`C2`g(o4)DX!bdV9hn1YPzxl5yVb;k4cJ+C8-q7t9aMWTi=hLLjf>Bxf6=q?CNE{XoA$d-2|D-n#SUlp!-h+vghTh9bC0IJ$FD0xiBJRI z0CREr5o0{kEI&~42}(qNyhtaPfq<~@3?RWY$RdxOm+hDYqpnT^DUgr!C0dERfKU{ZfSfID+F1_mX^YPfbfHIS_WDW9O2p_6o21v( zuY^ChEI1^*7p})gOuwd`|E;d4SZk2Y)_i>IsrVosXOcEeG+MLK6LT{@(Nu`;aDiFo zl7`ce_`GNe+*e^+lh6Hb9%cSs>27^_*9znDW%zmazxqZpwM$zTirWcI&M%E3Ep)x* z=Je&xcjp=QzEqDc%u<4StYcCHEXS|g53uAU=+@@ha#70j;Pf#fFF$Z(HULXUo2%vg zLRWn(Y zzQHlI93c*aR->5jfWbvJT=aZE4fU-(;?pPpJ-}Y;r?SL7?^AM>WXcIE^%lvS!192{ zWQ-?S*m|fJhUw2p7{uTtoGq}iQq^g-yQ4tbwZDP1|6G88UX?gTy@Tv0{ z&R_Wk=aX5Hd7Upb&^r7F2JVXl=0_OlfU&}a9=s@m^)l7|)B@N2zzOb0^P&Jsj40(Y0gqO&3gfm^zzqYw#Cq&SPaz z_)JQWZl}(oB<9m=d2^(>Y_x%#pDH#j)KYndgU_>&UQ@@>-%_iLUdSG>kx2}}1qxkT zW9*nNXW41-u~fAjsm=h1Ky#{WXDTT`F*Y1(<%3c9E zU=Uw(3ZyU%&)39kG6X65WT3tHHlb)KCg_W*%bG|RDLxt z^e?QQYFy-3w#{H$fF&hpm;ek}vg`3H1fx$~1VADn@B|nn_pVB+IRqt;?)gYpf&m-^}7&$W+doefPo9Nlo%4?`AAl@e(I7ZIrAvU=La%|!FV zYq7eE4=?R^yvcnoqD7aW%|atJ+yXrw%pK68&ZCtt)A8~Cc5@?hwR|<4Vo0NVyRe+x zW@;`61yRp#9S0$XmZ&-*J_r*-i=?i`^vZUpsU|0}^i--ELEJZS>}q#n6j69HE7cv5 zpW21xAUI^F?@dW8P`2+Em<2lsX?7-kz*&ayN-%Rcf7hIDatbwY8p+IU_---068Is| zok3pj!`D%!xgb$#5cO-aCYO6d2#;adePsj75pI*{#mCit<^FvOyF$?)+%!q4{^o)g zrMzjs<7rosy?xQ#Q}sqRgiNta#YZ$hP^omsCe2Uf;~llcTg9ag_&(^ISs2zs;MhWI0Ls{#Y?7}2vwrUiuy268h1l!{JC{$!xCU6wyIJ4w5%6Cj zS{ptjZ!{HRZ+sNgZF_i|s1^3aBxkqX_}PRp{rVE!LT9D&x)ih-TiL%Mm-+Z&u(ViD*VV$lQTY|J_J>VD}+A>V=&oe3)=_04+vGm^cw`D0A z_BU$btL&E6=lkw}G%H)FiM+hvOKm>y?+M9de(QXv-hpuL*&1h*-ut}{KAY4B(ew0? zN^>=#BfsY+^RupXZD~U0>r~BG>y+U)z|m;Iz;8bLN?Xv`fIFRvtJ`G0cRX6rt3S(RAGS`d@svTcOVBNFEGa@n7nwDGv_4MoCF)^owj z!*+r-pvj7Qq&(Zf#C}9rQ60?StCwQ?sNdLlbP+Xe?`q3S`$1SZNdPfJ=FU6GQ&x?+ z)-JSl!@ywx%v)8qX5@}-rvlGX3H$|jG>a0R8ZmA*FumzW|Gut$bzH#tPZ~(QP;)R5 z?2t1GU}g$S&yX$}Zoa?bygH3hcBFrquOw8Fnd^M5jxrYO1tUi8G*~-5^wN7Yu(i6*2~35c}*$RF$&CBD6DnT zZB|+0u97BP3+nTO=O9LvWWQ*olL(7k!5Np5pUsXh1DLLpXq2xKaW9&zVcKR4B<6BE zw=3W*X;2V$yrfCBO=SAWa4v%QFmXJviGjj8y5B{1{MmZCKT+>|0Bm+!`~+2G5Z)eO z2WI8pk37m?qV3-SDq|VoK$B!R8yH2sBBOuNTv9`$7=MOM8qoe@1L>+%e7EqYPST?D z_=WI8AM23}9>o?K8+HY66}Or7MQ}GV%6Y+4v!vof-w3sYTfn*0}xl_V-#-TR>er%RvQM+3lARQnr}-lU5|sAfNwxQs_Fanb|cc| z_$eRQ1m{SW=KH&T*lJL;TSWESrnNvCj}MQ=rhF5g=UZlu3*^%lk`G3a5;>^pVnva- zrFtf1`2@DO6zhhM>qDsp3?U8;hi(eFORZQmu`@RHx@2ziuH9+Z9+mWhOvcn$9emRS zed)YFY}|V*9<``?06v7ZJrG}$hR-AR(cK}QU7N8Wp{Ng-jOXf9m@0n0Cv2Pwz@wD= zB^S5)sG)?fUs<^{0p8~M11gDrpYuMwcW-9qqjbx4z6GR;`48@D0gp{EY~{@{dWO`I zs!A!CzHRV~(>RAW43T(a#xM?#ijNB>VpQE_It0GM?XLGgR%1`8P(p1~ReAQs*7HWE z;w?OrdDf>UGPM<Sz@cv(FFT8`!(S`Oj~7E6=IwW;)L~cb3@|tfc@Hp=$t){ugFKsW*R1KX8sKQ z($c>{@DX&&VY;IfBF1fy`4M?(SSwZI-5f zAD?og9{q@I61m)MOw@_}TS|j$O=y{dA&OeqY1lucN+0Kp@(^mhD|mHYVDq^*O11?H z=K>_io`U7Ric8C_)9+oUTtEI(8msou2M1lPM=4ERmHS_}p-kcHTkgH-+5#VUQkfs! z@q=IbPEevHa>OUH$b`-x3eh~meOI!y9@&!K?y$2mk0blfyKGO24jnOEPyPt3wQ>_| z8!C{d++U%*#!t;fk}`FmUfw=QLOkogS(r!Te#ytjv&Q9rLuLTE12W(FJg%k^>Z+bL zj^r8TuKs2?I*gO5EmeuOxgQjmSGcgW{B*u0o1Wi{lVx%5Jm`t=-l)7YEbVHEMBB;G zTI|E`VcYb(t*kJd$G8#No2r*3o3fs%k|tBL)~Z*HXZw=r_TSz1S#8P><`5FTwkS$8 z-lI=2$pB?1>YU{n8jOyLomeZ=cPoZ=?F`SSUhjrX(LVn)S2ZqQPdw2>3Gis9CD zj)AJN-6HdGH3=#V*t2mPEtf|X(8X5f>`^$j$jEGs(rG1n0fNKzSv1Vph-r@p;(4eX2M$jU*6kvr^#w2Mlf*0t3^~6@`KhH*Df3HFrr0^NazV&1 z)QM>z@MbEdjK*5ZI#GC8mqMoTq!YdJ>RO4t34GaIbguPCTGxoH#ZoY4j{TJ$jP2>j zr_oB9R_0YGjGW8ZPxNG!=Q?FxFsEfg->Z?8RtA4l5&hYrY0!_iz`WJAPa2iX$5$!A zEN%HfHu&|`#hiP(lH>3j&*dYgF`?&jKGe=zgBHlqD)U<}syl)~^hS#uCP0K)lvU{W zbCyeU^>+JBiAII;G=;mV%!`q#>|Ug<`yU(DGx~+u$jU^H zcL8SI9m5LTy<{+KM5oBAJCfToq)@Nv3mAu6i&(|U#rM_FtS%KP8{np(mlZR%5AdmM7o0l*Id9R55!@9c6<+St!K3yekq z!Wp%79ulRU-~9INTi9ZDgUAmWJnV_9-&a-!OOsrAzDQ zEGJfd?H9(;0KY>|CF3$1N=B?YcBiU2N57A#EW-D1{oR}4(JQer8+j(2`W#C;e8c$T zL_)PkYrL6SjZ@l!PIXIyH&)(4}&`-iCEu>{qw)zYJ>9_DfnA&gKhC~8fHL`V7T8WsHBr7GMmHh#H zFh9K9^5~PU!-ZN(x^Y5*jX12QNc**;BFhsrkBEE$)v%GHmHjECb44$Dt0z*3;Gybd z)>dx~-hbt~5H`}M*s810xACA-P>AB! zO{u|TE_pJuzxBCIu-0~}H#yN&nJI&I85DdLuM}0iZe|w#(@8=1-`tDFYFJ&Gj=M79 z|JOP*AGX!{Qp-(3o{a5tm7aGI??&o-Sj~hEbqpjLN zI&hCnV%^7Vd%i*F#Gx<3z-@c?ljefyKqA9Rmtyo&Bc_l28te>Ubh>{nX6nH~166?KD(a^3CQ>yZ8{JmC$p4-xEzSq9DHGt2- z`rSSjIVp!D!mcU|BJ#P*3`DMFfe5wT3~G-YW(dXPqW7D$eLDJ;?*hKHj&Iy@Lx1rAs-#-<<#BNij!vx z8pH5`11%S9u4MVf_kW|Yf<#+)g?l$=7ziX@%9FAGgT~}G8BX_BRA7QHx(sMv`x(&; z3WIb+zao>C4M?8gU=D}}IiA+MC6@@vQrf5DA;t+$tIIf@KJzdo(U4mFo2K#!to#;-76VS;qnx- zvb3)1fOhDYyLc%YwV6U;lj&F3xI`5H#3m6*eQsiMx&cG@F0fT9p3qXqFo-F#)AeN6 zABVmz3|prni0TY|p?PC4;l7CxN&t-c+ayf{&^om>Sk>P zT|}sgZf|VmrWLO*u#Hm+n5H5t2%~B4Zq^nwB^W)E-d=|@xlItdR^cUCsw=hY%ugkT zT!6Unu*>3~wmnVxq9bTX&lKB$DuxwU4k^y_gDR+nRY!6>RQDZN7p-;cw)n=mq%?J1 zpj$tr914}hZUZ_X{WV3oph!|6NV!(8{$eSX)q*MgLZRh+Js zAT)&XOoX-gt_nU-Y1{JF7ZO1k59q4^%O0pe7`M@H=hH+M2pXna?rZrceLkv;0n^$B z5@zF6du!LGahGb|D)(9+X94y4v@Zr5)D5EyKj2LG@?|cs32ps>Fpftys&zUP28Dq!|j>#)gkLS-LoNitLkT#o1j6iF=PFtc8~ z9RcP^Is&$LCfk3bB+EXU!os>MS$4L$I6IEFjRa#VB&%NXOQW~=W0z$NbQ$`kRY=K~9K4b&Rv6DAK!+d--}u8~&Z z*ZW=z-!IH-)gmsNV7#Diw=iqN@BUO19}d#0=l`1H&G020m;y}6b;Q+REcRNPdnP`^ z2~eEoPrcqys4sVCR7-7G0me_0Xhz$e5ut?hkfVDn!4}L!%rvoQwk89z%QoWv=rMr5 z*EcHujn490iy^kl zQ{VUM)&`N`aHE@|~mNS549UfG~5Q5}we$57lt zJpPh_>+!eaF*^_DMf>S_0fP={(seg(&zJ1QcKgk;%d>Vf)Z4K*%M=D3C;jMoge{1$L9p$hG~&J`M)P>@g#8} zOA}ZR;xx)o;cTpAdRG!eLA~Dd(I!2lxSPpxWqMfG++$MX%RHU=p1IuSq~9Ni3Y#?u z2VX8#r<-A4RI#~vEM1ZP06f*;>n=xvAtL(6tNmZDJO11(-kPC?GFnX)G0NF|Mv+8z zV%q&YS8ca(ovQJ&61tQ7{k3uF>7*w!11DCgKy}Nc^FNK9QeT!J<|_N45Eie8YL?1d z7?Nk(%{s}fZ?~3>?N$M5@;>Q`l&EWp{f!{2ReC#0VOnR@-OLa(<+&+9r`q!fYr+up z4FTSGk&~R@HfrvPDasapt>X!3c6Rn=6V|P1-!kAj@fR+`IC<*}_fq!xAHQSON6-TG za$0Pw`|nr&_Ho=#?R9Z&PL+IhmN{}c-Q&82!f#z(!uvW?H6fjHeiQks^0Lg2JgCRf z7FjY4yv-%IUF?nVmA5P|4Lwk=jd(=mq9h)4MqBSV{Mar8_iI}Esp0*86>7}z8eArx z)gan{M=h&!XB)?0MgJSh*+p~wg$tPhKxFq3$4sg{09T$P_7e(TtolXD!5q-Zb7TX9;P~DGFT5PFbwM@Gm6q@K0oVT&<@;~Rr>-fSfk41SjWT=u} zf^Dzu*!0Hu_$#;(mJ_?P8_ZLOjLHwve0Y+_>6ergO zB}{S1=0?8oNRE5^cO|64TOBbD4uwX65yBgFZd3m$hR%Z+{<9=BF*U|DFaI)o+mwP% zz2D!!8lDBlBw_66vknTMPLzJvEB&ALQczcT+yvVX-vPVljK!6{LoD1X@&T-EH446Xr%PL7I zY3!m00Nk})?Sy{9^q>De#4v`Nbgkw=BA}P`pYN}fy~Y_do@u9sNtQ_j>SrmH>(##T-@?tF+h%yBqili>b! z)c1CS;~M~e|4%ay5PjaCXG%Lw%1AqePSz7YlriL9v4aGMu}j^l!>Pivd#kMHzN~y% zxj}i|t7+=(EYr(|Db2c_6eVL(H4>KS)Y?zmmd-Gjn@0HSo&r|?iL*;D7NuN@zWgyh zYU`Y?p|?3ps96oh490}iTQ8ULu)WDRdLZD!3fF#t0Ou-O@+sAYk<#Hzw#uZ){P?^r zDo{|zf`c6+RE3%2-ARUFY|{DdN#7VUJ?+><{Mf(Vs4unB)*ZLzxIaWDaXuqCM##S% z8>hT*mq|Z+5F0JLDh}h#K`oFYELNmb^~rm{s(LQo`s};uY_2SAT0}2L%eNCXN{rCd zuXm+F+q>lim(ed2%MO4FfWvv|H0b4weRY zffBb;#kInv_8>%=ATjNeScqIN2x88mU#C+YgY_y!`*r9W`t4h*>UlCdK5ISLc+{i^T@4sAfx=>%T|F!rbECDHu;;M^0Jj(yT6-FMHiJgP9b z5F|`(M$n(1l8;K|G`P7{B7HmGYhQ`^Q|Gk`gGLY>4+m@0i3yzgdL_S7P?ZL%ve`>9 z_l!>y#O>~EM~(v3+sg_2Kri)#r>wlauz$UqUhQR0WpaBP3qCqcN)7w;cASEPIm!BM z()Gn3c!b~kJ~wkX=RVHPaAgALW{?E+&o#No7f|9Mvf^0tcdE=E?flH$gQ*{`n;4 zw-^3!{q7M~x*2oxHf$hCQNm%{-1~UVmA)_h-!0+<^NeDXkJ(Zq7{?GD*HojIe*{Rf z_(w!KFV%fc{f{iTcS=laiGcbB1E;?^uo|3-2rmu4t!#NbQ1qlS5v)UyH?5j}Y$0DNECPF| znu0hU($vls*y&_-WE2D!YB(C>i0+AZuUO2^!RfpH-qSWLv{UvsRM^Rz%MJkGLbnBSY4+lf`gZHvcnPI^+p zdWuJIE0U$_=D(_Id2+#vJXa{-yg;W9rTFh`U>j5Hdtd4Th#Xz#c3t=k&%`%|#_bR5 zO7V>QOR<PT+azz1R?&$60_fSTN-PBE;zbubnp*CkYf9Sn<@lm!@v(&>?O{$4$@N z)%G5;S?4X|M;u-UU_8NjZs**u6e~0Ex56CPZYneRMyhwONY~wAj&I`isE?qT_e-3* z(q{1_(V$?Ihp$LXkTzP!6m`Mich$~L@VQbvVC z1?e;WFxl6Wh{@DDF8i zM=xcEgYT5MMuBkWIINeI_PeTBo|yHu>H$)NH`9pk4*wYPkn_3Vq&+hv*NUUg8sZ`*j5wiAei`!>w&ejH(5bL z2ZmVU^tuYY)<(dkIh(22B!gd)u9WtKRcZ&JO_8>_0`3hu@Ik(j->M8-{|Yph$wBZ+#H{( zDBSYBq9k9_Ba(Xc$Ec4%w2h&p0=sXA@dNsq3XxcC5~3OeRm*9*S1Q+*a_w*Pu1?kq zSYCzGni|$n=Csb1Sp^ou0$udhtF?K%Qn?Y`eb|_EFd>65wt^+?*^6~boBx{bE(vpD zwZuSCH39DP%yh&j;yL?e_lPrntC{LJr@fhvJN!kzKJRFZl*ftOkI7-;k-w^U+EW7Z z_6lr&U{Ie;z&?vJx~*II#@!Xtw&eTGJ*#apHh_8d--?ssX_4DfL!UMtAHT70#}mjd z$hyR9^&^6?ktJHluJ@yw)S>d!Rf=i9t2=xrOTdH*sM{$ssrOuwnvjl#tM*2P^H#oV87`xXwno9JtvSMFhW5yhwa^3%D35!GHz7Ibx6ny zR>qSO(_nnq0su1p*Eb2k2GnQm-V_UvoUHSrQaiG%>dt^zc5_hGN7VkuKp&61t=t<$ zDv#(=dB$T8fey3pequbK;!mYX)nLfc0sY!yaapRV3ac;GAFE55#4i@9&$s90QCH_5 z$>5qu$PQ~EOp7`ABi{lqPIa%qLD(Nww(M0T0&IcBvkVy87$`?RTE#~*`2MR=7CVa- zy)uTijbuony8=E`r?aVC1|0VDt{Zs*-H$0v?SMKeYa=l?4X#v5@eiAa4O?KuCC)FI z*F5v+Bf9fwypk!P9f*~5GB#ut1qc<`%vUeXD-l5VXSn+R=z6QTsQx!>cW9In3F(xQ z8oEnL>5v%80frKg?(PPW8af4$?#`jRq(i#9CEV+`|9ih@?AvT*3mRe2D<_s_c2ttPxTE{T#Wnjpm%_#cYmd%AuB=P zqPLq5xXUsGlnJ-FhohO6dKXQG>W-?Kjw(1GopW_;k)E{kklfR(@RiJ31|h?BYoY4R z=s`q#TZ8a5_Vfm&zlD|V;O&8i=pqFyCU!zvtX*K}USu&8Va0GWGHLaqAEcdOeEzHp z`-tbTJ7%~Kl1_v$6#ispP}1?KQ8*w6T{d()*}jHxm&vYp9RHrX%B=FA6e#I={R6NK ze_@A~{AJ+Q|Ua9rOtS4$`GW$W#Tswk6{-5s?pCLaC`BPx&;;(tfSvw#$dJb@B=a^ojPhSjAaOM>tmUt^2iX9I?{fqOHeV_g^tjN}k2iwL( z=G9Xo=5@D#=*L86x6K>yl6Uac!YO(4eqrY@MR`GqR!L!@6o3?;u#S^>ZX|xWZUpsbd{TO5%~fE_=#3k5zV0cCm=30K*jW~G_sH@&#gNiOukrv|A&lQ5SFX1 zA@XJJmFUyVwD7g0^pL!gbkIg&*KWJuk9D%PfGGw=Yj4*Y>6;bD(*wR5mwvy%J{QL{ zPNpa(sT1Y6ko<*Y0zmyKCZ33Dym;3Vnh}=~+LeWp=hkKCX$+q}XTy`Ys@#bZR9Axg zQgc*1gfxj(|4QeDIK;+CT={i8Nr;7R_P55dc!dV<&yWnPG<%fTe0;%=6?iU6f{x9s zO-`y-GZcXMB!o*XY@m$cQ!^y}&;2JAmrKv!YpVCf)b7r_-#RyB$IcTv>*oRLqjzAs1RhyIDWdo9~d1IDv--E?L`N#au{w`@VDh1)AzQwd>M zqqu(KG20_KU8Fqsco{t-a8m&nqi=Il9-0Zat*1r7miY7}73a+aPek5RF4ySIX%!5^ z2InNwx*njypeKqVpj0*)PAXR1QlV$pnYAmj$Tbkk{<=j-og~Tw-pnc7MZ7+J`@bx) z{J^8+-Lo<<0(!Z2^5Tz4EjseDx_Ld08wnVGt67MP`JW#1B;!zn3xc zK9^njG!%}HLMX~>(u3*pG!n>h69ra-=Hp^xkwgCsJTHAamJNW!**c?D%?W^zkil>s zTz{GW4vnP>I?*(I&2``wrB+NnV@K0Z;T9b@87F=};N#NOpMVebZ$e+PGgZpkLj$)S zevXT@rog?Nz8QbA|0nvmG?rg38&b0q;6lX9?HXfYR!$5~O7d0_WQ)_0B)XpQp6N~19M<*4T2Z=x?><&0D|4|WCf9I1GF{$3{kHgYD`<07{R zpTk&Ar|^#e%U!^$|4;$3RLoiYM&7++C(a@4`|PEEt*2QW6mNE-x4;((t#PbOk}uW| z4!e*zjK!rSJVAJwL{6O$^rE4Yz$J{&Q#=YyopVk5bG}z9*9n!m z1E|LY2e}wBh98P4Dqm$nrv3pm!|&_N_G1f*$Qu-K?rL#VVps0HL^^xU=;=CKby5bd z-LQh7yJBE3U8?&>gMiZ`TJ8>rbszjLVLGMMoTNbkcy{G83d#r(_cQ4J(%f~PD>}0n zYv(so3pa;}E-0mnQYj_a97g{R=yGHHZ@X5x$Ms0{5o4#hiaL}|#)^wK_Vtz7(ef7j zklzDKfS=TWkLP`_kW~#+MDK6j*XKuR4uYR6SLWh&hasXZJJbkA*OiTVyQtl;`|6A( zmna0^XWY-2dvInfnuGv^)x6z5;iw=^!GD3!U)}N4=DWVn(NEUvcfO5-2w%|_VxnZy z|0ZbIJ?$GxOkgIs;l+RJ#yFs%eVH@4?LA&ER_%4zo_jL_LTO9ag(Nx)w-R`;8Bw(k)J6CUCd8E-oO z6eSoImY?B(ohgZM378H%;fPZ{i8W|6T3Nfj(urt>T#e+8ODl)=ISRhmdYj1 z7@hg;BrUseoRxyNJ~QF)rggp6 zUAw!~rrXAYQJHv-tA{EH*XFb*YtT=AE49^8EdN4iLP>nU`YR|XsjiJWYcer7DfLHJ zlUPfL!{=D-WD45WH)#}B54gEXR1jZ%J>TXGeG(ulQXX<>@hK}pex+8(4eN5cmj1$^ z!M#tL(Up4R!Q&=-VX7MZFgiK4lVPYgM{?w}5{1^)EzDjTf#NZe&hh5enL_Ld)z7{Efo|aC$8&FV{eQq!cTc56PmUDlI zy=tY0&JMx=i#nzRHrp1$LLB?)p0lgmzrHW@_rep#l}-tDeXsd8KWllFHdH)87Fur^ zfd*qwtDQh=d=)jN8x?fhIEj!y96K+!FVP)_bN9y%eyA)BX)SviWYhwp*Ee@;7owV8q>Cs;4$(=FFrKb}AAS_@v7vCf+ z@&4lLO3VJ&unDiz)*>@m(a;ul%8O{ny`_o@+|f{ZS9nyNsNY>z*cbWLHvQY2No<@K z(p$ySEx4Y3WH90+$ItaIEpjP3Uq%u016)KrV&I{7qKRvmj3Pz~zMUqE9Cd6TrgX0^ z&@|>P-edpQ@884c6~V~EW~)S2MdERd@yFhEHWipSHR(=!8E%qaQEM^-N1mYb0PgZ)fg$JYR7Aa&_`j6|P8_r6xVbLEiM-Q{Vq>^2M zGJcfE*iY939bZ&iz2;8zsN?$`18-G~P8yGK+s1A!Zg=%*qAg5d(T6=@jj<8MEEe!; zhK{ZDT3zfxbagfjbFmlPY-$Bc3+jZ{gi~g1RO4nBt*q_xuEkqfM-%(1obT_{pk)X{ zx0N5{1@Vlh$UHW~5BEnh`(jV2~|;g5AIx5&E+u7ME2L|tLOvW`{6 z87Q6DMJSSoBaVuhcAwkZ*-j+1W(}jF$#;G}nCr|s_zwN@mH{01Q8rsV^1V?T$Q+#I zldp1Y)HBqTP|R>l06GVnlsVl8gmi`r>)d`Gm`p_55{3C-rZ_)_gnGZ!s)kp59VjJH z0@eGnIi)7@s!r_p$-KLxSw$U4qztcjlIz>}RMKh4?7~A#27hmtpU;d2+@Ow|nkPj& z1|y*)yHs7O1+r%b$RjS3-a5~0P=CLWbFt+}n?!@d4`_%CEFTqt?i<8(j!E=-y2Z@j;o53@0Ahg9Vpopnb1 zc=7T(_wD~aC()$s+cDVftKg7(96U1Flz0b$YM@n zr1g*iwqGzQ`tg9g-~k>=v?T|P4u;#~pZ7l$)76PeFLDx8g&If?KsIx?)0 zko>c!k&UW;gR7z0J2ElPcjz$;m+jaKG>QS$2Pu)A?^MX%YM*Sp3Jd*~)jdw>JB!yp zBI^wA9dz^y<#*8N{-SYYUKQ0pf&=jkD5avBu=DpNJkVM52_Dcuy7PC)=^8Q>|H;q3 zWLbd+i3J8#>pk@1jspp}`|%w6PSo1Zbi+@B827ZFx8l6@JmUSbf_B%N+{LqMRE~xJ zE{jTROM0krB;sucvu(@WtH&lXZ7baCK{QViB~AlwyL{Xww2fK?)Yy|UX+OUsR1q5x zAkV0^TQXuL+e=-CzH3W*gRq7>ou5ztsr|a3*Dxnr4>Wqe2+vfwN$}kx`jL``h7?)h z)_a92X}?P1NiQ~^bUloVMq=HsV0V~Sf@Q4(C?2`^`C(G@QqmD@+aZpU#Z}p2)~_vj z)gO7~2F_Q3W_-+-J)8xMLpwX1&FQXg%hGvYvaL9IId8Jcw2W1W@im~kQHNc<`ey0r z;{1E;U_0R9o2rzOt}}u51k_qhkQloI{O!71l*tK#6s^U9sl6%0Y;FCi#=|v3RPx>_ zzwM%>!KlTCZ=arySa&w+lFsy*E-8rI|@?_Ap1{zV*3Dwa_At z`_3u)5cv3v*P*lE_wR5`kIMW|CGFCDblR;G83sZxX&rr>7R=avvB_i!QNKlagpN#( zs{3JOkQ%S{`EYfh5Uzj1-F{^R@vDSe4B$X9)3qF9Zo((ultR60h9oHdVu*2@1W(ft zCzxu4OVhOrh4cHL)>)2wpsz? z2g+@2ZYx&))5tplUy^TYwGIh=q$1{}JK4&qd%2s^!8eGl|B^peD(7guPVIAx2 z!5LPhWP_}W0uicg1vVrW&~E0u#9)XB?6tz>KjBql*4Hl4=(-nlxB4U+x;NCJV|44g zYbv= zs=@{G&A*^4K5B&nj((bZTTzeksuxjg_q#&wR9R8Gn?n1)FaFOp6ZhFDk$U@shemlK z=eCl^|9{DNtv}HWKXj{*#Q8HLDHVg>mgt~vhvcDyaE?UOknqCcah*d^csMP#YHT9E z0=|-dmnQb_tQ2b#A!Af(tDKZSX=_-UUS+=N{A8IZK9qfHy50Ng9ZtG!?{L3tzR9+% z(fnYU-`cY_nicPg0fJzgH6f9hm=tOUdj`kpdfOHkBwMPs4|FJ z>{Cl&4vQ4o8h&RS-mtH<>sQBiwz~)JyilB4$1dcx`^?h3&MsoCt z%L1L1g4{p>eOeve%UqaWiM8s_n+>W0or7l$VGVS9vml;DWSBB8m4vHz1OPOsh~ zGaHP*4IE^!%}Z@?q%@SoKw0U!eAOq0B<-3F0KeZ3C!HST<4H`d4WN{g#P?k8w4An< z&^sn)Vl>T5 zNYdt|&~6sWSoQV$!7JoPA*F1W;9hmXj6MPS)b2j*WOd^X{xNna2=ieGYp205>gA?5 z=&R>4$mKv$j+-=d$qsIXEq5Y_p$j@fIQF%-j;Dq^%&}(~Vo~#$NMun9cxUoqj+|@q zdz%^qr%GRHFJ9RFdTPkM!{g=8wdXqf*0UzR5sqC}=-tJ%YJyk{z%41I;B9}<9to2t z>Gt=Zd>uOWWUa2Gchzp}iL~Esh_EqUzjEqUmkhjOJ7(^bY$qYgog=`-stLqbWDPpo z5^J@nw!V5c{>(@*N49A%XdCuaZ=t|%`NE?`6^^8MWtrbSRh1HF(}wXTd$PW@wWbu_ zEdLjn2KWjun4QQzjhv+}^T@X&Fjd;Kv3++#22FhnQy4S_46biG4BH!?+P6P*?TSDL zzRC`>+pSj0@(am4Si^Qm@`v8;WN=w?a3f{-x6AF`gdz9 zK}DPAePIYDQ*_pwWYW?~^HuFIu;%UHVy!=4Y;t{wg9?el$)a_|@7hN5kwyF+cxpoL z%eQP;nE<9qcz4w9U;87Y?S-mpd~e*pzq=jIQY5pOJwfcaC7ra2DZlSW($oH7gl3Vt z)>&{dX7HYujEkdU6X`D|6HEWvidj?2&UD`R_%Ws9RmrM{t3RI8PBQ;(w=x$FNe8>3 z;*v$F88Z{y*c*k#b*+-EXXDdJmsb^-J@Is>w?$&x${_+ppH!_uH^tZ`;523* zt$q7U0MS&nGCj5BD>|2!WF5%uJa=E;crv$?Hz4~GY_kAA;BGORwVP)q_YG+^3GuqA zgWSG09n0YPZ(MBb(D8Jzv=s>pWpj`kGhyTzEKzR@eKx0p%e_#OH&sEAwhIAkUrOsb zNHW`nuYg}ze2w=oC03Y(8w-a9n$a?lqd3H%6DZXH}aJ=V%R1(cPxG~o3 zY>N#fVt2-qvRUbqO#HAqxW>q->VHedS((D(I7ZfV)Q3Sm^sb+cvq*5mlqlt@*IVU?yu zYz|LYxy-aMq~I$(;G~vjYtH1c81ubdZ-T_&)f_i%V@1)|lihBP<&E3A0!2&T+QbFjNYv?c4w%SG;%c*5=?7+tER2WM&LcSiGLDz88wH}gkr>%r zZ-xoYg}WsG;>@9qk8DV?t=uK2etC@^l_SBVjSe~guQ-NGJ9X0$mDhuYOX8gey`ON@*9DYR%xAl z<^h~&l}Laf$O_V&6(cMeXs}|+OwP^24nkpsFlT|GIkchRn2-^?parvh5W8x@Xl$VF z`~+qiOJSAZq&F+48U#y;`AkTb!?QW@4Lvth}LzxD^nzT z-Z=~*{T)9Ks9`Du&+{dNhaPvcgZkP4N8QEm-Omj2SeUz1XH&h32}=*i2D&fCp4^Qz zubAH^h6tT;OAuVSvPdl1$Kt>GFg%1Wri6)x=W}LMiJbkQm)aei{|N2Gm&xKnDww?9 zq#O2v-BF?q@>yOkj(+?@ghq0+ROYBwsn4Jn*WuJwFgDRB^=Ngm(jhUsEBI^$QG zZyX(qMDyiQO}b*8cTkQDc!U5E1#@C)Wt}`};(0pn$D{V^VnN%#5SnR6a5Ledqviq& z3VO7}H`m{6GifP8NzgXEkin@GHrJ!86-ekcJ2^&_4tMyTDl7g!Z@t|Lis*x5 zvHy$OXqqEFBOkCu#)iIO2oNIncmOEUSK&B<@`;o;BUg_|*t~zS*Aj?>DaM?_ zhtIh7xjDz}Ja2kx9L;}0pO^3VVJx31yOmZ75r7x7n%Pnk<-T9(rKZN#O@fO8y3lI8 z7>d~YD6I4gO`ejpx=+RW21@Ee+K+{KROhVkNz72Vlmkvt!)wEX+5~ptNa@|Q;*Lsg z(8v)r@XJVA&)L^BVakGH_ON389t**xRh+_xo_r?kc)N0hz}hRpm<{py+M2=ub#&kS z9(UCq1cLsw(-kMUF)letF`Y_Cfik4iZg2aI(X6PNc(tGi`*T)N8 z|HCB^9@oZul8rT}*$vGHp-iKQrq3eHu@;qrC*{to%0n*lZgD`eR0#?rf&0ZBx2s+s z8O9ohc_uf_d1Gq#gyu>#Rh0{h{j^U9v0!GQR!loFJDjMhaluk>K283uFm67Ntf)!$ za2Ia>cQ6v$vp0_1XZ_O5${jKJM7o8dSv3{P@4P^Mbav?8-Vsm zYrBMw`{B!6c^Nh^Cv<6caRqd4S8g@tqrB0)k#uH&R-SyW!tb-y@A4GqW1*gN=sp(| z-oh`l=;qwU@#dtzDBk#)vc)~gO^kmv>AJ@TYx!-HeL^Nc&>h<*o1|H{?1j-1cH}xA z9$)yzG`=ZK++!!>=477uMhYFkF&$RkOkH}MQZyMenv;U%x<|ge1VUb0hPyv(YKC9Y z=fIrv_-9k zibwkTjytRx3#euhFpUES+@0Z#URJY>;Nq!rY0u8ihxL_-rTjZZ!&eaSSP`ZdwI>k+VuuwN0 z!rL*(W+=JXNNpRN_07n%FM6OuR6~CcE(}udTi{o8%9FscOt<`LHN|m5n@J*J>i9oN z(ZOIgG_K_u_vZ~~QZRpKXDE4#D_Yy9t@HfR0VHPt8^btlwuO{Z`JY9Qak@w~g+URR zK6R`7w>@7sfNQPGEg)h0P(t?vpm3CzQD&QqIDZ{?N3)rLK#VdTN&TtXZCQ=Q`0TyR z(1`ZcW?lShE<2eCJauivPkMky-skbW|8RnFq?;%F_lUb2Y#zU#qfs$rPfEHd%k@{D zvnSIGM(5_^{iTZ(DGck$W({@N&KC%=-|eMvKxnOJ?IMMCf<{ zCgplBDxo1oZ!jcs(fd$XZN;xQS8VB`RPyo73r&%9n}ibm^VMUt>p0%yahNUFE0Hg> zA#wQ7XYtWrWX?L)p6jbEzI-=Ws30c5M=a05KyWX zk+B;BnUIs9Vatn7WXmiTwjwJL08H+NHFr#DuR^kWGjilWFh;)1Pd6MmbjHl!aFt3? zwLrWT=7e{l$dn$>axr{`%^iH{?Y^PBlJV=j@hkVuh`pYko?`%~aUu$1amEr)B*ThN z#oxbjwc0N6XPOo=4HI_-C$T{^PIchLUw;3pc0FF~sqn8xBl&wBAD`bh+~*ST;*E6L zJZgj0oE(CkBkB?yWr7;ugGutMmgr(+Mf0PLi>RjJCmX={+uiBi<|8_?A_YPpX$0XY zdCVh=WA%R)p=QYm2(e?f-?<)@86lou2o(AM-Y8|Ul`$^BtRSTC!`In~EVj75_H^R* z7pICj1t8UP8(^z)By=xDb3Oj#zdgvla8Q& zOfOc54JMry|MQp9q>Iy5Y&z=BL6Veitx4MA>6z38Hsc6*o0?AoS|=S~F}T*hNFsP; zX|FnJgn%Xf<>bZo~ zTVNi(n0JyN{bHD=`^tg(q#x6ASEXyi;BzR;2RWHZJ{yJJnlq(D(!Ws;HF6uFhjtU* z>#wuV5V#^r0k5a^*~%HHJo*9y?F$)vIwd_5u`<7Qa}{e}&Y`LWlAaq@LIcW+a-~(g zq9gddtX`>z^ov0>s{lxhZB*3yd+#g| z4I5nI`m}bW+Np?KH_;MYRnyBB`V!e>Y(Oerf`JRLYZ|6NtqF2~@KmpwYbHh*3B*4a zKt)@Tg#TjJa0N+cxBEB;Q4#+E2?FBbD+GXmDoDQ)JR7!qhjp1YoWeP`k4!8v>3?;) z^0^G;tgzAAlPI$xy|ej)ZlSHX@jsJec`GAFNpZ1Ky(T3+_CU3N_&$zy4v#0daX0D{ zXml(q4x8H#E!o%Cfds5;N4EpVBWqdZdnv%GZGVV_PX61J6ODLx-9pMMBo_2tz2 z;1ycMQU4eXXIK;J!9>Bb@L@o!lEiq}De@SE7(E*DW-{AVH%`JH}eT3=jTHri5Zk`-!AXr7qy zrMN#fG2IR#V2l7}K65q0g13qR4Gc+rrP;Xdh6JxxZiQ=yJ+?x3MvR1+-C&aq(bf4A zFZx@FOg?2@pomWLZys*0wgkoFV`6a}gq|Eh)tmWQS_vW%aYihoQFU*iWYx!};~9x7hqLPWB5q;~Ira^}x87^_l#19my6F zwpy@Qk{`T0Y9JJ>J+9*5N>}Koo)1r1Z-qakas^A^B+>>$I4BqVs2)_GY{iAx+TUr* zn?-XpJPjAQ{} zU)kttWb9w4lXH7p;Fd$QT>mJtFr=Uue?peUutD5+Trv=5X3OzNUM`DP<4tti@G>rb z32gB63!9D;6!dcA+#obE#F!H0t2bnV(44PTdENN_T?TWujj>Co<7 z;e2AYE7&>@Vu_z_gfQ$lPeHh+s+Ck)xBJ|7a*4q+k2fT>9Ix;+~&T)UG2}pOrP=?N^-u{>Ke27g;-&?r~Q)oef;a}Nn*Lz zjTlk&O-wQ*)^(4r32O!d4dc<>XX(2p?~tMWGuR5te&%v=drg-ka63B0?1Ou8!3g-N z)*|%yS$me*5pMCa-)gnCDb2Q;r%0LnKZ)u=aHKm(dEsp=Tw=DfFVaL>eM~o6h{~d;Q3ptvl$WE{Lrb^^XvE6u~ghTlmM&j zCCa2OvC9w+^z5*XYh4h87!i^fbym*<*b|J?pG9@e>-I@Bg=``yd+$8Mzuz-}w@@;f z>-oX&*di}og4d@3T{>!UC0f_ga(~TNggK-|d|PKw^r3!+GTw~9=_WPqt%!dYamJz^ zyjupq<`J{&y;TmwLzP7eRml{#64!-i;NwsV^ExC;0M=2qBg9Oacd32@hXbFr2~HdY zDvQ7GvOgvJ1|rQ?FvbZSi1MC!BK1f5Oy3MUp-Qbk5Bs8pLy01N2-Xk#AU{$Gf?VhS@&6iN@?oYeWX6tK2wQ+NY zOhgZ)&v_lKE7a&-R>Z9ULk+$^md2+zZr8*>A~(+2{W|wQgsBqJS(fFuGTK;VZ0rD@ z#Ie@KF-;!nEjSA%nOj|jn-wj>MefDOP8O~CW6u19Fm@Gl>?wU}a?96?zaq5blUr=K zn&{0FD-ZI;){;y zdz+8TC^y+c5XK0wn1$!~*s&)aW%xC%La@inMR@P3?o->XD|A~{^k1rN*B3!s_%4i= zZoAM{jV@LtLJ%WzrBy&1Q&S19UnW;GxeO^cHGJ@M*jiJLYVnxURu|=h10Y)chaJvRy z1mi`QJoPYBHoCv@;z-jXIGR*I{iJ=@$Z$ZbM%&Qirs<7AoNHLdF4a`zJ?*5^BUjCPDSg%yKA0mJ}%p^rZ?8UV-o)YW!ha2c7&;Y9*ez?vLg6LR0WDg zoc9*5(_vvLxXurhZbPjhidVNA+a(2xw^@4HR#r<}C3f%j?um4aehhz6;^0(TN;x2V zH`H4`no%R^c}vZF`G!5-q!$Hr3GK!k;F~jI`q%dOvg!Cnq`-8y{9+(KZZNLya8Pqx zlvT|0Hi2uCNwGrwi7$Jp-Zten!({vM`TLJg&xyfAcr%R_>ha&XympYW*tg&3k`hbusX*2ncmsE`p#Sw4v$8 z8|cOW-TXo(J;iR$H~?5@Rtlsh%!$@wUGxwwgn$V z_B@slJW<(lI_DnuVDI}_l_h$=#iQ{<0je(!R0FSf&zJr^lpWTge>KkbJb)DI)rDe{ z@lLVqU=i^G_(w)Vz57vJ)S?0#M@xw(7t5phL5*ryWz8z3+wyjnX@!B~pqV4^)Hi&# zzWKvplNe~}?f*Jaqlr2?$%Vz+u4c!DJq36IWdN%TFk7az!p0co(~F2)v9*Ktx?FhnUDPr z*Q(~B7uTL|0P*asJ?;n${{A~)Zmtb^Y~T2j9rwM`P;a#K`)Rm0oI**|n+{GOE{V2% zHb?*O7wgReGWlmzwqHS&GMKEYTMNuMJd%Wf>DH($GVWnREixX@*GYv6^xUIE`evud zEWHis-Pew5*V}T=$K)BO;yN>(%v=7yN5m{Fq|A>xeBI`inCiCRo zpY!`KYv5<^FlLxkag`uQ79gfY;=|)j45S+68{_phF1kOv(*T$PX-AH_)8yHDZyn_i zvjhmlSbMaN6wg%OZ&8K?W88Ni)bhhuL5KEP;=rHm-KuqicV8$qqWt=2VTtNmenYVi<%a`KiGVcat2SbV8C$im!Xw9;uMZ=U+VQ))b zGRFJ*4lmkWq@hDouN8GRHZhpUC6zdtQalGXAbEa(R!%2sPCG#V%69U*4DN^e-+J~UL zGrewgJx0IiAqaRl!{TWCDT#ytsAPupC#zLny#gzQttBm|96SCaZ|4M!N+T(oA1_!F zXbSb}sENSQBwR+@gP%*Zk8e=0nV6Jz0gpw^cstFSFFEr9mH~?G`u0#;(P{6vwOj@& zwvfSGxdo&=Rh>;Ml)Q>YfDxU!Fl~V;_NPMn^)Bv!abWd>!O6z6lDCeJw~>NMk{%kOjo(oW3nAs#@UbC%zA z)=clY2|_nmlD_JBss;YmX@037Va!*%70`lieiv&@ZEs^Dc8nMW>{k&2}A6 z^|4?Ipi@JJp6NX`=;>Fl)j;WT&LU+*BNG+vLPp7wpL(L!Lu#=^>i_J@R8IqY4q*vdQ_JitWeCaS6GltSK1-f33Fu z`_W;_ZP)iv+owfpaATH1AZdMus9Cka~Tt&90f>M1p79ZP!==Em=f0;avts?w09a**2Et+YymUzC)|73sv^K(ZQa=O`Y%KmkgY5z|I-^F~><#_c9EsjLn zGIv|WOib}fY6D|MnPH2Zc7+kc(h863A#p1!+3J{~@L`@3|F2EGqHK}DPAQaY?~ARE z)%KI`Atx76qWAf`AD*n18><$(P8ZvT1Hf~vZs+S((?x@Sx`NEQgU|n_gTpNh0D4V3 zhC~G^*N!c);*VyAHg$dgIX#tH(Qt@$ab&jaeQoo{fPu$N5iFzG30*vI50M&U7wFbd z=Bg??U8$G67r8u_y{WIv&*d=VaUIy~z!!zB`|BH@B5F%?%F5?HEOkhfM{q4D6|P?l zULhI3s&@A&$U>1h5`H;7mg?81NxcR`}~~y>pAW+nBn?>z77x zBuCF^10Oc)8%|~Zfy&HjU3nJY&&6YD>x9XN4(SHk)z6bD%5I-M&r%0|b*nOupZN%V z(fx3~8^$I-nsVDpVO+j{DKk)z;L?8voIL8cc*8qe?GzuUby|~g=Pu~dA&>>GhFh(L zaN+N`;6|$cv9gnV&X*a@(zAApbY6IlS^HINhE1z{i!JmBJVI%5exYx>Lk#akIh9Zb zZ)Q2$&c3fQy;Jc8XHhx4ul*e$)jHALT$$AGq~uNrJtO~O+D$%d_j3q6goKDmK&hEI z=~0PiC;A@XGR}o$s~ckkdW_#WPsF?+pke$s3s!O!i(rVVTQ3UOvD_^TpYIP?!Fi&0 zvL?pe*tWcAijQgK91?k3(jTnR;BE{V!%~>t2r)Yr;vErN^Q}H1N#2I*FGK@I&qB?! ztkP#!hZh1u*-y?q3D>9O$=jj|v#KI&Sm~wzGCnRS9faeNsQLbHp z_B&3iJ9(tpH6^+a$7OvN{czJ2gnn4r#HBBLYrkCQq_c8Nv>i{``OOgh?KHtn1!P>% zyDdks<#t#lV`P3$QsPbGv{xF?x>8@qNB|V%;O@#VgVnI0kx7X;xfSaXp|LC>ee+Rl zGOrG<@mRKgo8jim%2KKo7fr^HkIB1}f})>wXDMq~74LUaO500%c+B1%Zon81J4#vy z9IwXtVsHQV(vd>{8G3|xPv#VP|IX2gU)RG~Y8I6bqhx-bazMzjUy%^`=D;83Ea%0! zOMluw_$Cy{4igLWA<=yJ_(GInEWJgyFOHH^8Us&j+{?Jr{~84`PahV8J+k>axO&ox zKQ^|s{P2~}1{u~ph_%2XMy%(g`ipidO*ujTz!u_VjMuj}7if)K)y}kX3Nv&Oyf^4` z&Z^|{>sK~q$WSUQG|UjQ+px!41V93U=y&}^O)t4UMT)m5C3}3J$FinK8HX)$JnWxr z4tCz)4mj<9|FI3U^U9u8hVHC{rY?J|H##(Do#vgDoN z7cyC%r}~YRm5wuqjlhJy{LOkTG+#Y*;t`%wTxuG1nxdm(Htj%MO`JT*qkk8WyzNo_ zJ?0nvjl-(*cCVwkSUgtLs}ws?jj^BPl&(2@rG)by#EMv<4Yp1!rFh_!p0w`znR@3l)rop7eM6EaKsmD#93f~^9gK!n@(+?T zz>6z#px3So8Zc2wJe!b`cDeQ|!p(Fpf3A`bTyZ?6VwSkk@*kNC3{fWGk(#5bXt8zKiZaS(S{=}bQ;cC%mg5va55`~!XmCBz12`uu4AGXsLOjJE|Y8Mf7Cktgo zeGmWX(2SE^jn};?jdj~#_r5XQHVQ*z4%uZ=w@JE$K(W4b+}P|jL&d&@G(IIWBuRBZ zZji~0QvcE3V_Rl+cLb(vG(GSq6))LFd%Uz7|G!&t;#jY2_?nylIvWN_UNqTGBtD&LN z@4ugPU*)`w72zO31J@{=tUrjIZQEWlCxQ#JY|1KzCbuKXl15*@pTia6*6gqs{<8hY z^D6pPjv8&L_62Ty%U%1@1-23%UnhNoK>&@3!!e5H#2=P@de;VL0VX z1vG7)8$;P5&!tiC8VbMu_Tnb%b|D77SAID+{vZ%(LfHLOXi(95q0sh~CL$!XmEN0q zK6Vf!FlDZa0V0P=mAIg(`KyMH>wQ!&p>63C?0|E^gH+U-uok5b&fe=v3*>!COYlrx%9c&;bGD#H73 zlY(L&6`^C6+^=W`h6p;L_uQYcqR&e8i3B0sJhn>8F(7S_=moCXr!kBu;Aq|>3+)|F zV4Xm)E(rC9@M8Jh?hq?B$yhnVlvCCR zp*eRvBR0prI4RSwr8wSu4b)lq5(~1hF1dK?^l$_1&jmZWbN*MZVq9+795se0hhrjR zuNSr!Glt0{XDClSGumXt$=RlSVh23R5rit!7}&dw-f_C`G@5Ib!~D zl*JDlsP@)5+=v!6opBAz_Ep%6k-C-j8{CeRhb;|@J&3WyK z6OZ@Eq?sPZf6+Y=qwWVIAyPTY5|7K(T`nHmPnqH?!Wy%VB;y~JBC&Nz3O;(|Fxy$v zUL1qtFP>zx_Q|%qw7z_k*gCb`o^mt04#3*=&{K7a9AC-y{&urccDs!Lo)0)Er|4Hi zGQDHCr0&0CbW=Y;wxr#jf2+lNWq3cmkb+#MpXqp_Z>xygS(1rPo9iearMky*F7x;t zO)+l{JxISfy+5HtBsV%L%}}mdT@H5n__>~5Yo+X@pZvYe4-(a);JElI5#KKoRhvq+ z?04}co|xSUmju)ub0DBG*9l)T9{P|Uvtc(|n;K0;R9anBKCRjJ`1*ZluI~tV;Ui!m zZqC?z!WS4ugkjGWWAl|b@AFyBziry)+AnbG&1l5fuLn90b6{wsC6yK_4wM>qnK2F@-T{ z2TrEt5g3ZGzFF+-7u%dFuuu7Rs<-NbdD6%WVolWd$gl3WGb%pU7$F8kaP#KR38r-t z1T3_AJ}!LAkm_uU}I27{Km~N zH1Nc5nxfIG@G%u*#iQzhFXnZxf~DxmW#<##UX@L8wdW^D%5%OMRg%dqQ3CgE3NW~f zEM}93MZIAycNk7&%a@lJ7scy1Z8;V9$O%?_cz*p(zt!D=Me|qm&_(GFg~adjL*jIJ z60f%C*@k%>I>yBxm4?e#ZT`>4W<@tw} zMYAwk^zjcu`0-A1@DF6cG;_BXU1xYA?;`hFVhRpWP8q zLmT8VlO;1#O#UsJ!luoAkByC{VZN+$n#pVjd*Ri3v`Ln-#%R4wnf%)=``dPVYe_+= zm(D;khoPQA3N!m@ir;6ql^|>|FeE^r&xPhBsyJp65}xaqPs$hj9uq1&kOJnomaGKL z113SV?)#GRD!=)RSAxh1?2tpZMDe@!KAnx0(O$ zN4+i^AKVq2D*Z(S+aFGfg?8K9fV`oMZU32(>n6fIO>pPRXe-3W6D_m?Xw!jX~AMI%NT}c!Be*lI-dA{uC-q-bkAVUAl zuilk}yCtMVD!VCft0t$5S+?P1viglrX z`i}4B#9BfxeNp#+O-0rn^xa1|T;l`)#zhEgzsM&9@!WHb_6Z&DxZeaq$w0?T=o=U? zXn-=I-+ui~4jwoxKdoFROC5up{lfy88I8c;eNlT6Lq7G*ckkO!Bf7c5vHW5$lr06Pm8FO(^hr)YTKKaT#PMmXo(KO|3}Pv1WB z=g~i9^2EuqW%Cvd=LulJ476<7Qnqi~t~t=p{e4bdo}z=@|jQn-o0sqfVmqWZ~#~lw{PEGg@@5&M$7Uam&=UreG+o_?%g%e(DLQW zW!1`6GG*En*}7$`%%3}7$L-$3y#XBz95hgW1d}FBl9{t-%Jiwz)in`o?b-8Z%c>t& zX&{@;8#l|y(Y|$Aylk-s5n4EZq3qqgS2}lgZ$<}aNig7tx%1}Ayt(sa*IxIw_W(hU zRxJ1J7!0=W^o6wrW_>?Pvl`BxeSRIum0+-J$ui+L7z%dn+NEx4!N6~1&ipx1!k?PJ zz|Iw>Fy1k0cKOda#?;R-HeTp3s!fjJJ=Rv_fZGMH*e}xD?P6iX?0~6D+vu=}V?+oH z?>mOm!7;8;8${Z;-TVHdK0yQ_fnEKT3Jfrwu5K6pc|YHd872o4Gl7OrG$#lJ2nsMR z$`J<8FhnOXKo{$P0P<;H$K)JyM>vA{!62c*D=@@&G$I7BUQarKLC10T5xUTlKu10& zSRtsab&T~DHwKK;1P0b{fMcF5-R>JL2K~d-XYcf_LpJww8|~JK^+1b|(b0oIVRT$d zU>LN&Tq`3GH~iT&uaGJqmT-i#DGaJ(4(m z%KyM)060JD6wyhF6f2_N2MiwIr0u0rzhQli>;_DmF-zVZ&?ZhPgx&Z5ec^#yY2+q17UXwX2$j~y>zjl-mHfx^FTwthxHR4;wX18q{r|ModkbG?DuC>PwxPbu@i&#>^R&4vcMDw{u%J2SbMs)g8vT zvE!6k)23ycmeRa=bLlsrpA6_XKt_!jCDSKQS7D`n>-M_V;oDpr{jX>4APj6YYV3D-%&|cQ8 zStCuGHkJJO^Gk(F6{JGt3aYXTYZRtY#-M%Jh+(Rl%au2m6e(0hMvfRMLk16#0)-2> z7w2TDRi~D;Zt3a`A11Zx*OJ<`YfGNIc~rYnp+W_zU8lCJS-(cwv}z-T3l~<4=05%U zNZSr=HRB|SZ%&;(rLpYIhzc-)0Y(EudDcDZ-Hv%b(#FRCzjTZqbvgnA%n)X}+A)Ox zHu0&&*@eP*jG=@=6Bsy>;An!rVMYs_{PUQ*-utJ>HYal{&&}oFai7W^rkk{nj~Sz}frgBZiKAIz?Ot=tG`kZ5QVjG76Ahe9h)~8lAgrO8 zKp@EBKV|_(tP{DXg~%^%{j<2Xp#+AOPKcvF`h{jR29LV+9peNv=Fic@tA5%FxpJ63 zYmG*QwV_WwnR^Mx;5lkITv^^i!@(In8+hc*o=zCbDa!bdKn(nrQ>ZusI`)05)#$ zIuTykXa=kvpWd-}hgGatQ8!RTKAbRif^SU!KAHM97EIW3Q84FjUAt*SQ14oU0PSo{ zo3qib;K*&(q?z^!SY5z-?d{k4CV&?-hAIR2ZLxlU(FSmw!?+6m`91d7WBM6$8V1jU z_@}^*dHKra(y>EF89a2bh8$%#3KPY}1z50Xfz)qYUw+yDi;NjPMw*2+lNGC1$e^Kv zWXX?9WW?|h^8M`Zl@U&uI6*^Xwr-OX6+hj-=@7RUG5WnCQY3rW56vX#p;=DSy5=+L=?t{H*^V|+j3d)c*fm$YizO4@a7r~OWy zHdO`<86+DvZjeF42Facsdt}?LZL(w64qaQIbC=E<{+MvMXe=>w#eL!w)}5*X~_q)7DLD`P_eCe?8aZc@7^rT-I$|ClT!< zq-DF7GO+(ZS+!=B&I`e8<&P^>NT^(?vW5Ys{-|N2rp~UEvwACs!Cg#X;F_Zl=w?@qJp#-hZ2^72+)b!}$#JI67zhe5a~Lxk z1nR=T>5sAjZoVckpuON&0y?0RqZICY1qP!7n*)v{*ny{Y%y*+(FZwRvm}OVjj%%3b zv~50m7Vq`lgC^P|AhBljm&!4Hgbc31@Fl)7gcI&lZgiNyKtJ?Hzw|%B(ZTme98(^= z#wV!o9SsrMfDvvDteo{=eyklD0@_0p^W|8FwPgHNu8euT?)nb2=V21hg?@yijhHFk;T;fmA6|sSxn# zr=RMki5v#B=Wh?e0B;Mt0$_~T1dvUUK7iagbL)L*qy2<&6XeXvGrr_U4s1N?Oq)DS zep=}ZV+`OCfiWk{4(&2y;wGw@A01%4$YuumG0H^Xz=i_Alfgn?YwiTshj%R9EIQ^% zK8Q0X&IFQMVT1{tq*Ui!_TGE%sj($SnT+42WgFe(a$IU_ZGC?B>1V1nCHPq0ym|FK zMVoP>$7!Ejmn>0IH$k{#e)H8gYUjc+Fy*+&lARQ0&iY&5JTuxM6asc1ga%wp-+kv@ zwXwkS1Ddg|U_E#?7>-sfUs1Uge*PsXrus83&{POVe7jxuoTJ9v!C z0W>6xLAq643tgV*u zUx<2EC-{?R51c)FR@ckg>Du}G0w}*2D1GPt)*m>Cb+mz3U;tr^#w=kNAS(1_7zsK? zU}e;^@;q+m2*VGBFd1tDat3Zf0ByNO;9!T3F`t#g{C;*j?n@ax8Zrg~2?7f3LHeSM z`}*l=8_iq?o`L(cdbTDUom`E;5XG%>!4mb9z?hD|3pA4C zDL8rJq%4@TKwbB4cQ5WVU#HPF(YaN&Z`)o!GiJ!B422ys@e}}atF-!Fq~7C?J+5w& z{C#4)hbt#LZS#mgDEaNkZ>mAYGf=2t zp}?-&f*-*cElo3@XjqA_fDpyHkj)XJOrobEP@t_rh_P{uE^NA3`-SrsYT9hFX#%7n zhvAkkSz2ku=o02_u93u!pbuPJy-Xc};ma?+R5#Sd{uKHan*sMqgf0Lk&PMj*Bpvff&;;{OzQ`n1WHGMoGQ8^`vI4nvx-720g3ip@KZrT)A>d<;s<%W$Ts_9ucky zWH=N5Lg$}`|yA4*_=(Q~{*f5BzYY5k+AR-wQ>H-0Cq1;Ml`SM<5ZQlHJ435_wR zVel>NwFYGzZs&;xV$YsEx@*Uvapj7YGGoRJnLd5G^y$+_x_0X-O`A29>Q$;snQ~>+ zx*zWdq+L`jcu*8BQdk*p*T}BYt4}XkxoV{v3|Tu$>43!|RUuvRk}auvXv2@*2C=5M|d3 z-*DL32@G!q-M`)l9i{uM30ZctAg-BLBQUT_M)i)cK*mm(33WM3=XjLZEqo`z6{>eL z?xqdQ2gb=YYIgeN8it4~AIzTJKYu3(4Y>WGLI>z4*xirz(4*h~{7VH9?6xr0B-oMF zhxsrLeLydCv8$(?`{qJPUCOKvvPP0+(Z;i|%9P_)N}E8rD`O4l4}oLNsx^9z8#)34 z*UT9P&anpH0qm-n_XP~t^=ImRTvu^5C14a|QlGxqozo9TG1v|8yeQwcX`8nDDDg)s zBzXG;Hm=>Mp{Z@o^g%uEn&IAh?({kNVa5;IH%I8yBQ!X_32&cYixOx^I}y%A3;RjH&#gxDKc9XvE>w|v=h_3D7(Bf*h*?pA|8fB#eGwRPK8 zNt!sRv}@Z=y(+$&@Lga%Ffe4zFy^dKwt{McSThVetq!&Yc&i{V5GR4O%O{SV&>!OU z0uvaLCQK#+Hg%E#>u(+$G^wu)*c`g-y5Yu7GI;nv{R~-tz0i$6@cUk&%We|HJ&gn+E&- z&{sMgk6oHO9d(R)^g!wKXYA9_Ye}z=Wj6~>I2N-4!{wlPQF!m!-$d~JYlSdpAkMAw z;&{usRZexEf1kOzGz?b?mZ0R{3V4`cd3&pX{Sm|o3|EJ70z=H<)d~zmKEz5NTLX24 z^Mn4j{Y(l#r3_%*Z}+NKt*-G{ygO!qvv_aZ8}3ubz}oX^?Le4+^QO%Mp_`2!Z(A6S zwFii>NC;|<6maDvuv(s6d9*)sQRLVFS7-}M?Hv>2MO?EnEF>_L0b>N_?C;0Mr9T7i zH4SSTpc$}we0s-Pym+zHuU}t}2Qb#ejXZeFK;Ldi3;oP>|9<^t!uSbM)*1w8XJeWG zWuFRe8KIGW0ILgluf6?({61`6!DR+Kk5F!vxLY!=7ao4(Vf~CTf#ICTclTC=n@M0e zcJ!FKLatuD+JEOl`0e-K{8v%ouOM5Le*OBX@ul~V<5mFsV>5xF(~(Zn^=MZq)U%L; z9}L&;p$cg|M!!RM<5i6V{qnuX$sW>bTdP2M)AdcYys!IL zc2rrNGe|$sTXINA{a$8d8Lbapw56=hf;zr_f&>QYHCWa_>+n7DM5GiSP+SW2DkMEl z^^n^0YD>W$1(hbsJM8ZuMfw!cahh*xF2x7Ay6(Et#7ffn_s+UrR^D=JOR2N4PN46$ zd)jJyqr(J-o@aV$edzE0S9hr}p@MH6&iTfK4(KU9u(;A!w0}{p4{el#j8SB{>m>mb z{3Pio64}MaK=1;ZE}ngpVAs~kLVu-?bk0fWMH>C)-(2=1A*C;H^MGw0M( z1A{!~!WsGnZj~oasT+K{^yy^KfI;e38SEuN9Si}tZT8s+0Jv2?@%R(U?4X4)Nk;W) zGT*zKTo!~|hW->^q|i{#GpEheGK?|JM&sUl?$uqrcP!j4|33S-+AAOonMVV2f=+D0 zI(6)%o+KD);#NsgtQJk(oX?(`{?>xyA0f%ve)D_*>~x@AK1fW8B?jB7kU zaI3@%4ue(fvoOG9Ok6oJ4kcSD>u-HSJ8iI6!xhsjs4yhw=0Po6O}BYg22V2xTe zZ>6y>7@vF?eBy=+#GHoVtX~AWMd4-=7}l;{E3;>)x}w+FVqr zUPb!%?Jt7{4$?RctJkhp7a{zxvB$tRtVi?1(w zUj8sbUi7@+L1o~MNps!pcsFG%lpDhdMwr$k&p)C}uH!Eqm0`eu($!8U?|%KRj{9Mz z59Nv1p3v*3-+EfIwaTV#b9KlSV3J<+3Jkr^_0|_){blu~`^oM~cbXb$bd2ZTdro@) z-CJ(I>vpL*yQb2KK$76w1j@KyO8AmI`TCR6-~BVegmUQW@_QGlHK&%;n&+5e_kz;( z&#uzp=MM65zK^wDu)r|T{euqLLkEIItF5i1)32SR%!o3Qv0+9nd-KaTrRS-hQg?A( zX|}PMu4AvidnwIav)=UAWNi~28#>-j^|qv~omSfIX{U4`NHtm4BtVA=47tN|OShxl zq|u5-I^Qy*%IG}5EcK;yKha%^_9?3UH{Z})s?Vq{B?p%bjH%DiJ=YeSTIf7zM}N@# zVdf82h+zyq>n*A$)xWPU-!}PH%ai0vA}@UKfjQ(6jjFC<@<{!-h2{?X@ZOn0RMgSl0bej2w*JO*G0xDb^?7Wt8|Rk>sHB`J-bknTLI=)31Bd1?Lg|mxXhgphL0crbI(2(aI1uNm>7Z$ z4EJJgmC%aU05N8mPbjxaTr&ac0tE{cR0B`IycqB}@VZs<&SQpqJ#<# zJGSkR)@@s>(IMU=)22_;pi_98bnMtsTC`{(0|pGxJPoN+rPjbpw=x6^3vB_sZihKQ1-r)Rc(B5z5#zHpnPN`W8{f03%a|5`-|~YSXLf zjSd5OQ@kn7HZ)VFp~8w|UR_?&uicB?Ja`$CW2nmfG`c z%LnN{kUnSoC@not_LO*^$CI$tVOkC*%9cn*xPJ+=CRB#@a^jceuKVwj$6t9|%8ew0w=P--j7LR6&aSXi*TF z5Rf|l+))x`O(gH7c~8RDgvrCtJ}i&D{Fn?lIY92Z&qoIW2K^%tFg|p=m-ao$)-s!X zl=~yqmOxL7EiD3cn81)HB9BjCSm_fO(AqH1^mWtAXGK1fqW$~=!^Y-PV^$5h@6r2o z%z=Ln)V`?;e%p$F3IFJD9+( z?YmSELgh~zVizDVkc0~6NgFKZO^rN+ z9)9>?U)pDXec}!rI&?_;fHC9Y!I28{VvO`@(`#MY;dy`u0YU}B3h`%bJopW+eh3=~ zCP1FtzT*tWM@XT+q#q|$SA4EeY>^{}j_9*HbnuXB8_20c|JE;vH$#nTHS`@GDoz7*qDerQM5x%k zW48(;#FJnx_?-~A$;PLir8tkdBZ;%OmdZL9ZYF^Nf8~Aq_UVs`9{}+w@CM-+V*kGV znlA&1djJ0Ya^S!L`SsUdHTT6wAAO{5q86CyR)%1K!BqR~jCuJFb^lUTNTs{)cfNW@ zo_*(88Q}gQG_d1+@xvG8-iPni-8akuW)dC3tPvPs$Q&0mUfEcMjn_YUUBAO@pN#jU zZ@2Assh2A-piOu+>8rXMXUEGf8HR|^0F#B@S3i1Hg`f)KD`+{I2NM`zd@%Ih?%%U- zKdWUhFz7-ML2E%u%jKSa$2E{V8Xh zu$5u*PU?4*=_5SB+|iKaj>xUr1J?Y74_{D0qv^V)DvU99`+e=@)54!hfye@SOj37Y zU7fShaVdcTK}L@t`bHHPA`V5!5ce-}j>KBdb85Jvp$ZW^3yu_^IZ^gR0f7MlOtm?V z=_B+}o~~9piJvyUH2krl#7`4HpkcVy0t4ZsVR|rDT>WfkjEfv`7;w+#JG*Y&;?}HO zqq|Sq;L?cdoNLlV({JF~F-F3_vzz7b1R((Jg=rY*hdM?ZNc&%Y_6I!r(`R1*&4jPU zGr{V^%ovA0pclH(kWkKjj#jv)Kg#F>cS&;DAl!Jzgvn9<(~m!C8*CzMd~6r!5BhOG zpBp+!rY$E(=FCj1kOW>5mvUxb(xY>60vzjEV5U&YWW(`X%BfJ9k_- zDTg@|Bro}A$yG@3_RF)t6&3eZn=`af&%0*(_wLtYnN`bIY2RBnZq<4SQYP#LBhQaI z-lG*z*VWOQ=E{^{u z2o@OFL4T0(0~HWZ)3Xy#RX&w6D2@ye7}!;!34lqDb^r2r%`f>n=kqa0$4sI_6Bxn| zgiFmIYATcZrrI~kJO{gfjK{7vATYSyIPESaFu)wkk1a2GBl4VJz$iILm+Lt!HF{`^Qe4 z`@tH9(!)#38=t))jecyT>(Ov|Ln$@1lrm1}DBQcS3IwkvdsPJn1SQr4fq`>;=0N%L zA3U%9ArPQ7u{on5V9i!3&P6kHp{BzXqcXT*Z2E) z^!Z0s5Mx}{IYWaC`Yf=EKuBq{s*$d9{x11dkfD9j50YM$z~FsRdl9ucq1It8p~|lY z8JB)zvd`GSfKU;H2ptzoC(SO~;#GtyzZfEv0HMlmCPYAsF@fRine*1Lw<6qXZk0&D z1edbRlm9jd78uxhqRP+QII|kmMI4FH7krH$YDkr-Rg_U=X_iIn@xn(P&F&ZG1+(E^ zsDIRFr_GKUMp&$WF>Om3yVcB%GApA&bHPp&0fDo7+QHy1btD0{|GB*~U)6RT@1u+} zb693=&zJ}*&;Zl!e%!~*85d?Ac`Q=r%nqM1Dd#A_`tI;c2cH(gF=8|Y2tVBS3Jg{k zrpWjR05D&K80ZN5DNHI&s-*LVLFez5Um0-j4!Lz~G$jZnlq0;*m(9ughQVVq!Wy;S z-dfAK?-dxJk^blx0T7J>^JL5tgGwl)hn^g5a%g#pAteGdQI|f{M#4QK)*XR?a+o@X zj;s|L6OLP`Tk*S!T8@y#d>NZHVqF=Rb>=y9zi_X@DrBGq(K$O>(Sp$a2lwya=vK*2 z8t{5=Kv!@*gX=|CC^Y&)m4!mU=nb{jSAy6WT&uYw1cbWpMVot!3Jfus-ugxalA4_O z%m1UkiDA>0O={Oba;JYY*dIaQNWw9I=#U+A_Ey=WwNpTijmkU}!Wcuf@gmHLV~A$! zn+5J+gTM~dn3ETS1%}wbPy$12pjTkHS@CM!D%tU4-H&I3xiJFv+qTaDWA?;5z^!uK zT3>%Q22i$iSvlhg!pD8;7H^+tZ@<>w_U+rHYL%+`#}^0Z|JW`^V7SpRPGC5X|7L-Kl>Gqt4=^6Yzz>5pfV>L8 zV~;+ju8X)ac5L5KX3d(V?vCUQ2zIMXmnK~x)$={~+@r>dl;iG4n@hP>jux=t#vu0r?O}YxTgaO`uX=INE|ix9(Im<2TcBV8>DsAlNViJrkUG#*Kv5-?(w3RH6 zM1aHo1!05n-;e*kem?QU6WU+QXc$Ou?UCGWG&pqNkgWY_t@_+f`)-=1KdxA_qU0@+ zS3b}5xqOlN3wbudv-0!@Pb(%Zm{dM3;&zB0OkOPc1^3uNTAOD+d`3RY_?h;Xy-0Q` zQ?;ywhqsYYqo&G~Df48-%8jyr|F81*bymcz->_b4SFbJg8u?tX;=l(cFeFWwR3f)T zO81T3ZyM}BxxYkija`~qvq2*UN#y3(r=#!8z9GwQ7M#3i@>K~8=2poWIfiWHH(=L@ z-#-jcnS}#(nTeAm*7yPl1wezk4K8%6bZyP863+tKvD-BFMdCCV@b_cwK|UU_!vUsR!KeLSdfShTQ1xxZH&NJ4!mxa%m?}aTscWK4HN$4(@!)tHB33!t+I1O zXQj>hqz-PCHLBGJcqHIX3DDNuD$$6L&S+c9DxCMOxn^f*721q`LYzkpf!`DV{)G)`$XFiBnXI3!@zEvoi1?`aLp!} z6FXpIp43CgAt@Gpkf4fWQ{B6DS4(`xKwAPl^UO1{X#OJgI@q*+le!$@(PiV|R!RH> zf;W+p86YNsmbvi}Zs-rDOe_MfajPT{6av=t$rcBe_xN0&Sup37>!=)2i$#_Y6?3Z5j30cHgU~ag%1`7HMer19jN(a7PLMHC)0EhfBn-5dlUIVkCy47e;T??Q*nB zpe@&dPwL}pGDd$xlDsCbjF_Er~vQU8YW* z=7bE-8QX~ynzEUoE46FX*368gbjDRu@!%oN<8H`3jvfvkIH-SI+@lSuOQijd;T_dA zC=P-IhPWe%E5^7ZiI~HyJ(7UoV!X^Qo!>J%cIqBJa9AoEa5;{>{)&C?*9xc8zQ2<_()=&(1v>-@)qR zzQ{QA0ll~nZrQL!%ejx6BG*PUec*bCMgW?uf9=ch9Lg!94Mz-ChUSF+xTYP~2lpS; z-8%g;PwHTkKpDr(B-3NIwxGnM? z=LmrN-acW{c*n5L)CHl1dfqj|1r}O$Z5;FG7>0WE?VYn1p^qq-&}97>a*r{&=99jd z6ZCM+vr$)1e>*uV;E|FYMm6TM}kUC^Z+=lrDdEQ3EqR1B~1R0mr}tc(k*W z7*Rs~%yV?eAz52xRRMzlEQHr>ytc6v8(K_?4lXLqHZ_x)Gipky!KHL3Q*LBA>2Ron zMw;t-qU(8K;kPbQpl1PTxU8WR8Bj!0R7fFJX4jEEk#{&MLkkR{aQ*t*q(I>(WW^F+ zNaxO7J8LRvpiTQW8t!@d%H`@`y>iV;`R&)J;hjl|y;1f(d{dPLkNvh1KqW@ctvv85Z8os$;v4TG9*&sl@ho)iey0scLjF{?cep;ht zD^{+MUcGzi^}ii15Ezd5@*BMR@vG8gO%wec*vtQ)Ah8l~E3~l2gehhW+y}`k!8NXT z`0O|Ssj+-jB zcN1|iLhIt`L0PZUz2vQwZ)vQFBsr33IBTLk(zf@R-jcO>R$o6s0t0PxwacaL`A+Nw zLQK;Su8_nfz|({>u;rGP8X$^tTs3KrJ0tFnlq0;q{;9uwN@dB>HiyQTpp11QJ_OfZ zfr0r_|AP!4s0(HC;>mSx1hAqUcSp)CBsXi|Ehh&=ywBok_-gA5%+)@Zt`*Th-%k0q zy!Od!+Q!OpwTS4L_rs3j%Fl#nXZ zt4P!BO{Ly~deZjiw(2imYD6jZ3oq2KkO~(m%cqno-&c_uGiyk*t<9wNg4$}cV8<1F zwmZ;HgVB^3Q$`vtYAAUImX__?A9PfP77C)nHtlYce*L<+YoEYiuyW-}{lvXz<*Jo3 zYxXSpVOG>&OK2FzjvXtz_Uw{9d-mu9B^Ud)9liuD8#iy1o;~9<3_!5JfM5K5kKLy} z_XJELwgSvcnW$p`_}cRirq4aDDJLKh3=xJC3SqV%=lNLu^btrZPN=BD0L%);4D&|F zBzP135HfkR`xie$eD(dml)wPv;mnalMzn|S@?*>E7(|c6=U;;jIcCcoDbLg(lf0GU zElrw4$ZO`Dw^LpfDwr36s$g_POhgmMcVY;X8d6F=FaEiX8!RvkbN`?NdZ44}+NOc{ z6eN)%vL$Y!tR3SLzk(e$bTUT*WzilQ=?|fe;7rhg;6%hk1R>}kyfk?zj1ChR$iYEO z3!+n2n^8>#jgjtO(tJskSr_P5K|Lj?_YnDh}<_7xPc3+S!;0PTo%iNA8unRWc49F*Yx68EC+7CR(@3^UI_Vqi&U&OX9SD zM%NjB+5d}_ELu{&F7vhI?wMPfZfmM9g&t>mL@`A4BF2hn2<%g}1sqpc`Eh?aLVm2w>^5rBbA55vf_Trp)?&mj7;4z=GH?I+!|js#LFDUGnD5D;w5t@ZbG+ zgZ;6YzyRYX6g2rG+U;$pyHT`DFsxTTenpuX5flluLsY}Bt9-43B#bsg{S3b6@0dy` zv`CNSNkGGpzD|0{(lm=a{_5lMRpqZ#OZM1{zFZP8r1E3RYoMV^MS7$z!J}a8e5S3L zR(hT4rT5WlkPCr6$=3jrBa|`aFuK>0y{60&VS+gm{g8H)K^OB&Q8tA#?Sxqp>hC>G z^)w*`2Gr^#x?)}kA8Bi))qBtm4G2yAO@31F6HPqzb=9v`qw(%H@5)EHK9aCiVH$p$ z$e9Rd^p8N0EPpbkgXci>OWL8CASXsBfg!rk(5y?=7Fna@l7M*=tO>dZ(M+Hv{?=Is z@?}tm-vnb56GLfo%szF6)GF+eQf4EZor zNbC54@KFw_aV62GE0_Av4&<0~Wz{W7z zK$zvI5u@}z?FeIBxpHOc-nF|XrJ_9sf@d6`C||a`B7cLSyd-d=3~LxBm8(=%gFl3c zU@r*-fwryN%He~D^*%sGMa?4VUWK@iGbY|3=2nRzrnyz(W{RD`f;qn9+M{zQtElsAy`20@zv~=AuCNSIx z>8;nVTQ3c2H;}aH(n`A0>EzpH-%8FNIi=jha?~_SA?#e^$~e8Qu_@*GssbdRCb%3IeVMz-gBEQTzH!d9d?^^?0B10tNOHL z&YWDzmZ>evmj0ylUkv8VnIrezbDzBM;tTTSmtS5s=FXj4X3q4baE*iW5G*jjNO;K` z!_?5wPJntDLE1H0)kMZ>r@wDFO zA9O({yK)T;?RM)#ZLBh`ihPvk!qCvnpI!eeNnVlKb873j++(+`M+ZlInh*pAqk(xT zItEW_>o-1sLyrn7P4=a?Mo>Xx$KNXRf^k=$QeBTkV7#G1Lq{htz?dob=92IV3>sCqT$doBlst{nwof_6_c%fUR+t_;DDk*2TX)bkmOBkH^BkB$ujRLU= zu+%9J{FYV{T{w@UhgIi^UFLYec$+$sswL<(v=8;E}p%B_-E5`e{rz^zhu#|~b7 zUrONYzBR9mBcB) z`y^wgj6RPKSMPG&Dq+sWiWJkj)&>E<+$s@V2J{)AWvnsAh>TBiDzwee#hk5QLpG4i z3!``Mea0mV@$lipHNJ$=#qWU7*gP8quYg-6;FdH--6~bXaD%3|K6>=1%$+?~TD58= zMGF;`58nGgl75v`Qe;jc87gOx%(XJ>OFwUTUMV@Qq?Ddm+K1njR%6PV3u>x{0d*zU zitc{7N#z1{OHL>$`6BXZA6Xh?kqnhGNQxY(C1d*c<-PY3N%m}cq)CGoGI`2$IdbR^ zKV_E>{Ft+6&#pp49K;L+3k>WGQGJtxfZgukvxDV}l3!?AW*FI{FFvYFhU+g%eW5RZ z&ZYBo%yUVB0aZ3?a%>H23WJ?GfsJn3)v>->+Ytg)gzQ^bT=7(D(E{DVffG%?;x4iFgAp-6Dz^S zLx}h&$wzuz@zTpa7e+hIVEnIBf35wcO_Npw6LC$-Zfv@yPMWGQCRYBqQr8fA!WxIE z%OtLv!Mk<(BWMydPMbVUk3yb)%I9W?mIpzET{>$?Dr&qjo`3FnEk__=O*mHY_RGB* z)oW-b%10l0RO{kNp)`M!R_6v#7mdS`MN4GHlo|5Jkw0|ppc(Bt0u|5B`ZdIzk~$SC zRESc}HFTnZVqUmavc}Mc`>8z}0#(`gK+FbiQi0+88QRSPsESUXIxQ!TJ8AOxUmDes zMKEL`zyJPwnLl^Fy!-aM8tj7L7NmwI7?jnco%M0*Gf~ zac~}j1qK)nJ4lS}j9J3qL3WgnzVN8pAgHR{F%aru;0EGK%dR#QQeRUxyInSYl6jG~ zY761+bInd4l{Wph-P6|Bw;Px9?@I{`AiHMz;wS~$`J@j%*{wr2pX6WAxw&i3@o8&w zw!XCNfUiu^(M{hR-+9Y|1qP6u6*fLJVYflwByyrHbFy;%+~cDY8nnGDx4vvnfxano zeLx5IDc5=)fx+fNzpT5hHS5YX+5^6`#+0)zfpXTy-GgS3Yh5EZW`JfZ-{mi7EnK2qTz@a_;|n_*cpLZC0I=^#?*fv{|1vXFjbx$hvaRTpfS^ z_4mMhymR&<>q{N4kYdPvd$xS?o!^7~HfT%V-Zs#KCL`vKByREa)~JE57a(+G&yigN zFp;V{4q_fc2@KI8FM7@M;9(A4gc+e~4u!N0)y9i3BzDZk#6cJehK!VX>w>`oLu_Cu zfgv`~D=^%w7}IW*q&Bv?p~|locWm{=I|x;Fv*6)q;w%tn0C&{r=4bIMLX}+#cWvJl z#U|or#TXSB;?i5k4CK$BUm_zTb$1X4*AAix46)37uM|~aI1$T?+6h@VguvkF__qoS zT(k4M8YUiU=Lv>755HW57BEygtvo>6N&-o3x7s}ldywWj)@p9YqEWET`YR$dt zNH)&Pc}CE2IrT2}jQ+_p1kKeFEsb~1^c!OW!>yj)`dSMNWy+M1CQX_I;x5F&wFMIx zk|s_n{igPlep7B995`a2^qUsC?7E>_vq6Ig#s1p4@ey1?mfa*cW7!OiMtG$H!=&+( zG?a3+D%JF5ZlJ&IbLW5KFd_g3VDS!ct6aEXVH9QwkhBS7I0No8rh)azr?mrNEZ7TJ zXlG^e4!mvuKJ|B?J%IWf<4D{papmmVwX5XF=Ceb9Rsg{UdxPhm^Mzvej*04=bAQIf z(9?i1Y|gX?bX>PYMi)RI*f$u>Ky>pPI%KGf9Wz#KMK}iFj9E*?+-Q(t`FEBPO=Y@N%)y? z)iT^H*k?{32|pXVG>4xKmw{sjO8BYRr=#cco*~O_794+S{8ecf%&oFWp(1J+h{e1a zBnI3nA9`5N)nUx!OX%Ofzf`GGMG_@Sbb(tX>8VK?1rYZD!$+6}?t-+r>#n=BEIOmd z(IZCdeUe5Iub@SX7Mj74JOs4IV*nS)uts5;bcwV3kZzT(4nh#^$;E&>;{|S&C(o$O z!JohVsrP~BpMTz${K~N+j13vHa>dF4?~YWdQU%;9E0(LMM=W?f02suRlnMCH{{^{K zI(kSh#b>ZvB@7ih@j77ra9_pd08azDeJZCtleg(Zwm3587= z>qL3C&fTO+)hg=NN`4>=R_Pn4P`-kWPlii-PG+EJ<6u|{ZT8%%SE=r!dDw9I>BpY} z`OR@$C*{C>vw>R|$~Mz~or?-&sneCe%kZH5=q0z=OpJtcp^{2HG;4z5201cuA7 zVYtyS&W7PU{+n$W%&iiO_$Bi_Zk3L~dfh79wQeU_vS!hk3<0-F$H-#Ztr7;4F?~iY z$NK=!u9shWIp9_a6K1!aB}*1boieo=)=jJp)`zS@TvcTgmIMzpnR)tpM0K zXlT%i0H=28!x=^nZp=}ze`Dd~=(Q97igTS~fN z2$`p4?>~#hVrK3+XP>>V?Rf2X7CZ7(J${0VG4YH4Duc5^&kW^ib8vg5 z14=#i><}o~_L^(V&9^PYpeE>mYqa>715xu*A;YNA*QyK?W?$S)Fac(dQe_(xulP#6 z8eJs2YdIj4Y+CZ5_4s5D zszu$9&p%Tjk&Q3y1Ccut*O4v8fp>z1sMNV|xHPWPKPN8F!2K6T+*2O~v)-(70#)A* zM0?l<0u&apZQ|oPDzL|6=`55V7#?^?vFvD?5ienfvji;$P%`w&jxB)_o>EN+gLnTH)W+C_H|B1!4GuA9Px znL(^3ogc!=j-GvMUscHl=z;MXL={Spn}KS2pMggbO(rjbC`L>ZlyxU0I{-$B8#%i& zRCAc`_owKVbEn1OiMcrO?-+wu6UkR*XjKCixV&%<`a!sjg2zflQ+rA_zLvyx^utl< zDX}*wt1`a*C78Tw@Z(ua@bh$Du-4D3J;JA5TDhNZQ=#^4)g`qqL##3K3ap3^s02iI zk3sFXO;;y@L?mSp6ZL5}$aMRD;V0HvP+GvpdD7r^z;t!d+~Z>Zl<~(zr{u0!i8ebRn zGo5CzCR0mgGM42fghmkx1610IJn1-V;Na~W#MX7~b-KiMmMw&FD zUaK1#@HcjMTO{_|T|B4uCWsHzD>TYE8Hh5$PiMcd>0EX@)3H{!5I*};w96fERMvVh zu8FrS!YG1-cVIS4TtekEC|;CW>4PlFp3mH|SyC7pxpC3kg2ekDNn)da%c78hq(?Q= z)Jn`#i1||?XB)p>D-V&$2Z$u7%)Lzj8>%t#&ilXgqvj`|pim}i5Y9Eu0}9XkU>P@= z#L)PE?o>g8-NdQS0i_Z5xgFtuKk(MoPJf)cYh(1{WAj*S1%wAdThSZ$DD z7|#_idQv`_EmR5jM~wgRZHpdtEK1h26$#Rg%;U*WheIK2uIN;9bl%iZu!$w}k5ZW_ zE2B^&>yO_Q7K>+#6-j76hiHz+bP|jcQ}4~mVR=i`$?=h+-o52&^xt?Fqk}US zXyOvf?hNZiQT#XFImixKKnzvKi-|S}&b~`JZB3b|pCr=oSLC0r+x5Bfq;zy-Y>L#a zk(9B%2z20K{aZHF3~=^^padMfMAOO-NWK%gs82{2$rZVq57P`*2#gDrzZpVOBHd%S z$DYQJAuNJzoSrtpl#b^U^k7h%Ws-^!ze(muN01lXhjK8?(%>SSOUW?Pp1{XuyQ1AN zmP=uNbVtWjY1ACWMPub@{F-xI(&3Cx6wA(oQSH^3+}DMENERI%T^i!r1qZ)L9fFga zC7)7-?0ycZq!m49|G`pT9BY0s=$-YB9JR#r_l zy7Vv$>EFu%sE*-x^rMCRJUmH6`sjteg~!x8h|6?rOZb=1fI&_XKQvj?6e0SbVZ640 zJ8z!}#Y zm31LA59PHl1Q~lavU70Z%#RGi03sga`q5nZ#5a#_k+afVV7q0s718WD^19x2ZV-n3 zL#xCTFwJHWZNEguk!~%WTfXRuEwwq6**Il`%tL|aE;j;W#H>mX9~l?UjyNnnu1)^iB;!x4raP-6bzDQS8q}#@$Sw^*epCN)qFuq9FRmK z(bap+%ALdP$}ZGvzi%hhYF#g>ovCMZ;}k0O+q<8hf{>q z*%evNK?_!p?tWRqjMU_w%`7o~$`yCp8ZHM$QcPk0KF(|~&GGJJ)?s#DWf>rP)$XGw zV2<79Q()KR3=*OgW^+~lwWG;=i^J#+xr;4vKWk`Zpt$v?1`(dz50$Z>Ep2LWI~+6= zbiKBQsj;+h)p$-~<@)XrKMzE>-~ZiK%x*5Du$1E7Aotnb44i~Pt#!=7fX$t`o7r4& z4t2obyIyW}JJ757pf4@^tAY0HO&bdl_rEZjGbB1Z3X441*L_D}ck;wqqp|!AyU_<@ zhY!*G8v$ArerMEex4Hh?CmknHLKkIfB+k>$%lJ;hS5fi=)}+{tW+%hA{mD#SvH^DB z` zIV0{w`n)8ssi#=IxTWdycYz^Qvfm0XC|mvGM2)8c82nBN2Lx1%^sPQ=eINCpvO^MGs)Jm>+qQp(XOawI%+5E7ydbU!J zqe8#BROIofVWm5$>)`-1q?elD&JI?}SK+s42&rZ5dRM9q@T(x_pvd@fbc4NY1b?qM z#db| zUueT3nRz?4*7W1IhcR<;y0(27l~X z^|jjIyW!T}Oc8t?1CEv5>u`U1nHL8aK9RiADokTN*`1PPC%iM z64D70;TO8-rxGTrC_~03X6<>p_!2DZ`l(v!c@cC+SXf#6;aivZ)wuLW zLI@VVtcvI;4uLut!0w|h2lijZ!8TZyvIPPLz|BuDQ_DiJ#*bbd(|Tfl+sS}&-J>Ye zjA6_<{pQJ%6`%R8@zl%7slp*N=Bpq{XW%bC_0lY~nl8Ao9(L>41mj5Zzz-X#uGQNo zc{j(_mh){>%?V~-*~3O;EwR(Q&4NS0SBskrP1}~cSeAvfy=r+@g5KTIhjuB zBY~8AWw^O%h2eJD$mB2A2)9a^{t!}Hm=spy(#Fn<3l@!4<}%ex=6{^RD*bws`Ra@F zA&N!8`Ep$vMY6~=7Txc^-8w9#Q0R{OV7&(DKBrGR#3#Vw6Zu^ql~BN%(!Rb!n=re- z*e)@bHmyQf015C#CL(A>saoJcLb@Q|QVWD=_q2c7tv>+k@|KroTlreueZMfLnR=I=o`Cl$yz|P^MB7%ZYOoUG~nbXz{pqZM?iy|M2 zP=3aSolp9oXfGUHQi^fZ23&kTfk}MmaeAt@a?a4N=NmZ~)86&+ry_pHa<*eOKkR3h z@ZvhkuVOz&^I)dRB(a)(5gWZtvRUvK+#kC*kgFZFtQDd67XQ?;db2Uz=zHFaOfgSI zPNd}br))udBr6X({5?;1I04;ijy~V6ZIk7TS^3tQ%C(Tp-rvbbn|mAjbCFXypT_j@bOm}!lYiqTg|)_k z5C#E}1-vvWMjaU7q1pVbjO!0Ev@V;>x$3I(T37+z0-bvb*ebemUc*iXO*4J~s%wU? zb;tEdB2vL?_K4lQ-LW-54ob?)|yN&!#v;ghEOP05;$o2%J9Tw!Qw)4A3gw z9*TGUJz?q-qu;FRO@CG04O`u`HC<0PMoVO3-q#@ANh z5PlrYwtX?#3|(qdvRxoZJVVEfDEZaQ$+MUADvVTdrI0<5%~uJLuu;*}aOZ)L{Jjyd zpQR0ymDzM8folK4S4J;V;i03QG3c@Y;0t?l{WBFsr?)VPtg#+zy2z3!e@grlPQZfH zRQ07Y8O7fO-gJ^G)DJ%ar7=wJzr0C1G45~(9m$vj8OecRBS#AnHjS~@N@`RT>gkin zj@|B93$WksVZFPkm481e#_1 zsaS+vvmYk3dIGOwL&^esRt)?U13k7f9sO4viHwO*CncJLbJ40F_F$rWY7<_sA)oBffI^65h=8f-BQ*Ie{*U6n&eUT6+mqQA7gMK= zB3tY(Y_`TzfM9d1ss;i5B1ZU!Gb5@sTw)2}@d^tUXu*?i8kO<%A& z;iLyDP^?Ax(Qk6*^xmlsu}|PK(D2M!4#fPE8SDQiGrle^*c18m9^SY6U%PD_;YL5) z-IwJZo@SqZ41?4)|H9Qu-~;)bxMONgJ^amDE4QZvE(i4xv8(=Pvv7(IsJq99Q0YTT zD^;QRJc5!j@lx*qr_+~Bibl7*Zy0IWm9m8Fuf7TRR~~8p=|D^V{*ihAMz4iYN;-{6=ZdxU{9!N| zq$O3s*N{cx^}e9jSeo$4(@ko0#hrJp$~lGG);Xol(%C)v+DY}JCsMV#u6??wNeRXY{ zuh5g@b_JLK2JrD_zk2nl`Xs)Tvb|X*&S&5HIh-`{ZP~rp!Aj5R3l*3BUs~goNKewy z;*k`d5cD5b=XfP9&kxrRqCz0>9{|2H7QzYd(Gt+`JD)ntBFAP>E~Eo@kBG-neYidw z(RMP=SEr5k-_l&z96uR&miIeu&lj>$XDla2*^JejOTf=vh-sz@x&RQUU+Af1qd1vC zMzvSaZ)L>XY}}=>C+i(~ULC5xOspxXB!j{%Um7Y$^mKZXS99Ez7EUX_i+6cfvCDOoqw7|` zZz^6d^t-UR;9pP^_D)|F(`k@1Q}=M{TiaDDtL)G(kP=GBZuv$?6pbkUXJRfOuhE`<;Jp3%Xcy#Ge z+IR`vK3Z-V{eUZ`n9k1D;eGN|m0V~fbIz8w$~B`%vRIobkEGhO1DYj>Uql8YjH|6< z$0eWfo{IV5XsMtGgIo-FEh8#nlSt5-?C5@eemi$_+Vk4&xYQ^qojaK=u4Xg!jjhA$ zIGNJsIiGVbDuIMOliT*IM!)RHI5D^N;N_3|JaSKCkksXW%iQ3&Ufvb}lnf?| zN&5*t9h&PXu^YE*zlgk)`L*W9v>AaVc`<_9jO+3FTnLVYJO1A>FVg_i@I`>)(c{Ws zg~e)lA{1ZMXe}xHSly|`hb~&(6}DgSCjG?hKGyA(3)djjyOB^jLm)Jn%Y6{Imc7IC zOA&6{322-C1dfW*RTiG9sc}ON|R&YxrlX~ka`Zv$Ntls zOV=Y1vTXt8?n;#j!F zVL6HBWLZGq)9;WV3ZVe`cuHY*;pWps;OcDXb-Z43LzUg)2)%fI45Y4{ND1@uzd1q; z*k}pP8_ZbhvTZq9?*Je@8_d~!E}AMxmKt71dz{O4mQhf-R_xdu3afg+XFS-bfyioUGj)oF8 zr07;1KVnqRPK*JuuHn1Wu16Edr>Vh$>WV`r)*4B#5n7je&fW1${7;pQoV|4D=?L-C zotB|jqOMOe8%;%D9tM94^!CZbE4t>(3 zYh;$dl;n@)|22Ej<*N)Tenq6vR;H5li>f|fcwSotOpzAb8i(Reb}3XG^k%S<51gEDmDD`-$^Gz7HDN zY%(YT;ozLSnB2o~BUOLCUmXtK8tl4CyN$xY&2kTG=B#Dn;CN z*QADGiD`TJ7!rvIZXa?;osbLz&hKUj`k~vu{=F4U{pTL~NHok8Ff1P`f zqWNs&{xgBaYA?UQZ%fg18Vsgd^x;VS?7s`mS>ZQ+G%LXXQ-L) zaB1Z3Y<;MISiW8l*VcQE={5;VbZvCFxbVK&lGCd-5v|be%KFEQ(@Oiws-w9U8VwJd ztj`8u!wzhIo+BY8Ea58Wis&~xt(O9G1G5E*53vPxEFpZ24%H2g)33%fsR%SC6-2Z* zDATADbc$)}mgAq(SiaAXt&;FNvj!qxrFB0)e0(!;kG@l>`xkJieFwK10a4!Q?)3Tq z#SwLmFu&Fh(SL(KBjCKNfo*0*V0>p`KKMXfa#1`=av)$C7MoluOVYNPD}2?8LGSju zdFwE29+Zdjwih7IBaMnP-Nlg1UXZuPL_vsrg?5sx?o%ojcqPjiPd&e<(@tb-U$Q|U zF_{vLQlUiWIRY3@&N0gpWUxlEhvT(H81Y2d=W|1gL4dDjMeOOQ9~f3u7Woz+dZv2M z1|+r4RhB^pds=MpF{cu*<2j69@(3S|xcG;wI6*iB(LlnYJ0T4Vx(@{Rzx%Uz;7ltN zIXvuxzy;7wgB8k6Ja@1U_R^a)QXSPXR2xLmfgUO>xZu+&{DO|A0Q*1%z{56JE0L1X zY2{YJ1laCGRR@GWi;$<{8>a~3DuLUi#Si6Q^$o2STnn=E^`1)eBd9(+4Wg|4tfT8K z=USMeT@cn??1=;J6L8LHV@2&cebJw?`{Cir)oVgYI2sx-TXmp4ZY`rq)|F2?hb}x3 zd?DB3r!<{QKt>Sd{`#OJE(Kp!y+UqRH^zYq4riUb6(bUl$WE=0&SJmn$<-QCjVH7b z^yt*ZN2ml4H6U&Pw2EA;cl1H(2bjrrG(%xg#+ZRT0APRuI*Bh5j|T94U57WU2?TtV4;$Ok|hIJ7tB=L@kAE{nf6WPJZ}Gt2jHs^p853< z)&J&1&VF-1fycoaIruP-03ZZ;^pNH~yK8hlv_MhzQQBZ&@B+s~m zi5X|ky+NFE1{{3&{>0iUP<-wSM004fx4aWQ8zCvsFjGb0n)m(FPv}8@KKqX9&8ErW z^fU?v2nA}ncHmiWHW?}rNJNy2*ZQU~t;(dN9onxI!{^d<{WX?IND9%F2wyq=h1C|z zIj&X5jWTk0LogE;_5s$B6~`uwB-_C<9As}q>Wba?Ko6Q80>H`I86!!eQ3;Z5kImT= zsnjVH*%Df(xhLKadzM@dJ5`L9^>e1(R&7v*y*J>rJ$Nw81R76@`p4l!>VuV-=Qo>M zW7%r)6oMo_3BZ^b`fRSQTiwp;MyBIo$OxMf&o zfRw5Ia#aZ$9QNvK$PMQ7t6YruY~3;d=(mT*fpM4(SEvW72aGvZDFw02CvZNUaW!zl z!AU!9tiZLFzq`^Wx7*Y&N@CL(XD}+jEf5dq$o(@PqviSX@aV?lb-1%0b|U!ug#@r@ z#Q_$rmW|*PpgwVA3D?pyDvQB|LLpDAi&i03CeVAcht$+0;LrPKbDHIMfyrMNaC!^_ z98TLWYZz}UqORU|5Sj*P{QdcbK|x%RIR>wT@KDcG0eYmr01w8g;kKEac7HX5{UHuX zys!bFDzLI}(hLkzlU{;p8g*LR5igT^r*=rv86fJ!im(}$4iA&3d2o_U6afhdQcUEZ zxTy_w7t(iJeHfLhwcr1gn+qKEEoU-sc7;PEh&@p}$Kwg#3SCl};rgnq8cXg`yo=Avz|xq8R|Y$lgPU zEWS@hg2vOlkLPZC9dJ-%RKZN=oqyR+nTAfw(8?d=)BWb=16oMFz~08`_vV}B!fYHX zll~TlsDALS5Bl*K^5WE5V@|bN<^q93gEg^nup7rJt|sif#_aT1-k^c-x}$~!XH7c> zhVa=sWDBRO3B!K4vpF8>&w98$t;NM(9`S#KGvDcThx)eO$cj7ISPRu$ADb$=1V>Ig zS@&KxdI6Iz>b(1wgMgj*($d3G=njsX=rj9Q69gY!uu!~6x8XdnjqQS=>LOW=?! z+Qs|5Lg1{|8^0T!NNc^yESQxZ1i3Op0l3&`V|0>!7JaYpK;`tmONKnWCnw1V-RatB z1(G4|k?r}04ycEo}!Zd>Mbi4-Jcdu~LaKu`O znHLN!xRlHeZi zp({*Y&o`T{XBHxeKbiFyt5@z#ZHyf+LKNkSixO>Hm_^DU0m)dN;a+V2auE532fiQ{+OL0W8*h zY_bt|b7_kg9K0O09kQ9!efw6&RuaN?cMRsX z`#x3)D%VJYVg|DF`kPwhN+pKoU~XPM(paI-w#FO12K%1@m|sZQETm$N^7%q}m_;&A z`o&F3I%!Hfv4G6daoeOKYD?M9I9=?sNm{ufVa z0dw-YE)?-MHJ(4H_dV;=O^TZGF-Mxpk=dSt7_)sW3jZ?*7o9wB zy-Z97Lay=#JVKD-E!f|qZ`1Cg=^$waaq4M|Vjl_P)W_OdIS?G&!Uq;_HrpzgqM&nD znV(e%IqO8@69t5z)Au$wd$mfKeZoMdA5k*)ZaNWG#<5CQuBX?29+a<;Beq>GR;2gU zW(?Q(#h&SPG-&^Y3jDj5H+HXk=wvQj+hFL}`SIH^4YtTVWP3aL6Ax#k{?U-rPcBPIgLxH3ebqP(pbWi&zTc6f+N# z7Da~y^#bQWEMOlWciEaw@^jks87j_WBW)SxHw`R0jzgOE$LyN74$N$sSRQS)Va34W zoNpvTmJ1dK^x%n3JNG)iDUs*cr|b0EaV75KHZj-v8PC5Qyl%y-zbGDF1pYp_9)}g_ zA?04yX&J+J;$!RO@vjCoZuyCP~uYqqS=ES}}svmnDDZ#!A&O829j7W=Kc+*MZdXFbQ? z`UKk@KJZTy=YEGmz10lfTeJ_9#$_%I-{oKz&o_tC^|@_ENy?VR7TY}!m~+C% zhU{?r)f%Zyghp@_YhrvGc1PwFv=$5zrQI#ms!Y2~epx=L+Gr5nhqj?NehVPm8mGb# z_hL$|Hf&JMkSNQ@lQ2(*ye8sf3$PP+E0>%c>{{DG;|DRgFKKkxHm#bcOdL zabPlT8@!bdy^MG&KV-N(I zeC-16(4bk3P1)#A>Y!lBYPlc$D~X>o8NYxsN8d8Dq(~2gcH#}KU&Wq1MMB@q3}%#b zDwCpzFC(lnuVws`;KtKibx7cg#%B%Zke!o!-L^H%VQ$q<3}uz!m~aEoB0uA}zdqkq z)-?gV68CVpe5v5)>&2#om*)ouo1wpSpDSe%7bQA3fz@D@yggjW}LfRyN!@L4!x_lr5^65sXtfhM$hXE8#AN&{D zoh3vMfVyy`e!r9b32-|u?_9)^&GCnq9+h3Zp2b!`Ccto5D!4hmL3ZK>_Zly#`a*qy0jA8XBd| zw5?s>5Bl-NopBPFRIP-Us&OSiyecL<7IZy!Dq7S?X9B6_=d#>3*VSDww{e{cR7FYV z$Kk7YdP*S5rL>qnG%R5l45<-%9tr+jN(%o-@i70f8)6A#eoAyAbVvEMnKI2XAjRI@SuStbMR1hk zKrfDOn&%vV^zD}c>mN5yD}J>aNPs^=8RJW`9ihBY%UA3Pk~^_?+5ydY(JUS(^fet@n;Y5b>lE` zS10NmS5aDd{9BaJ0S0+_n^AoZ76u!5@VBbd_#Th>8zZ2^LIk&5%|j6y?r{~MSBo&3 z@Hko!;U=Z*Kh`#s(X_h&b->U-HCM@`j;t`R?7zY{uy=7~XeA5+89i=_9ge&2wEww? zY^QGxzaTe=aqYdH?;6aTBP@`z+n4RaSvc-)q6t{{cXdDwR9;>OcidA0gjf?;g)y_gEAuW*S!xzYI(}jBgI3 z=6E1N4z2Td)RC?UjvZyyNKVYFl|MWHkFK-|K12R<_YKMKy%|Ev1u=j|TvCUfAJ&Oh z@Nk2MF&%i?fBO+Z@<58eApSqQH;DpXGoPR(hgeO(+!?7~BVo6I4Tm#-?PK|dC<0uJU{f!?L zg(6B8Pu%cb=zeZV#e(Cm^O}}iMUry;^`Mt!#ANlG{;vTfJm&B49fF>Pv;Fsy8QTv4 z-#cz2gi3n;XXqOGfe`NAXEzHCs%ffD+!S!)%GN`^i}dU|x0;mpB}V^kl*6!NsK6TF z@@1~-O5S=)p8klC@|T#9QrqZ&y}Xi>$-u>h?dK!m;;xLp8^}oIH~nOhtF`i(Ih+n@ zKNaX*F;;Ax+CxO|@oKYHPm$k_fjdJi3BJG}i~`Yn8!2nujvy?MO9r}=nc0=3ot0QM zdGrB=^!q(QAQX!R-)Js!VfD$$8Qjr)teK6uXV4ZE`zFxCMn zeVtQs$F`YZ`aP)~wF>`b-D4)V4fJMOLip%SXY7Wi&}Lf*azr)$HREPhD~eC{H07c6 zMpTesvGu&|u`g1mVU_7L6nhF63TKG8I@k34QhuyYI{0M$%JFet{*|asGm6uE{Yu&& z@P8(H&2M^33r6O~?pF7c`N*ybdCz?iB5Jg7u;_u$_f6TE?X-8Vj#?u@{ju)5OTIAq z?{V1$B+3wes2ATDH8{tyerS}?W!0zdkHkuu%vohGXs|>%;jU81FnAyUvMg~@C!LR~ zagvkXMrt1yE6B^SWxc+n0<9Cn7w^Zc0lLDw6D!%Y(>)@Vo{~ zBEQati0>a3>&XsJrVarb*J@@quT@$jnw{REpK? zsNxY~HOAIX2+~!ENc)i7!+hr`k5k#bjonpM)^Pi&%%c@98Pew8YC2>BOKUIwY?cwO zj4bUiu1Ei>F!>3swQSmq2>H0fa2xeSP~py*rw@o)@i&-MyTvEWiy9)goR~>D|9(@9Z7ez_NSy_iO?FRE=6(G%c zh1l#2yT4<^pAkXjjkDSNm%n$d^|Z5duRNNoOz@L8Q%EQvg=~N%#9+N*IEC8+qJ_SmDETZpqm`#a2II&LcYlh+|7=WtV`I{X& za3!5`I<2ZzL}Ry43$+AWMvKH(4iokY&C=QKGZRQ+qJt3zz$9BHbcnJ+Zc*H;(l09I zGR=hz^xt<}E-Yy_ZTJ|s(+v?#JDrx4sQ9Tw;P97qmCd)NQ#zeppSeWw>+D5$;KS%& z#4pyv5bBG$(L9eFdo^d&QvsmQ&dNeU!MB-pSv%W&yDEMAx^>gu( zB&OEJ2e%)uphx6;pghZJ>?>OqU#8lH7@4@MQ9R}CAIjh-+*YL9;|;=cZ0D6ug~v=$ z+vcaUe(aUt+u@VZ5Ekt!s-I|YwR%3-NiGqI`Ze0<$1*Jeg>EFm3za%*rV@Y+?`!I^ zrsAL`F`eagY;u!=?#O~@`W9uP>8L6bi#0=f8du7ge9pFK@#LtGri-=cv*V9;aO1{? z$;y!n{+(k>Pfp`=N%iTagrPl9JUDNnFFaj(GTN?bV?oTs-}WsvPt)Y$c8tT)gGka<1?w@f1C$17|>fH{DQ!)HribOTfr_l zc2uw9qxI9EGv~l_(v&Y)|KzwdW5<2-5<2(I@hp~ttFzhB@n}$Veke({T@0nw!*4(# zx-NK&G;|KTk%jWcm2NieA1MvJmEp!zK~9i-n#fg)ADdykn&okD}SbA2JlQAvP+iRNy3ycE&U zd>V9Fe!$i}L>M0nWG?u)MUT`fJ&GFawkVQuT)&nk)0r?A>{JtKsA&ww)eBG4TAm_x!< zXyoFw&3i>S_A0ex7>LCzT7f0xX1N;QQewy(z&Fqq^l1k7wW7|n>(pPqPQ5;jwLcbqoiO0xndlDCtasbN0RV;B)C$PYoug(KYjS-3OItqrJ zid3775kuWhtTm^)1b`Y;NwcXM(H8@Lf}>w8Y6L)|aNxtujIwPS zvu-lbEXs0>j74#__?cp7GK=M{qHf|5V!hJf1u>n@ns#!LAr_NxbrsLrineKA=MTS* zVA07d^{or2?*avn{Kk))*Sx)~k{D<_pG<;pHT`^M z_%;@dm?vu(eIK{eQk%3CSJ3R~lnPiiY1MEiCcQ*~dIzddN@fcp!mP-o6G}Mv@Aet| zaPY3Ls2xw5ed$);AN8Ca1xHAe772kyV{NGdR1A=M23lZ$^t$5xS1yh4#Ip4O#Y_S@ zm@Q>9!XSXK9r>Oe-RpUa=XYB_k$I?9v5b?myB~ly=5>MFJr?}6JMW6B@vYDijg7%R z85P5h=xFb!n}WJ`;eS%76l(1G{cYZ&<2R7J6zwkRRFUtSVar|v&RRnJuJRrgpdt`9 zPVm=Gs`p%H3i>t_~qL(O;?A+PjDij z_TXgr5QvD6*6lLRS0Z>GyEyL^i~}0@rOko1_@otk;1hX_dk}+ z?b=IG+Mi_-It*)|hT=H%ozlPGf3qTXZWvRmXh!wU6zZs-2F6t$KGbb8C$afTU&brCCh=4V^GnGx5pP3^-5}hFa@?jI@)9at80B{*|T5YlN8-6(+a_^16 zmUec(q1fc=SpVh%~dzt44SMl zNeL|P!ohXrb%OK$MG2Slyg+7;S$EGoGBOrfqQ1M0{pY1I#J&R698REOZ=;5IhJe*5 zU)+ex+Jv19@BkU`I1~hR*)ep6P%Fl8(b4J`SZP{)z}*jMY~V5@s^@%GU)r`KWSU;= zVthTJ;oX5w^R2115f)qiuD)&hdehUG-YnjdmQ16lSSR@tZX0y-4-~gl3SyIctCkH= zHv^fX!v-CjOEOa_j13N+Kd31taIjeqrKL&1qwU`}wyY!WJ%IJ+yr+8n6*JGRY0Z1X zHGgWjG41>bOD7w}+&U(>l$cN|31;A+~)5U-UGcy$;xqSC4z0E9v=n*-Ou$xI$u z=RwD?*BO^3HFLjGOv{NC*2?A*?0DSPNXgzrbDQGw9g2>ijK!Y_NRRQlq2vLPe#FJE zrq7;oai7;FH}u2&EB9aqn5bPVu41tsSv~lBch6Q#)uy{yfodT2m`!QI1@xqk$?y8bj zgj?PI3Fr?6;t%h~dXtjlGK*BOoX|z9us4HEj@+M3oRtLw*-@xb2v;p9V~1SPqzD|M za3ZL{&~UCJjXg^0i1W0X9H#(}*2k)bXWd~1I2BX9dm)f*kYVbC*Q-Te^=u=ej8UUw zT;l?HCFPO#)Cm#2i-ahA<>f6?-Xd%u9KA9Oa6#JR&E-yO1}|X8o>I{oW3}3_6NVhl zJ|H_y{!<rEkL71%+cp%IB+3kAgO`+$Yk{{NG1eiZy$8-4gxcJ5x zMc2d;LK0*7Nvv94-=A|7-$nn`%vXX{(0!L#T$!Z0uK4X#lijtl!v@6?FP=7`8RECcD$k`vrZq{SHCR?L_#wXjw&%6ZbS z&h_T|cVQV)4ht>FBza|E$Ks>1i_VN`JOqc7NYN}B6Nu{ZvkW#G(kUbT=BN1n^zq-# zNUJ<}DhS;7(i~>r^cZp*L}o%U_;1acGqfH1s)9u+cJ%6XA42`N?>%~n!I|tmTo^Kl3K^AjzJ(9s5H^WtMdz@5&3uDIl0IZYh;6|$HsqrV>xiLiuq4Z zWL_D=n~D4=lSDzcSlnhhWfFduDhHf7GmeBYTp|gr8M&w>hVVXhPigb=mm&vB2GF;m z;06fJ(of`L9u8!Fm!+!zNilWA5Tri;SGSqp0NScfk_r}DiyXAT$q<{9zx%u{#Ak{@ zQ&jY2&8z&8>bOoA^5gX5?)Llm_~_3>-;dl9c?Uzz0U?G%fFXj=eu^6dxDKiA%&1$3 zPy_-<2ZZOKyXwqoVxU6^2UrXabJp7dl=kco4e5VANv_ASh`BPg$_2oXYpWA7-R#^u zUIg}s!N~S2Q_ayNeCy6c>rd!3qRHp9!t<}m@$W5n&tkW|nju7tgOJE&>O>T8SyXds zgJ{U?F8@z5l?q%1PeXF!(y~-055}(}#6pls4;$Egzy?qe6@Sc#3V|e2WqPb_=(HcZN=>PRKHFTS)btFn zPgE^KlHf}YyMZvkZRh%bd~%K}%EMUboYTUy=%-+;K~^gt4jeqYJo03-Zw@m?wA92(uE<1u)Yoq9 z;4S-f!43%U4>sSNvYYoa4EddPY*ZpolbwV4#Vi*@cDqyNG!jUvhy-!y}B?z6= zTU-DD@+LnB>9{TeCLi4VZ8`f3U3BHU8wwxS-};b%y)RAupcIF{6cQ}#1?_+uwNz&~ zRz4+~3%ia6LCl!*ni*v5gfwusMv!GaP8g8G7xB>)co!v9fq)?ZPzf!ZdAZs~3W zfuXyR?oxUHk&^E2QcAj{5tNqhAq45}?(Xhy^PY9iTHhbQ@`oAsJo|p``?|{Fy~svr zOaEHd^&ga(fCD~q=9!P|x&7@Eo2bn8U_0#!_FNDcj7A;}uSZPlXmUBC*&8oPsA#{_ z>xrgh1jeH0Ghb1Wc^(7wYV4>_=)7d(KPl26(xAy*lnqS<=Wdu}x5D(o8mhWzA4#T| z*%ciS`6T8?VJiu#Z4+{`L38RXVfjZ8YyBsDJmzN`s3c({a-_U1ZEqBXtlF9z-yCUb zMb8^W+yrrdSyc0l#2GB7_dpC|Ln8AP6S}vyc5HsouNSaeF&x?~N6tYS@Ql+qx|AD_D_ zrJA&xZWe*sOFZBW{r7OT0(b*k=pf_BipfnRW@0_m%?vXQ_6&;2N(pD_k??6i7O`0%`ik|yFis=0AZ{zdyP(>Ju1P@Ln` z@@>-SqdxO+adGma@zkAyAxyU4j+K0tXk%ijmWH6)K9jS>@RV3(zCYR3$o{>!IHX~a zHj)%&BZh8xqy0{eReB10UT#7b--2O zaWrkOrJFJl;|4rtR9<=p~H1^Py#8M#9;S%r4$m zCQmrlr$5ETVNaW&3|O$So;CptNC0tgB#RYjV^3WDIA3}*rSca**~_TdM;9Qp0{aea z+TO8z8AU+wt4%vrW2Fg%n~@|$LG%jAalr3~VjbC>*66?QT;ZDlUb9WTN%16i zOnfPLL4M5iJ-lsKXJ<`2unVwx`E;Mo>zZDw+blC(Va(neLvf+$as-<_2mgRooPD&v zRZ^%@E6D7c@=im8gwOU}bO!hIl26->30U7L(q z%&(FU70{l(pl=|A0jzs`qI7O+nztGSYM`+6^rc@@D~--Y)+i43T`(Co&(CveV+>|t zQTrTWzh4%VR`yl!>LmdHJRBpAaX@=QZg5zC%G);dg0H;wBEY4jI&N2>O)3T;mWMfx z>!Et9U+a_vdnNu zpu`gM(twcsFdYe3N85`goet25d5$z*2}JZp*QF)`NE)b^GoRe2gIgp;Ox#1@7> z#QB>+)WBtP)s+%uIZt~sxuK-tYOReAMgAEw z3ISN8*vN}DOOy>(3g-AN*l_P0@Gi0Qn)?%(Nq%Oy-h(&7n|P&7Yt-h6&XY;jSS)_* zwy(S6C;8E6CzBW{AY#IOs!HxohG0yN@n<4zqRD)zeI^{ ztd2w2v49}7CbF7wnw%R4*~B{JOPERP^WH%}`i3uoYP3?MII?D_Sj?KW3S?6Wm12-X!EWaQ*zJK2w(hl4 zOfkr)(&aS;T8Ukm#){W^P+I_X9UMg|S1}>lA-CsrF*yG28-tnGR{jU>06a-d-_afx z@aV`l6r;o%o(blOPlPI;<=qy*=0x-tCcT&xD5EaIZC8PM~(+d1iu-h|7qz5?jp2f>u z#%-$A>+-89x>w^azKn-V?!I*RXh>_Al@t+abIi9fueSwaG4Z?bn-QS_M#B05E0{Ax zft)(vp+Te*0zWJ__Rol6)uK~FWvG1dP^c75@tfpPOI&b6WH3uGBrs4I4*Z3PFtQMN zN3mO6>;9y`Pd3Z*fLNI%lp)|Sp3cn+sDW!iD#)NVc7ni^hgB#$?6Kaaq`` z(=1+98m{9pMCwzcOZ#8MyzQi#HGrx`acK1T?&gs9gn1CF$#l#1m)cvdO;}C{#-xeM zJ~ds{9@clom^APLh2x$gdjz=S1jdeo>3=fejat97=oMB5Q#>a?gfi$S4L!HmX8&qS zruW_F_f1qJ2B`f9KwxJ!4A@R_I7fD-^>JyQK7w;G`y#KO@$Ih# z)f*n0-ji?+v%%dlAP5kPZNjInLj)NZA`6BlIftU+e*%tG>g(Ly9Z3?EQ0TyOt*Vh` z&v1;_?uyh^sER4wxSH7Ad_ub7>Q*aVJ1>uBKH2qcaGWa#zLIg^pFC+15Az z_+*o%eDU5v>kOFkYGVh2#`Wu0mWMc&*=pW`C;KE1DVBR+{*I^{9`Ovmu__-e*Oa_@VJX+ChD&44OP*zv`>3 zax!0`(zImvC7fknQAv@FOI7dF_q0!`*9eYs%B~mZz1~5ql#&wT~ zLRKx905ATMANQ@htG|M&~!*UjLu8i+J`0ZRqXcm+j!>u)%otV-at6PEb*Ht<7e zEfOxHLTR=~ex*)L2OG5IT^JTIn=B^z_oM-x{#Zr-Nq!<>qEVL~8nsxQD9HU^ErG|B2GPVv&4Z)Tq zb=O*y8sHcv_!ePFciGR zebs7J*a`=aJYnwB^Yc>;u0 zlVuO9j<~_5>>n+S%ITv}JKPF5DA1mJTOBR08-v2$>7d7J|90xP)`TFK8jf*IBURYb zX|B>F*vpz|=<*1kD8e1aIrKa1H2dI1FqV&9T9kx%Znn--0c_0dk1p|CN^uBGY6U~T zlZ`~ih}e_CNdor4rZFQ=)8>MGYgyJOqkHA90D|sye1~kgc9?x{V93EoLi9&p&l@@& z?#l3tDDF)pwpU`Zsn2JhB)`Gf*T2Q0oiC2d6vB=W#wYN z(WP}N34VO!TFE?}J^(7aMjP5yc1MC3m46+jBJ=1kMOX2O0%R~q5TodV5kV8+V)qFB zE})QW)e-?Pob3uOr)S{Gi=g7xUkqH+d2N^nlJ6G7)~PFnkhTJ?h2WIP3)f}PH=1Rc8Z3qio`=Kl;Y?$#J#&1x`bDJE{32U zgWvpc>Ev=a$Z{^au*4>vxUnx=t*4lW9+9i_F{tkwu`UejKBdvg$4l2DEYqUI5#iJX zT8TYPPRujjq~RTz41;Y#5M%IVFevdY(H?iiUete%RC5Fdb7o2WJ;%Qi8Y6NvH^-C$ zVW?xkcaoA2(CBowppcovHk@w_70rLr7puYR&J>4-jkEoupJErdGk9AnTe4lI_0Z`I zgH4o5>in~!m%>}gObUh&8U6V#PCju3w-*N}_C}asbfxiQbcw-^=i}}Ky38o1+;^(= zcGLy~19v;_C;)r3(XIp|PT1UtJf|OHsLr+^4L`8NLz^w=YnWXx*xP1N4C<^~=6_oG)%3h3mbS!3Q5XyE?7zKW-TW?lWbgsTb#G7@^}0*Q@$4%oqNqYTGtw}fR^@&1Dy}2v z_1Me4OUsjJ-0d-q+AP#`HaqS9MW8duimk!kI}Gg6Eaz<}>7Qt|Wy z*Hb0FQy=$ty{oie5f#bExLuNdtDYAkW4QP-;OB1z0C9oX^i}fL`@fPuDq-->nKawx z7qXtOy$HN8M2P2&kYTthWFp>Asq?fjV7g&I1V>qb{`L9oMq6R}PyY)zad7UJ_1j3j zpF1xULh#sEf;GbDr?lfOy3U2cT+ev^nWJ2%>a;Sy7V^hJ&z2jiHZ~&}v*I|+dWugW zzH@9Ix1(#;I{~e5uKzAAf$5Yuc@Ma$vJZ-lLk+)%l-|c1`TeJ-{VWlcjeM#(x=8EI z89@hCf@scCkMzGzd|Yb0zmq)eP z4d20lO>9P}^(qN&8L|EOz63z2D_$r-|H;OZv%-TgN2APY52lk_?#>keJj|#{cL>?v(B`XfH%9fEeJYSK+}Gku z=}jYM7MHn9aN{?xfy(2V18jgsFj;jnD8C2!5ATWs071Zz%{k!f zbJ53!ysFXrfN763T=cfrOH8T>J^d}AJ%}g?1$`BOzXK5>Q46&il(?oq@C#0_P0BGi-H zEtNw-%nxh-!Uxw=8b(bpt}lzksFJDnA-T=>L7~d@?_@tgSAyWx{=dq#u21_lzpOof zdoCD?JSItKFlRqJWIl5kjQfs>*zcb^U?$W$E@$YSx(dc<>1 z#X(Ujye=LB$__dUD?^=VLLkU1Jz4SH3S^{I6I}NtQJp`u%U7qfQjE(R`Q~3f(*Xzp zQAm}&+cvf&99ZrHCGdiSBEBXDe&Y4zSo=nT6yz}VN=4WMuOaaD%NlzC%(Hz#?Y~-r zqUOSk=XMUjp##!$c>~mgE`f09wyjq5n238T#%f~4`7C?lKza_>%JXJJv?HR{Z@rdr zbB`}eCq02cZl6VLn+3__T)LJn4AJoolSTPfZ_13#;^#=A4%hPQ#)2JVk*oWqiWTkj zh%*yshJ8kJyqIUMz(DenM(r%2y@AkG*AhCbN%r=~yr_Q~5-BX2!}Zm!gQMunFwU%( zP1+kL+m{4ATQusMuiCox0 zO$4ygpXz{nl=kgR>0u7X_auoo9yETNqF0ov8q=(|6x= zP5PROA7-kGFMAkzD#NJV+(%!pqFFl_vwic9BdR|SY1qQK!Smr_XCjz{3S*V&*C!|b z(ojfqGQWiF?zno52TuQCiC*Q%TzAc6dB8Go@5T_wY`cZ3m%qKS!OW8~<=H%r)8F8E zZMO%*EghbBjm<<@;A$eoNTze94; z!WQ{+oY!;?K&Nd)`uk4Q{2hHUoRtwek+S{N;2A_NuDKMxA^PPFuG3)$!-3MOBxEo? zzZV0(N=G@ts%?1Qu=6}#<`tcS+dJ=lJ3(?^&@2l@VUr`y5OmP;a1ms9$lsav((0TL^OIhwa`I2e$jS^zpMy}qM|b+S6{co!u@x!*mA@w;p= zf?DE56l5Q#U8Wc&slrCIF!Eo@b!Mbghp=vS=}`5)AmhJy@hs0BV)AbuB+|#5_g#aO z3Sl5(ZapS-8k9%r7NqS=V908yIM4V*wWp0}*U1{gJ^Z@3>!A~e%Sg8H`ebWXgREBnF^6!UT)7eYz*P3IvRdqVRUnfLspp( zAY24v)RMK>$m-ID-PQ0zFoW5pNu}8-cK2S7RlVBPrZ*qC;N;fe6f8HFn%$9lHj!bK zVe~+|HM|BlClkgq31&YottJM3xG(?GBjF9NVVs6@e49EGVM1Gj@wVa>GU0mnXm{vN z;vaYgCt2qF#dOe`$nUQCd@UeNmh^CnKW&X2n7j;+>! zv4!x5&|SghM3KtCSibC)AW@v#1Ll4SEH6J+29I-cmZKrtBLHY3=CMu$tZBN>j~A*h zOZ*MVul(n0hIgkW^2Sq!rj$1$0IV7I{21wY+5vi~Ffpl=KSnxdvVD-LTA|#492z(} z8nYL-8&Nq(r_i*s=DnFjSHT2tvV-Ovnp^lTTO;l0wUpk74~JwD9=QM^iF7g>usTEr zN=25Ij8SoFkxo6qpl_FQa{#*@8=EMRtsalTi(1InNdcRjLo;6~@O0QH5EfL?NM~KB zz5Nj=CmCY((1;;*!uw7nd%yP5o!r(pLnHW!a?`)fB(xrX zR_uc-0M@VFV5ZEX`nwbk-nGJl4duA&^6txQPHxy+kmZ4Eef5fn?VKm6+KBkUt^3Vl zkt&Rgl{k6+5E$m!E;pR~W@6-acylUxw7P;@FJSe#rVKvc@Sv%!#kSb&>ay)%IY_~C zhs=TF^5nUd2u3z#shZ<9WP6nZ_WtG?e%@=|3%0bnzBOqK%NFinZF=Dz)WVGri>FUi zO%kS=W!&i+0#s}sD%dDLKZ7*^s5jG6lUo9-e!U!EXV^Ub-sQ9kg}t*IcgJ1sqdXgs z*tX{yNbJ)r&@R!@$W=fD>hs^e7_^TWdr^>ZTT>PGC&xb_-6#%Bvlcp5_dLh51Rn}i zKUA!*>IFSXq(fn713jV0kmXNAA@Wu5zCu?Ezyi`xkeF)aY1{CQ_fX$_xA@1E&pb!Q z?r4T097qBrU4y*IM77BA`{uP>O3XJHx5V$tP7fX>Y{n*g$ zeaMAYn7~-M12v^T+MSmX%W~VYAw@bY`k{mL9p#UW?%A5k2tpPiupyL^5>l~cWhkUn z^N2v4S;)76<8_}DYkX!!^f-ScU}bRVOHE(nkOEIrjcj!TEN0SiWL#INl85TsE%n`(V_exO2^LE;?Qq9(nvu zpk>_bn>fIhTVVa!Vt~ij!ELw-IjVPfpIx>Krc0uA#o@h__Co%;jCpJ_jm1JHGt=VJ z=u$sAjvY4DZnh!VCE+iQwpd?&Y1AjJpB&VmxT50Qb|0RiP7VM-HuMbLg|6?rbWfAN z!2G5f^~OQJv5k!E7fdGAM4s<4-)rCN7-DgA8*ASybZK@)OiU>;(v6vZsSyVIW;n-Z z%4>N@<#hfGa?0;Q0L7^8#_r5mmsI#q*Wb_X|AvK5!+ZVv+Sh^|#8e?%`ezd;3V#>q z`yx-yO;!L3|LFwZzvqZF5|khIklkP`=INK1LZ+cH6YDy;Gshe5cmA+O6twcD2HW~^ zVBC2BOfi@qqbBpZR&ZwgVjsZ&1i2q96W$cY0XhB+#c`rEx$p+B|MEjh@b%3bYeGs? z^`nhB6X0?{#0{1snn=!Q(ELrYLDe5_t3~Nr#MfH^*n9F!LATuNHB)3|U+)|3XiVrV zk*iU(H_JbJ1ktcoWZwW~Qof{l9-D(|o>kEc^HNAO%Ej70*@VEx=LSOOSHyJ2URegO z6*E4IP9pFOmi7LEolt3hL7)rv4=mMdQvgcBC+*&@0XN-=R+&i_?FyGjEqe%}=#h{m2~tLNXyvq* zAG|!PyUQzin&A0$h3IeC4{Z3)NWg+E%W#6%x+`S4-<(*TgAis)%VG|)HFNAgsECv| zstUYHrJe38@8yK1Mj^~iju#V!D>!oSy7lXeVJ zuF1?hUq|0sBV3c{PCD29$@A=&huEfb*P}`8kuk@cuFh=LMba}~Doj{iblBm$DtoHH zgkL>X+R#;;6v^S!hwFWo?}oI#{){oPV`0j8zL+Fnrmu?B$Xp#8upsF~9Tj@!thMb@@#h%=%TqjS3j+XUMpLwQ`NW~+u*N1FXaSC%_oo>pGu^FksEyz7sx z`6M$iYGMV9P`#{wLrXAkX`nyiTIIX!Tx;y5@+wnRax-JfzqC1Ik- zM=;{u#~6J%C=R;MP|QrFI($xP zdW~Ai8kvC8zw^HXPqpi-UJ`(sP0vM%=FJ9g$S(~FRH-SfP%t5oirwT{QT`t+pcSrb z=!96UIp)Q|JJV)E2pmnq9JQz0HH?Z2fP7fb+Ix6mmf6cd#a|IP6Am6wq}-1vLWo zQ0T{L_GViyFSiOW40qz9P}X$T%j zv-}>35MiLmHI250M8sts#XeLGAFm3~!3ML1V^_Z<4Ppf_T)+i3CQ!mh9OAz9C%cnM z5LnBK95thOW9)xbJ0jD4)a=QS?DO?Y64*cL9gb6kap2GHc-5``G({c+C|;}>b@k3* zaxcY=Cqz1>EdE*kQ$!gWcBUfSi5MJyd{T`^)w=biLi%wkP?Rw&Qy7kl)*+_)3jt|OL_1C5+0wgNcQ<3>kiXdGGibCb z(#240M`0jXs+~MqmP!Qh18dz=DCi$w;1OUs>lHK$H*~Dn!U$vm$MFw+cV==KQs9%a$t!dUyR*tJw=aEm6k$AI3qpnRC;OlVQUi9W z#53K-s{tQiIK+=bMtHbbrO87BEJDX!tMZ8lbWk>?$!oLk7#?N`#rrI!@XnyYSj8L# z(eM}^?o9k3+vBX7`X9W_ci+rQYC_aT#9n-;J|jNmz5nbiSBm&YCGYKAlT}HN8I?Z0 z&)4(U6~qGA!oIGbw`}3xoTdd&@x3v;YKywWF>i;$Cl&tXNMrgWs+uP98=bmnI)4!(o>l zd&!-4SE+5gRI(djy2B)##Yxv;gzq*J?%g|jP*vFv(f#z!66&Z$c(D7Iu)-#4oj6y*INHc;zjU*e+ zAkS#WXqsw4_}#CkmpL+|36bhS(qlkVPHf-Jn-9gBEmE^ekxP$whaKzlAO3tjGRel1$4xRL+$7+(S11L8!C z#E0+Kf%i4;*?}hk8H7i=s-m{Od6SM17suFJ+~ON~Y89b*hayM42yH{AjJ9VoofJ3ke ze5yhK2&c~$6!09O4I60^v;_B2xjN^JO$W7&^rmUC_#!hOkM(sO{Ha^%9<$J?C=hu; za;31MWtld+m35OmH_H9D!(z4f?k?UUOoPodiBO0UkJkG~EUJ4rL|2=Ynh z*`HOf$1(q`vhe|AHYD{T_+{_R1T#AMAi9&M0SWdCSjj0AnZ^6b&@-3IyxRL}sEzgHU@uUhB}>K)-^g2SAG$$1Mx0KpJX+ zd0T+VXPYEtT0GpYRgI2RHbRt&$JY|kA$X;c!AK&w5Q7{1<_r;8;;~)mrKk!P*{xt} zg>N(!9Xzlq3S1fv3IoI{4d)p4&#*3x0RHlUiLWWs2UuO-ogBSP#q}rN+?IrYvWkCy z!8*!!hL3BmO1HOPFmcjtLIuV#iH@|E4=514*k=l+RXTqlmh3ihSg}&{u=++Vk%